Compare commits
2305 Commits
3.5.4
...
wip/sessio
Author | SHA1 | Date | |
---|---|---|---|
36c11009f5 | |||
df6f6b7368 | |||
9786b2d096 | |||
ea02380c15 | |||
048d5dc914 | |||
aa6b63373e | |||
0b219bf8cb | |||
109b29aeb5 | |||
bc069b99ec | |||
16fa186b63 | |||
e70c0d3e2d | |||
8d47afb195 | |||
a01469fb08 | |||
929636ebd0 | |||
681ef1efec | |||
4f14f122bd | |||
67e9ed5d60 | |||
5c25497e16 | |||
626cbea9cf | |||
aa7ed319e9 | |||
580bd67278 | |||
9e44978aed | |||
64b5ec0b11 | |||
48f9ea3d9e | |||
798a0ca240 | |||
5ee6cbd4c8 | |||
3b219a6a9a | |||
536ff6f561 | |||
013b6aa44a | |||
0e7d3a7558 | |||
8bd4895538 | |||
465c77ddcf | |||
6ef2d4a4cc | |||
7ae7f046c2 | |||
f4051e810e | |||
e6c239d0f3 | |||
35a7a3c1ac | |||
a0991c8261 | |||
9520880568 | |||
719d793e22 | |||
c7fb65c78e | |||
dd74ea99a7 | |||
c6fe6eb7ab | |||
2cbee05c8a | |||
308b1d6039 | |||
4cd832c05a | |||
2af4925d95 | |||
5e52f0e2a8 | |||
a46a68d616 | |||
203d7c4b43 | |||
24703ffa57 | |||
5cd913a527 | |||
91844e48e9 | |||
6c2f3d1d17 | |||
9c222c7e5c | |||
2249da7976 | |||
a55288bda0 | |||
e645edbda7 | |||
aee7cd73c4 | |||
380a71dd21 | |||
23dd5cc160 | |||
847cb5b972 | |||
cc64091f9c | |||
b68eb44ca5 | |||
403540e8a1 | |||
c39497222f | |||
9e56e668e0 | |||
41ae93dba0 | |||
6c527c1bb4 | |||
90c7876341 | |||
10b77a8305 | |||
1902f4773b | |||
4b95be6a95 | |||
61323926e0 | |||
e30d18febe | |||
9f2f80ae4f | |||
bd6e0ceb81 | |||
673d7038d8 | |||
3a6231dcc1 | |||
9d54e46ce7 | |||
63e6d11892 | |||
0509bb9bb4 | |||
5c78908a5f | |||
5a2269c6c6 | |||
a7e9655e32 | |||
1ec82d2ddd | |||
98eaef621a | |||
74a6ca58ef | |||
747faa43ae | |||
32a53f7412 | |||
9c94e9813c | |||
19749bb37f | |||
5ab4c484a5 | |||
e602199bfb | |||
62e1c08dd6 | |||
37e2b60cd3 | |||
f299078585 | |||
bf0a0d5bad | |||
365cda386c | |||
5de5b7a66a | |||
d6de0a64ed | |||
26f8441f73 | |||
51bca08386 | |||
1579aad362 | |||
a2a580954a | |||
944c28f3b3 | |||
c13a597fe0 | |||
86e0ae0b93 | |||
f71ffed3c7 | |||
cae96d9023 | |||
676b649731 | |||
634ce34e00 | |||
6c3096f71f | |||
991ed2da72 | |||
907f02cd28 | |||
9ba78ce04a | |||
14a3d9f7fb | |||
f73a01295c | |||
ccfa3d3be1 | |||
727e4c0b37 | |||
6ce470b9b2 | |||
4b7e230531 | |||
aefe0d3ddd | |||
bbb23b515f | |||
976166a04a | |||
b20129c37e | |||
4fe1360b2c | |||
f0113c5ff5 | |||
a259016436 | |||
9a79c71e88 | |||
e62d22a50e | |||
402f2d939c | |||
374aee75d8 | |||
88ce65266e | |||
e0a6a623d2 | |||
d0310bd745 | |||
35c665156b | |||
3754a76f10 | |||
db2e6e5b95 | |||
d5f95db68d | |||
d96726c392 | |||
d1c54f55e6 | |||
176a73f4e9 | |||
54a9592e19 | |||
b2aa29e221 | |||
0eba0f8dd3 | |||
d8d0afff0b | |||
b799e8c0a6 | |||
583255e1a8 | |||
c68ccbf263 | |||
d658ec8de2 | |||
8727661c1c | |||
731e8bfe2b | |||
f88d9c06f5 | |||
248a0c1b6c | |||
434f1edb25 | |||
2a550e6466 | |||
4f4132943d | |||
62760d5b2d | |||
34aa501637 | |||
6330379f77 | |||
30c64baa7f | |||
cdbed0c615 | |||
f6e7034172 | |||
cd9c5b9c5d | |||
7833e21b01 | |||
12ba2b222f | |||
8583ca73e4 | |||
b588ae4e0e | |||
28abc15c00 | |||
4d663680d8 | |||
6505f8e94a | |||
ff3c20dda2 | |||
c698dee071 | |||
8891a41793 | |||
026f61f5aa | |||
af063dc2f2 | |||
3075f3cfe4 | |||
419f2dca15 | |||
5803f69605 | |||
bfd1cc99a0 | |||
a6a2cea414 | |||
10e857cebe | |||
ed76b54511 | |||
9659d056b7 | |||
b79b0952b4 | |||
fa44dc7d16 | |||
b0dc841e00 | |||
c72ae375c8 | |||
0fd6ae5330 | |||
c8792ccfa6 | |||
b689972e67 | |||
ae6d7bbfa3 | |||
2591bc90ac | |||
61fe000daa | |||
c864ebeabb | |||
24c530611f | |||
d44d00f0f4 | |||
dc3082e66d | |||
c61573a8b7 | |||
3b95560d32 | |||
990f68375e | |||
1290c98c9b | |||
08599afdd4 | |||
bb040918e3 | |||
6fac33ea7d | |||
e16c16b3ef | |||
1eeeead78f | |||
046a1a7af8 | |||
db89648f62 | |||
9c839cdc70 | |||
938b1ff06a | |||
ecd838bf01 | |||
855a31ff25 | |||
7464add904 | |||
7514607129 | |||
12a1d7b38d | |||
d6fe008b2c | |||
d920da6624 | |||
e2c86cef47 | |||
8e270dc246 | |||
22b6a25408 | |||
cde695d903 | |||
e98eb57e3e | |||
11d997c42b | |||
eab1ab0fac | |||
660b801775 | |||
eb80503bcc | |||
14ceb10555 | |||
d9a4688e98 | |||
092586c931 | |||
31478e9fb4 | |||
cc7630c236 | |||
c29810b2f6 | |||
c1240d3f2c | |||
654f1dd055 | |||
53c609c278 | |||
a2fcbb7e65 | |||
db14dc973a | |||
c29aaa047d | |||
49064ed56d | |||
72bc46d339 | |||
40b895d16b | |||
33cad9a824 | |||
e96d9e0ea1 | |||
ed63bda932 | |||
18107d567d | |||
608818fa9f | |||
9ae2440ec1 | |||
ef9c50e63a | |||
5d50b08351 | |||
123fc19c4e | |||
a9058e471c | |||
bd1f48d9fe | |||
66ab4d217d | |||
7a8b392607 | |||
bfdf069d2d | |||
b4b00a48d9 | |||
e5f226612e | |||
20619ad3c1 | |||
37dce7d4c3 | |||
a67b82e730 | |||
e0b8ad7911 | |||
dbb39d366e | |||
e1de36398d | |||
871c28aeeb | |||
c84dc6254d | |||
60cb1ad7c5 | |||
4a5ff5dcfb | |||
5c40307745 | |||
39426f03e6 | |||
92e5d2b8f5 | |||
09aa59f98b | |||
c9c1c89a27 | |||
96b76709e9 | |||
ca3107e21b | |||
2e4f223207 | |||
17df668186 | |||
aef70152de | |||
e0252f35be | |||
0f47534766 | |||
54feaa67e8 | |||
64ecfa49eb | |||
fdae613b14 | |||
c57c08b2c6 | |||
d2103995cb | |||
196fb0f16e | |||
c0afe7260a | |||
099c8703ae | |||
b03e480dbf | |||
8430353389 | |||
a123ec94ef | |||
4a2f54f6ff | |||
b5c85eaeca | |||
8b3b91d78d | |||
e1de3973fe | |||
c1993a6ffc | |||
ab26fc438a | |||
c37259b01d | |||
bde8cc3285 | |||
59ba5504d0 | |||
65eb5a3d05 | |||
b925322e9e | |||
f0c2ad00f8 | |||
fc53a25a4c | |||
58872d162b | |||
30f1b8f02a | |||
ee4f199a9f | |||
e3957f3bac | |||
2506673514 | |||
7cb12015fd | |||
caaac9b9ec | |||
1dac4d00c4 | |||
b41902f4df | |||
ada70dd683 | |||
7e5d8a8d54 | |||
e981cae27c | |||
d7c377c229 | |||
8772edcd33 | |||
254740cf68 | |||
c3775c0f56 | |||
5ecf40e967 | |||
45026df4bd | |||
b8830f4a09 | |||
811ee1d989 | |||
8c32102e99 | |||
1b135095c7 | |||
becf4396c9 | |||
929e066506 | |||
355ad9ac2c | |||
2cda0472bd | |||
4710753700 | |||
2b439ef209 | |||
2fc76e6d9e | |||
ff544450a5 | |||
b4590da686 | |||
6c4be0311a | |||
4a2cdc20f0 | |||
6ce79c62eb | |||
96994721ef | |||
f358bb1a96 | |||
1bce210c51 | |||
8a3c8d1b1c | |||
08b224de1f | |||
86c92c37d2 | |||
07053c3df7 | |||
4fb33c9b09 | |||
5e6a25c3c2 | |||
e14ef4a294 | |||
8b659f0f4c | |||
a6395c95d4 | |||
98e74dfd38 | |||
1d728186db | |||
2499f2ed80 | |||
963905adcd | |||
7c21ab0985 | |||
77d3712261 | |||
0376f22d41 | |||
007820b7c1 | |||
dc98711477 | |||
e98c6ff31b | |||
9eae74357a | |||
1ee88a2878 | |||
14189e6827 | |||
3dd6113a0a | |||
ae5cdea5af | |||
3c1f389e25 | |||
284d821233 | |||
78272e5592 | |||
098bc41083 | |||
c837090282 | |||
cdc1678e6f | |||
2a3d76b0cc | |||
29e89491de | |||
d63947aec4 | |||
512f0a67fb | |||
a7cd4657f5 | |||
bf02cde598 | |||
b7dbb35546 | |||
6fd5f0e3de | |||
e6469df065 | |||
d61fe357f6 | |||
965dd2ab67 | |||
78e011d558 | |||
76590d6c69 | |||
16dc6ce41a | |||
1dadcee5c9 | |||
bed3bb45f7 | |||
18d850d7d6 | |||
f920fd8b6f | |||
b9da6d9ef6 | |||
3be489c69e | |||
cab092aad7 | |||
0451cba343 | |||
12334cd9ad | |||
9d18a2dff4 | |||
869e1dc241 | |||
b878f3fc4a | |||
2aeabcc38b | |||
94778c0dac | |||
390491b37a | |||
f8ea825577 | |||
f77ad7d184 | |||
e467a53def | |||
9cd2e12915 | |||
627a6587b4 | |||
9b3b419f7a | |||
f37ed44837 | |||
dddc333407 | |||
75714fb784 | |||
5a6342a5fe | |||
e07379b406 | |||
54bfe34273 | |||
bef876e4b4 | |||
6ef9ab6a0b | |||
53ffdd8d68 | |||
ba198034cb | |||
58e66b9e1b | |||
ac7efcb43a | |||
b514b9ec7c | |||
f69942eb35 | |||
f28827f625 | |||
f20a6b3635 | |||
7f7ff3ce3e | |||
0abb0756ba | |||
070ed9396c | |||
307004a455 | |||
d097327bd8 | |||
f72f501931 | |||
e9584cfcab | |||
aec0e75d73 | |||
a6b4d49454 | |||
1cc726593e | |||
5001bd8810 | |||
46aa70aa09 | |||
41cf447c45 | |||
b328fd7aed | |||
917a24580b | |||
af36182879 | |||
160c721afc | |||
ee0040ef1c | |||
9e60a55dd7 | |||
57f27572ae | |||
ceae032112 | |||
d3ab367fcd | |||
0ceefb48c8 | |||
a9815ae1e9 | |||
79140fd132 | |||
6c0f48ce25 | |||
da7ab7057d | |||
499ae609dd | |||
e78b3644e4 | |||
ce6b39bd1c | |||
603f528b47 | |||
5fecd07045 | |||
8edd7ad32e | |||
3a27d0b849 | |||
b351536dee | |||
eeea8559b6 | |||
17591117c1 | |||
c4470fd1e6 | |||
45fc7ec01f | |||
dc54472ca5 | |||
4b3bf05aaf | |||
ec014a7ecf | |||
a8c87f3bab | |||
b39e76200a | |||
01deb9ef7d | |||
5c89568cca | |||
6119b44746 | |||
7af4a7bf88 | |||
04e4d69662 | |||
a87e0f028a | |||
31774a7711 | |||
a32f27a2aa | |||
805a409318 | |||
42d45bd14a | |||
5870709fbc | |||
0fdb7430ff | |||
4771f80d6f | |||
91405583fd | |||
f864303aac | |||
e5ebf4a2b2 | |||
725a36e37a | |||
78dacfa865 | |||
16547fb3c2 | |||
9eeabf79f9 | |||
81b71cc143 | |||
8301acd4d6 | |||
f80afb1755 | |||
71c6b60054 | |||
95a1b874d8 | |||
07de96ede9 | |||
209014b083 | |||
127f10e7a8 | |||
67615a0cbc | |||
dde20f0c76 | |||
ddced79475 | |||
e80795b7f0 | |||
080337a4e8 | |||
884af3c986 | |||
edc95b2cb7 | |||
96239e95ec | |||
331ad76675 | |||
29152d3022 | |||
73fa4b1cbd | |||
7766a91e8c | |||
b7bad765aa | |||
5af09a60e9 | |||
ef623cfbbd | |||
ce5faba185 | |||
40d9ed535b | |||
d5675765f3 | |||
65067c24cc | |||
e2463cb501 | |||
de2f2d7ef1 | |||
7cdb75e7ce | |||
1502e308b8 | |||
dd1fe68e5c | |||
85c1dc5a0c | |||
7b3a689aad | |||
2c28a12cca | |||
324021fd64 | |||
1dceae97c6 | |||
0bef925b51 | |||
0ba1f29e40 | |||
3b1e536822 | |||
9a83662a18 | |||
5bcead5b5c | |||
d8f9502ea2 | |||
020128b9ca | |||
1566a4e607 | |||
fbd59631ee | |||
6c36856499 | |||
f4cda49988 | |||
a6b49fe7d6 | |||
26966b2bf3 | |||
36a7429aa0 | |||
7be1fe09f1 | |||
62bf08d323 | |||
260c082c4e | |||
5e0ff7fd56 | |||
aac312ca34 | |||
a90401454a | |||
5679e6b3a9 | |||
194574bb0e | |||
337d2da38a | |||
df848aa084 | |||
0cc1615252 | |||
d4259fa8aa | |||
157d7e2b59 | |||
ee50904147 | |||
ec39aa3890 | |||
beb0fdf4b8 | |||
443fe813c3 | |||
69cdc5a9b9 | |||
dac54a6019 | |||
e5ca53e56c | |||
7a79236bab | |||
cd37e2908f | |||
3368e49aa7 | |||
9e31f05861 | |||
b52f4ed25b | |||
00eb764880 | |||
6d011a3700 | |||
f9d9caf417 | |||
b50702dd52 | |||
e74a82b9d6 | |||
12fa983242 | |||
1607df835a | |||
7b705dd670 | |||
323e3028b6 | |||
7e1b4692e2 | |||
c1dd971ce9 | |||
39610e6933 | |||
fdb189102d | |||
e54d095064 | |||
e6634c56d3 | |||
deb77a4dde | |||
90ea27c423 | |||
19533f87e3 | |||
e6c5c84133 | |||
404aaa5658 | |||
2c70df25f5 | |||
db959ac2eb | |||
bbf0dce390 | |||
1cbb8b9851 | |||
96387cce47 | |||
570fc68cb1 | |||
e6ce0057af | |||
a3f625fe39 | |||
bdfe459d64 | |||
95ec8ef5e1 | |||
b8198716d9 | |||
071a4e5f83 | |||
d99cd71408 | |||
030e6aa507 | |||
f146b01e3e | |||
c249ff9046 | |||
8b48560c81 | |||
bdbea2463b | |||
8fb2263471 | |||
ea55c36a3a | |||
37595ff3e7 | |||
cca008b73c | |||
b9dcbd9d33 | |||
b6bf8d5b2d | |||
43ed66cf26 | |||
57eae1be43 | |||
b394d184cc | |||
81e01b6f88 | |||
52efe32e0f | |||
e7886734c4 | |||
083c37a7b2 | |||
b24a10aa00 | |||
e1ef14d12b | |||
1a33de91e2 | |||
6e15e2d72a | |||
d58b715c52 | |||
804ff8b5a5 | |||
6e89d2f46a | |||
e99d69b7d9 | |||
f25416c3f5 | |||
a7bb6a2781 | |||
41f14e0e89 | |||
01bd10f485 | |||
476eacd5ca | |||
ca2fb74f41 | |||
8a2baf5b6e | |||
d44fc3aa1b | |||
7d78c42dfc | |||
a361180745 | |||
310dc10c4d | |||
96e02c4c2e | |||
afb3b9f029 | |||
d5e647a191 | |||
a1d37617a8 | |||
2541f85942 | |||
07fd23dc5e | |||
946311b2a3 | |||
4868032215 | |||
fb0cf64536 | |||
b37afcdba1 | |||
f644bee831 | |||
6fcc7e3e23 | |||
95602eb85d | |||
9f3afdf928 | |||
627a2412d2 | |||
acffd1e792 | |||
62ca4ba624 | |||
6ea8f35343 | |||
1bd8c67041 | |||
7425b382d6 | |||
5f61b57d63 | |||
9db73767d9 | |||
c562245c16 | |||
9525216d78 | |||
28a71a29e6 | |||
7b06d34ba4 | |||
656d24e477 | |||
df0f03d831 | |||
1db6d15677 | |||
08a0479c9e | |||
6682b7dfa5 | |||
aad5d98b43 | |||
a8a4a85dac | |||
ad71b969b2 | |||
8bcb10391e | |||
8e7d74bc3b | |||
629b6faa22 | |||
ef1e27966d | |||
ea0eb4ba09 | |||
f83ad77c08 | |||
af219ce9e7 | |||
2ac88a7fa6 | |||
c495ff8ad8 | |||
0e12215614 | |||
b4b13b0cb9 | |||
677cdfd6c8 | |||
e9f18c6d8d | |||
8640e18d1f | |||
300c4d8432 | |||
92c877493a | |||
ff4926be35 | |||
df3872f665 | |||
fcded2ea2b | |||
420f0109ee | |||
b4678a1cee | |||
5d3c90197c | |||
d5d517748c | |||
de3d3c15a5 | |||
3db253998b | |||
d3f978a19d | |||
a50ddd6d8c | |||
37700b4b1a | |||
a8f9871725 | |||
a4a5492782 | |||
9ebb80502a | |||
dcacbb37c3 | |||
34443da7ab | |||
09c14f1a10 | |||
e801647083 | |||
0122cee0ae | |||
1188b1be36 | |||
905c4932d1 | |||
6484da26f0 | |||
079041cf1f | |||
b8df7798bf | |||
e4af954a95 | |||
f3df62f27e | |||
4b3c9e826d | |||
ba4780cbd3 | |||
b1a6940188 | |||
3c8325f1f3 | |||
5fa9581db3 | |||
96001d86e1 | |||
963e808d98 | |||
d124ca377f | |||
a3d3d81447 | |||
244121d920 | |||
adf95fb90d | |||
f8446f1bfc | |||
b922035d4f | |||
b75fc2bde3 | |||
10e16cca22 | |||
6628214484 | |||
e3c9c46c1c | |||
6a1b341336 | |||
65f96494f8 | |||
6e3e2d9f29 | |||
25d8debb11 | |||
426b227f03 | |||
2fbd9b95a7 | |||
9758029c11 | |||
f21b8206af | |||
4f8f79a907 | |||
c9e2f16404 | |||
2658754295 | |||
77d8ff3620 | |||
c4621119b3 | |||
a2a292d48e | |||
8349b70ba8 | |||
dd6fe58475 | |||
386929e84b | |||
43a355e07a | |||
1a5d2ac459 | |||
21403b19e0 | |||
cb7778d433 | |||
07c0105c83 | |||
7da186d4e9 | |||
75885c3095 | |||
5d275389af | |||
75589b4450 | |||
a9b12d5d73 | |||
6a00a504d4 | |||
6b9cac928b | |||
b292384380 | |||
01ac5aeb0d | |||
1a41ee0c30 | |||
1950a67e15 | |||
65303d027a | |||
fae838b054 | |||
eab4c7bce9 | |||
6bb38b873b | |||
ba5860cdd6 | |||
ee62863759 | |||
4a3a61bea1 | |||
f3901eb668 | |||
b53d1df4df | |||
df9b0c548e | |||
3056fc4710 | |||
e411a4af21 | |||
3430012136 | |||
809295c03d | |||
c9783b38c4 | |||
44e4cbf04a | |||
049695b914 | |||
7b1aab3759 | |||
5e87fea1ee | |||
a187111a26 | |||
3044a6b517 | |||
824fbe09c2 | |||
764eed7599 | |||
ccb81919f7 | |||
4505c38b58 | |||
d9770dcffb | |||
9936f97fcb | |||
4cf09f46e7 | |||
6ffe3a424c | |||
641794d458 | |||
73d9ac6c01 | |||
3205d1e16a | |||
ec61004622 | |||
ae93d258a5 | |||
12845f0eef | |||
6b4f524620 | |||
dec0baa147 | |||
340609d149 | |||
9a30c3d722 | |||
e19a927579 | |||
53a595885a | |||
ebd7406386 | |||
66cdbc5cad | |||
060c049056 | |||
31270c36d2 | |||
0aa26e9134 | |||
b45a2d7335 | |||
07676d483d | |||
6436452bc9 | |||
60ad374514 | |||
b611ddda5e | |||
186bd156dd | |||
3628c81885 | |||
180000a531 | |||
5f995c64d4 | |||
fe2c2014de | |||
754df5ebe2 | |||
d8b7ac9044 | |||
5f3b04ecbd | |||
1f1aba4a32 | |||
1390cd4048 | |||
31cdac7bfb | |||
0705901bce | |||
87495575bc | |||
b292bba092 | |||
5c7f0a0ad8 | |||
2df86e1966 | |||
41406e3be2 | |||
7ed4bd67b8 | |||
8ce3531de8 | |||
579e53f02c | |||
1379c6b404 | |||
153d3045ed | |||
278686dc48 | |||
9b9f33bc8b | |||
24984458de | |||
e3eb4a20a5 | |||
001bbd36f5 | |||
382cc52baa | |||
2af368ab82 | |||
f514160fae | |||
30e4dcef90 | |||
4618e11406 | |||
f887b77e36 | |||
fb527139a0 | |||
261fbef516 | |||
32e8a9b377 | |||
5f95351074 | |||
1c57c0e651 | |||
ec6ee27f29 | |||
70234edd02 | |||
7e24a696bd | |||
c3ed936776 | |||
235ec7cb2e | |||
4f8586d81d | |||
6cd1e38425 | |||
f7512dc540 | |||
45415a742f | |||
2247065dc5 | |||
c550e2ccf5 | |||
bc75e5ec44 | |||
5b39496008 | |||
4016da6632 | |||
f2edcb9bdf | |||
3d8a87563d | |||
93bde0cae2 | |||
30179bb60d | |||
498bc21133 | |||
af7a34521f | |||
7fc2b33a0a | |||
a5d3f7785a | |||
f1cdce38a6 | |||
65e4652142 | |||
569797d7c5 | |||
35a7c94cd1 | |||
c8365b7444 | |||
8c5a343729 | |||
22ddec46ab | |||
a4e70ba4ca | |||
978ac65cae | |||
eb5a836822 | |||
ad1b9b71ae | |||
60257f422a | |||
6576d4852c | |||
c30661c44c | |||
2d9cf195d7 | |||
a21e76caab | |||
a534a6bf09 | |||
2b0f219bfd | |||
33dde63256 | |||
4dc9540325 | |||
2b1a661614 | |||
2138fc2349 | |||
cdc8dd88e9 | |||
2a5f8e84bb | |||
6f8540f25a | |||
151c699f5c | |||
9133f6d352 | |||
ab276e4e86 | |||
cdc4f85e47 | |||
b2fb84e361 | |||
4ab1e893f0 | |||
70bc3d178b | |||
777f145128 | |||
2f0181ac44 | |||
83b4b698a0 | |||
a10a294a2e | |||
34036a3b51 | |||
911bc03381 | |||
0240aeac8d | |||
a43be186d3 | |||
fb63f48d0a | |||
c0279df3c6 | |||
e614b993f2 | |||
8035f30305 | |||
af0b5e6176 | |||
1bf996c705 | |||
33f69481f7 | |||
354fb6b8c6 | |||
d30477b6f8 | |||
496b5d62ae | |||
3652002a68 | |||
c0e5719271 | |||
997f851031 | |||
eaf184b585 | |||
0616261a94 | |||
d2c45f428f | |||
6054ab35cb | |||
5ba0c6404b | |||
f738c2be9d | |||
12ac2e5534 | |||
60985b396a | |||
d9b33e01ee | |||
0335f2726a | |||
23717cc4d7 | |||
4422f301b4 | |||
1981da1b4e | |||
a130f50107 | |||
c40198046c | |||
e5b44a3970 | |||
5e7443706b | |||
39e75e932a | |||
b8a8edc513 | |||
b1051365fa | |||
a935cd0c5d | |||
62ce90795f | |||
6bcad45392 | |||
8747c0c58d | |||
b91d9c2867 | |||
140935bca3 | |||
67c7abaac3 | |||
11bd7dc7de | |||
406f65cb23 | |||
2344706462 | |||
fcc32fca7e | |||
7f587fd4da | |||
f47c0601ce | |||
3d638c692e | |||
e6ef3ea24f | |||
8dd880d0c8 | |||
393a3a079f | |||
ceb035e9b3 | |||
e38570437e | |||
f0203d1f19 | |||
e0c5a61be5 | |||
526a16298c | |||
443f02cd92 | |||
b682c8e052 | |||
d07c8dcd9c | |||
9bcce4f271 | |||
eab497a814 | |||
a8c8df6ee8 | |||
b3549f421d | |||
ae0accb5a4 | |||
d367566db9 | |||
35812208e7 | |||
afaff724f6 | |||
ec8dfb0cad | |||
7798da8dff | |||
89a49ce72e | |||
655dce6a4b | |||
2cf403a3e9 | |||
4351ad2414 | |||
fd2944fa8d | |||
f4626cf8ad | |||
65bf0d20e7 | |||
2f496de98a | |||
644b8304ab | |||
5c4570fcaa | |||
ac0bd3b116 | |||
7ad881d0ec | |||
9a25224890 | |||
3f6f597093 | |||
9ab22fe551 | |||
9794e71a86 | |||
8cb3884fae | |||
098bd4509b | |||
4dc5bac72f | |||
b150869b51 | |||
daf8be9f78 | |||
d14cf1e80b | |||
605d34008e | |||
d3135414cc | |||
b3f29b46cf | |||
4545719e18 | |||
cc60afa31a | |||
85743ede7e | |||
8e231cb2ec | |||
72405cd43f | |||
a9ad9d5e6d | |||
2d79c7333f | |||
7a79cfd76b | |||
6600d6b6d9 | |||
8ca25aed8b | |||
1db353a0fc | |||
2d384eb546 | |||
5d1de33026 | |||
f2ba49fa35 | |||
772ae1cae4 | |||
1d267f2642 | |||
cba299160a | |||
ea33193e73 | |||
ef69c228fd | |||
7ba0f07732 | |||
07696086a2 | |||
426581eb1e | |||
fe88811a40 | |||
30aaa6e26c | |||
4bd071bf3c | |||
309ac65447 | |||
fe246470ce | |||
660cfe707c | |||
a0d7d7bc4b | |||
758764ea75 | |||
6935c62a61 | |||
a34d4d9124 | |||
c49bb5aa03 | |||
427750d6af | |||
b9ad5f8727 | |||
c776826425 | |||
a8ede18db5 | |||
9aa84ec54a | |||
91a6520c3b | |||
45fde4888e | |||
484ef5f2f6 | |||
cdd354739a | |||
420f544bf3 | |||
15b33189ae | |||
685a9f36f6 | |||
3b57812e29 | |||
d583c48992 | |||
0e636ea67e | |||
c9ce1bd30f | |||
f2cbc3192c | |||
56d76791f1 | |||
df2cab877f | |||
27ad8305e5 | |||
c97b4dd48e | |||
59ecd610b1 | |||
42c1285ead | |||
622c1c9236 | |||
2d88508f9d | |||
86efba4ecc | |||
90fae00aa6 | |||
d497ed4040 | |||
c4a21ae5ad | |||
5e5798bee9 | |||
d793077b91 | |||
3e826f9249 | |||
2adfb60dc4 | |||
f7212cf80c | |||
69556643bb | |||
f0ebc84840 | |||
490eb59ea3 | |||
16a9391726 | |||
adf8ba67d2 | |||
c37b222cbf | |||
72f8f2beb1 | |||
16bb9c17f5 | |||
ca44977d92 | |||
bcf294475f | |||
3b31774dd3 | |||
c106347c59 | |||
2679be9d97 | |||
4d59368c7d | |||
3f29680fb6 | |||
8f1df14ae7 | |||
7159e360d9 | |||
65723bcac5 | |||
73ae451cb4 | |||
dc0ea3a248 | |||
9548cd8341 | |||
8410fc38c9 | |||
0c9c2168b3 | |||
2690f54743 | |||
22783813c5 | |||
b5ed46a5af | |||
1a3f17e296 | |||
9281129f07 | |||
0e9ddd3b99 | |||
d87db04e55 | |||
906ec3c8a9 | |||
449575ceae | |||
6255c77eba | |||
c33622f2b3 | |||
e4860acb58 | |||
e96867f757 | |||
57bd43baf3 | |||
3a4e595d32 | |||
dcad22bfa8 | |||
acba0e47d8 | |||
dd19459e18 | |||
8be3c5ed21 | |||
51726d8de7 | |||
8f41c6bad8 | |||
e294abc567 | |||
9f9518c872 | |||
5faf7cb59e | |||
4b095d532c | |||
4288761235 | |||
73388f30fd | |||
2d9ddd4bc8 | |||
55aa0cf303 | |||
52ca15b514 | |||
bd383888de | |||
1bd349485f | |||
155f9dc1b1 | |||
fe7ee1edc3 | |||
c11cbff605 | |||
fbc629266f | |||
75d44dca6b | |||
3e6b794a33 | |||
a757ce48a1 | |||
507f29a7bd | |||
e5f4f77073 | |||
f07fee538d | |||
fa1420b384 | |||
724a2bd72f | |||
d525d02348 | |||
4920cf2b98 | |||
f41d0938a4 | |||
924a405829 | |||
fc9225e24a | |||
ac202cbdd3 | |||
cedd68c942 | |||
4231c879ef | |||
42e58a4b72 | |||
ebd1bc83c9 | |||
0bc9d7455f | |||
5274166f8c | |||
5566aa4588 | |||
ab638a4f54 | |||
86a8452ab7 | |||
8c96c16eac | |||
45c2e6575d | |||
a786a7dc55 | |||
6adf5cbee5 | |||
1964b54627 | |||
c9d0e82c52 | |||
1a4948f0f2 | |||
66da3f5668 | |||
9ebeb64570 | |||
571aaece2e | |||
f60fb954a2 | |||
1d136cacfb | |||
994021d77f | |||
8899325a0e | |||
43876a9357 | |||
d6cace32f5 | |||
2388de455b | |||
b14b3ab276 | |||
32ccb779c6 | |||
52536e94e5 | |||
5d6b3abd26 | |||
79682bbcb0 | |||
770ff19313 | |||
8d4855f100 | |||
7d4e14f384 | |||
f162dd7e87 | |||
06dc12e217 | |||
36edff9e15 | |||
34a0c079de | |||
84b581ae13 | |||
587c93eadf | |||
b5aa549e0c | |||
5fb6738612 | |||
685262fc5e | |||
8802f6cf23 | |||
2698d0db29 | |||
2fa7230133 | |||
658243eae4 | |||
7282bc8859 | |||
07511cb21a | |||
f71f767a3c | |||
176daa1469 | |||
5487f8cf93 | |||
944762ac83 | |||
c66068c435 | |||
d479c936b8 | |||
c5a3f784dd | |||
529d012865 | |||
8a1b83417a | |||
6c64c0873c | |||
d2c6149923 | |||
9f98f6f02d | |||
2f7c0b1985 | |||
f61548234a | |||
6eef830ba4 | |||
88a9b7648c | |||
b7678493f9 | |||
a9ec8a354a | |||
a42d35deab | |||
2008feb7da | |||
c41424b57b | |||
3bd5563a7e | |||
eff8ec00c4 | |||
1256af7b9a | |||
b38ecaf925 | |||
bd5d945fb2 | |||
15063ef3d5 | |||
3b8a125732 | |||
9860b1c677 | |||
a0e340f06e | |||
57d3bec95c | |||
be10f3c2b5 | |||
f7af96dbb2 | |||
468a855f04 | |||
429f9e1d15 | |||
5de91197ae | |||
bc6b4b01ea | |||
e89ce7358a | |||
bdf0a6fd0b | |||
783abd4f5f | |||
28aa9201f0 | |||
378df515d0 | |||
04d68c6e36 | |||
5308d12239 | |||
15cac0157c | |||
fc696bc054 | |||
5a7b7b7389 | |||
9c9cf6f5ab | |||
0155739bef | |||
d0902fa28b | |||
3aa0d455c9 | |||
835d4e4b58 | |||
6075332f2f | |||
c5d8484e19 | |||
575ab0d609 | |||
b48a7d5cfd | |||
9af107feff | |||
70b5db16d3 | |||
5072ea7e47 | |||
13b83c95b7 | |||
e48dbe65f1 | |||
14fb51e6d7 | |||
b42af9aa99 | |||
ca2ee22827 | |||
13a2b28bf1 | |||
04bf75d137 | |||
76c589702f | |||
c11f89e72a | |||
cfacb05461 | |||
c4ba31cb23 | |||
db0eacddc5 | |||
62972f5138 | |||
6fe263f19f | |||
b6a87acb71 | |||
784b04b191 | |||
3af9f636af | |||
16c0585b95 | |||
fc9a96ac85 | |||
9dfc3af9d7 | |||
8dc63932fc | |||
066e5cddb5 | |||
27ffad2148 | |||
41db363b06 | |||
184ac11c93 | |||
673aa3c5f2 | |||
2dd1c04bf3 | |||
4df1222ce7 | |||
208121646c | |||
f47a11f792 | |||
36fc3a5c96 | |||
ed12c9d611 | |||
4153feeb15 | |||
349c642d10 | |||
4728105f43 | |||
1f46a0dc26 | |||
7ff7ced504 | |||
a43ee41b0b | |||
ec7ade4ee6 | |||
9b846cbb83 | |||
2cc7fd07f8 | |||
72d54d9915 | |||
b390b82e9d | |||
1ef9ee13a3 | |||
6822795bb8 | |||
187dec2816 | |||
31f75e4607 | |||
92083eaf76 | |||
6b40c3974d | |||
c528401b62 | |||
8a17f512f4 | |||
e725f8a0fe | |||
525d3c2619 | |||
aba46720c0 | |||
2fb1d707fe | |||
dd06aa98ad | |||
cfb66ca153 | |||
0b4660702b | |||
9760227c66 | |||
5faeaa2028 | |||
cd58f9f9e5 | |||
00338bbc4b | |||
31d14a2fb0 | |||
49fa0ddebc | |||
d50c3e6c67 | |||
b39f56e813 | |||
ead0286ca6 | |||
6eb05af306 | |||
6b1e632621 | |||
29cb10fed8 | |||
a88433dba6 | |||
7e5f1fe411 | |||
00c78d33e4 | |||
50fbf9982e | |||
71ad39b6e3 | |||
3d329cdf2d | |||
21af5c8f73 | |||
716d7bb93e | |||
98f4b99446 | |||
830e701d13 | |||
57d9e7d5aa | |||
5fc16bb05b | |||
cb08bd2e2e | |||
961dba7faa | |||
41f933b89e | |||
d4dc1c87c5 | |||
e58fbeea17 | |||
2492dc50db | |||
b4f5f1e461 | |||
69347fff09 | |||
5c439f4e9c | |||
c84236ed73 | |||
ae1a3a0cb2 | |||
b3fea016f1 | |||
7c93639e59 | |||
affcd5c7de | |||
e00eb06f66 | |||
5ecc204d57 | |||
2a5eed1eb4 | |||
d517c13d7a | |||
bb88265d78 | |||
858694f4cc | |||
a46321baa0 | |||
9808e8ab0d | |||
7bd7b53845 | |||
056cfc9dc6 | |||
b7b60d103e | |||
14fd0eb73e | |||
ca73017100 | |||
123fb350ce | |||
1a27d7dfc0 | |||
b0c6d44515 | |||
6092e01428 | |||
e20ff5ae45 | |||
621a0d70e2 | |||
855b238ec5 | |||
96f44e1959 | |||
242dc8cddd | |||
b8a2004872 | |||
63f08bf2c2 | |||
4734a9ac18 | |||
81cd1e6c40 | |||
0b9cdea7d2 | |||
f63fceb2ea | |||
f0f7165ab6 | |||
2511f60d39 | |||
c0f868dd56 | |||
a112bfdaa2 | |||
4535a70f08 | |||
b59529e579 | |||
393c23820a | |||
e6a3958e45 | |||
59a7fdd2c9 | |||
c3cab28c9b | |||
4c55a6f436 | |||
c10e4c3117 | |||
3b70094151 | |||
a370697385 | |||
8a7c0313f6 | |||
00201f7e6c | |||
5e3111bad8 | |||
a327c10c60 | |||
938bc07e3c | |||
d053d45a6d | |||
5200da6eec | |||
b0523860d9 | |||
1a1215527f | |||
360c018c9e | |||
7bed964ab2 | |||
989d0a5682 | |||
b121c25184 | |||
e0d127b3e4 | |||
a0c56c74e1 | |||
f4b61a7f24 | |||
b2400fb883 | |||
f25832d1b6 | |||
cf363171aa | |||
38c8569d16 | |||
9791d15f39 | |||
80e7f5832b | |||
6e46ddaad3 | |||
8499e4aab2 | |||
5eae1851a1 | |||
c48a246ccb | |||
f084011a61 | |||
bd40cf194c | |||
2434af7e8a | |||
ced7fa9f01 | |||
28b559e812 | |||
76d776245b | |||
0d9f70492e | |||
b58f502dd6 | |||
5f367248c5 | |||
c2065cc3e2 | |||
034408971d | |||
490206b5b2 | |||
d8540819af | |||
d1a9aec526 | |||
e4b681a5a5 | |||
52036871d1 | |||
4fc6a804f5 | |||
08d2ca300a | |||
dc2ec0a8f9 | |||
2cfed952bb | |||
a7da137778 | |||
5ae2f87ce9 | |||
c4f6619fbd | |||
65edbc1424 | |||
1f2d7fa28f | |||
2407a0c4e2 | |||
7f8bfcc939 | |||
fae4cb9e56 | |||
063bbb02f2 | |||
15e1470c50 | |||
f602993aa9 | |||
271508c0a8 | |||
50f96d1c9c | |||
809544800b | |||
e757b06987 | |||
6f2da1baf1 | |||
f30dcad8a7 | |||
7dc235511e | |||
644c210fe2 | |||
68e8f98671 | |||
1228db6ac1 | |||
86596749fa | |||
76229a3601 | |||
95abdeb919 | |||
df0151d338 | |||
ee2f12fe81 | |||
10c104529f | |||
cbc8ec6508 | |||
8e7758e280 | |||
9aefbd189c | |||
b9d50584d8 | |||
e5f7390c09 | |||
797b1ff8bb | |||
09b738045c | |||
4fd690333a | |||
5ef5e25a7f | |||
87e8770cbc | |||
cac9d120be | |||
1ae0fadbf4 | |||
65497e089a | |||
2d763bd033 | |||
4ebf396cf5 | |||
6faa50d496 | |||
6fcd6298c8 | |||
5c16be5dbb | |||
e3abb6f178 | |||
70736be4eb | |||
1c3e7330f3 | |||
6e4c89b310 | |||
73b4a0ef5f | |||
d88002c4ed | |||
025c63c045 | |||
b189cbd46e | |||
a21ddb5914 | |||
e04a4c3923 | |||
ae0821e07b | |||
a508bece36 | |||
2c9969a8f4 | |||
dd9fc91f6f | |||
39d9838cc1 | |||
1397c7c624 | |||
c09fcba94f | |||
c77b38fc4f | |||
aef9b733e5 | |||
348208617b | |||
2c34c8e20f | |||
d19fa731d6 | |||
59bb1cc387 | |||
56909d0646 | |||
04da29c939 | |||
307f7a3024 | |||
e80bfa39f5 | |||
29714922ea | |||
88192114ac | |||
9d78208b76 | |||
d817bf0395 | |||
4d51056226 | |||
a607174a25 | |||
37d6a624b7 | |||
0c807bddaf | |||
a0470bfc66 | |||
b9463d23e8 | |||
04debd1623 | |||
9d31576cf5 | |||
8daca28a90 | |||
9899604261 | |||
687e1eabed | |||
eb09f34114 | |||
fbeb446ed7 | |||
92033ce0f5 | |||
9171bab5e5 | |||
f9819eb7b0 | |||
85728f0d15 | |||
9396849d56 | |||
6f5e5672bb | |||
b936e60876 | |||
fa4bd91213 | |||
81eeef7d3c | |||
a7b5134820 | |||
92a01c67ba | |||
71c23613b5 | |||
4f876995de | |||
9cf4a76196 | |||
9efe5287e4 | |||
8c4b34de4e | |||
93e3559dc3 | |||
caa0f63e1f | |||
a4e29e1244 | |||
599f2f43e3 | |||
3a453c5f73 | |||
98b313c75f | |||
8b796d80a7 | |||
719a8908a6 | |||
fed007ecae | |||
990443465f | |||
1f183b8a4e | |||
96556eb959 | |||
3ffeeac577 | |||
d7929a2340 | |||
667019a8c1 | |||
d9b46b4782 | |||
e249218a9d | |||
a2b1946b01 | |||
96a80f7ba0 | |||
9955fbf4f8 | |||
14966b0cd0 | |||
6e1a8f16a8 | |||
d106191e6a | |||
418cf6281e | |||
bdeb7d86b6 | |||
a0e0cc1038 | |||
78f6dec73b | |||
f6458f215f | |||
c7e0d547c4 | |||
fa54cfa0c3 | |||
31ea3f737c | |||
2a8a8065a8 | |||
d3ba002313 | |||
f18fd8d959 | |||
d54f7b13fb | |||
9c8b75206c | |||
15273c7f22 | |||
0ea8217c55 | |||
1735f28f5a | |||
8de4070f7f | |||
45e64f453f | |||
62dc5f2ac6 | |||
86c85a752e | |||
3de0ebf7fd | |||
1496ba0bbd | |||
f5974f6793 | |||
928ea3bb01 | |||
b7acb1d488 | |||
360d94dd67 | |||
671a813135 | |||
c005417f9b | |||
b5ec68bda3 | |||
b4ed29094d | |||
284ab031b8 | |||
6ad5064c09 | |||
3a8e723a36 | |||
dd3484b93f | |||
205773b700 | |||
f2039070e6 | |||
cf58a7eafd | |||
487749c25b | |||
48fb16b570 | |||
f94369dd6e | |||
fc0bd3b9e8 | |||
19946f1d19 | |||
562f56130a | |||
99f97adfc6 | |||
5ad7db722d | |||
a7d344d287 | |||
0ac215f9de | |||
4342155748 | |||
b561694bf0 | |||
cae9a8d608 | |||
88fbdba018 | |||
046067565a | |||
11299d9913 | |||
42ab233b08 | |||
4a92d7d1b2 | |||
2502ca6ccc | |||
651030ba93 | |||
8dfea9566e | |||
8cc54ce2a2 | |||
3abfcda8b5 | |||
cf0ae8f182 | |||
d90bf5c6dc | |||
955b550e95 | |||
b3cd46a5c8 | |||
3fdc8bfa3d | |||
e8f96a6e16 | |||
dc15df1aa7 | |||
05f5fac35b | |||
5bfcc5392d | |||
d2e830cce3 | |||
1e942be639 | |||
576009bad0 | |||
f71108a214 | |||
7109bd52f2 | |||
0f6effa263 | |||
8b0301ed00 | |||
4f56fb125e | |||
6487cd8c6f | |||
06e5c25383 | |||
36e5ae4a25 | |||
245e43ea8c | |||
52871c781a | |||
f994ada576 | |||
147a6e49dc | |||
be24ee435c | |||
9fac285b69 | |||
3ed5f9cd15 | |||
17e70ff8ec | |||
6dab119650 | |||
f80eb89d57 | |||
f6c2902fe4 | |||
1e890a8a0a | |||
aa1a84e677 | |||
9395f310d6 | |||
b99bb3d4bb | |||
4f66f096ff | |||
7f5f2284f3 | |||
59f9f4fca1 | |||
55284e418c | |||
20d4ffde6e | |||
78e5d4df9d | |||
1118ec9653 | |||
7d6c85be42 | |||
0192a6cb12 | |||
0e01a81219 | |||
36edf20273 | |||
a1bf19dbdf | |||
0ad739e78b | |||
ff9509b901 | |||
7e496b1979 | |||
448517032e | |||
a171e92e6c | |||
06e9bf9b0a | |||
7d9ec8cea0 | |||
5b4553ff0c | |||
d4ce7aef59 | |||
8b6df2e23f | |||
e2ff5846df | |||
24efeff788 | |||
4f359e62df | |||
e6fd2bed4d | |||
9fb6510135 | |||
7206b61838 | |||
e8ab0b3e8f | |||
843788580e | |||
4ceb3d890d | |||
3f8995b25e | |||
37e0cefc79 | |||
f026741dbb | |||
044b121e01 | |||
d57c3b4f89 | |||
960f7d5f2e | |||
2f61381651 | |||
ba6e931e21 | |||
440aa0d369 | |||
f42d4b5fa2 | |||
5d9fa2c484 | |||
f5c86fa171 | |||
f9019ce62d | |||
ee485e1728 | |||
8f2a6f8387 | |||
908bf3b117 | |||
6a739afd25 | |||
506490e32d | |||
846771f2a1 | |||
759b7584e0 | |||
e5cb224598 | |||
e32df1b405 | |||
db9f91b687 | |||
0941357068 | |||
d8993c52d0 | |||
6424b2dd03 | |||
2a4eb3ed1a | |||
c9fa00cce1 | |||
18eedbc02d | |||
ef9f63fe59 | |||
f8ce788425 | |||
6c1bd95643 | |||
2ed7ee8f71 | |||
2e63709450 | |||
6611d639a8 | |||
fe124e6ab3 | |||
fee0a8527d | |||
2f6b00403f | |||
dbe2c117e3 | |||
c4b1ccb6d6 | |||
2c70ee7e43 | |||
f7826616b8 | |||
9f476a12dd | |||
6f3cf0ae50 | |||
cd024e21f0 | |||
c4c470c1f3 | |||
dc9ad8df80 | |||
74d6225993 | |||
e1e0c5035d | |||
adf6d0eb82 | |||
90df435345 | |||
83d57211db | |||
0a8eeb2827 | |||
0c324c42f4 | |||
3adf54a952 | |||
8076c66a4c | |||
f9c583a636 | |||
30d536b19c | |||
1957899146 | |||
b52c83d88a | |||
27ff388413 | |||
d4306f7768 | |||
5d0a57c97d | |||
a87ba467ae | |||
7df7cd01eb | |||
098d805a8b | |||
4a21034a00 | |||
7904e359f2 | |||
0c8a94beb8 | |||
c13a573792 | |||
f9b42e12ae | |||
5c2031b768 | |||
49643882d4 | |||
175ddaa3a1 | |||
cc1d6e97b8 | |||
79b96ab301 | |||
44d9316023 | |||
5b0c9a74fb | |||
3429fc3e4c | |||
d11d8d5353 | |||
1742bd6ded | |||
9f6e118ea0 | |||
717bbd3bb5 | |||
e8ebe4de14 | |||
0ff614ccd4 | |||
d0a77b7e0c | |||
2815889090 | |||
1317956663 | |||
a2303d8895 | |||
2161c793dc | |||
a2f943db8f | |||
23a31b9c00 | |||
2591d1b94b | |||
300f97f102 | |||
7e3f6c3066 | |||
601d232064 | |||
8854ac84ad | |||
0302c3fbd4 | |||
6b6fdc6cfe | |||
7126ce86ec | |||
760bf52f75 | |||
6d791d31de | |||
21adc98d70 | |||
6fd7a56568 | |||
fba427fcaf | |||
7753361ffb | |||
c1590d9ed7 | |||
23c1138a58 | |||
2acb097662 | |||
449d116af2 | |||
110240981d | |||
ac5c6de929 | |||
51bdc44352 | |||
bafe34696d | |||
114f6f577f | |||
1f30670c1d | |||
10884ef7f5 | |||
5e12e5f42a | |||
e7a2e1f268 | |||
cc6744055f | |||
e6ba7c6e40 | |||
ea4855e908 | |||
5c7da4b0e6 | |||
9659d934ac | |||
de93677271 | |||
6ef3c628e6 | |||
f2af0be9ac | |||
ef7b74a104 | |||
906368d916 | |||
809cbf58c6 | |||
a5d60050a2 | |||
2c130c8668 | |||
c0ff02d9c7 | |||
cb627db392 | |||
33bd6bc75b | |||
ea5bf109cb | |||
b150a2842e | |||
2a46c39019 | |||
5ca5f026c5 | |||
15f22add79 | |||
131fa6a359 | |||
2d184e1842 | |||
95a55a2c1c | |||
8eecbb5c17 | |||
abfdcaa1b5 | |||
8ee74e5661 | |||
ff31ccdd30 | |||
f6645a41d2 | |||
daceb8105f | |||
4beba796d7 | |||
70c34baafb | |||
e71c016477 | |||
633bbd8a9e | |||
4c82df56e9 | |||
0d62ec5b03 | |||
38f943ef81 | |||
e421953fcd | |||
f39098a4f2 | |||
b203a95a78 | |||
2bb1a6792a | |||
e30d45a2b0 | |||
afcd90ae27 | |||
94c1d5a18c | |||
4254fa3d38 | |||
3b293e91e3 | |||
3d6320295e | |||
7654f1ca3e | |||
e62c66b153 | |||
638507caff | |||
107f5de58e | |||
b1451523ca | |||
f7a95b5edc | |||
be290fafe7 | |||
b7ae74edb9 | |||
b3041ae9fc | |||
7499b04638 | |||
bf2d2071fc | |||
488820daec | |||
77c15b76b5 | |||
e71129aa68 | |||
6f5b700833 | |||
9627864ca8 | |||
2a8625ffae | |||
ed82c3763c | |||
22266899dc | |||
9dfd1bfa41 | |||
409af28cb7 | |||
4fe604bfe8 | |||
49df72ceda | |||
2db029bcdb | |||
7fd128eabc | |||
5e3d8dd3eb | |||
785be2f327 | |||
8cf9baa132 | |||
09e3aed770 | |||
452ac297ab | |||
7ebb5c6a10 | |||
00e0d24a6a | |||
8a269041fb | |||
9f48adcff9 | |||
48e7d732b9 | |||
30048b74d1 | |||
c866b0dbfd | |||
1c79f18b13 | |||
6d704be88b | |||
5af389c087 | |||
475dde4193 | |||
8c534163e1 | |||
657887b241 | |||
10da35cbef | |||
00f15c1075 | |||
19f92df8ab | |||
8639cf8060 | |||
2501d29e9d | |||
8109dd684e | |||
5a259dd6b0 | |||
2ed28211ed | |||
9d0eaa216f | |||
58477282fe | |||
6b016c2528 | |||
b98c5f94ee | |||
ad8bdb929a | |||
5030d59fcc | |||
ec52928736 | |||
7f479e18e6 | |||
7999e9020d | |||
43ba93a817 | |||
b37e02c90a | |||
be5df17a4a | |||
ffbc1fd190 | |||
457fb604dd | |||
09c81f79f6 | |||
0725a7d836 | |||
f957ae71c3 | |||
011c9d1beb | |||
aed589a98d | |||
3830e90642 | |||
3710b88ab9 | |||
5c990f103e | |||
2890fc9d7d | |||
0223507a2e | |||
59412a9405 | |||
110d58bbc3 | |||
eda17defb2 | |||
f563fb124e | |||
14d0a96999 | |||
f0474ffccc | |||
11ce6845f2 | |||
70d610b5e4 | |||
f5ca649977 | |||
8954d33019 | |||
2e6205bc05 | |||
e0dea63079 | |||
7c244b01c6 | |||
3f5edf7c3e | |||
92d8d65543 | |||
cebd8e14e9 | |||
16e92a7ca3 | |||
59e2710137 | |||
f1ca96bbf0 | |||
ec01f5d5ee | |||
2a800e4ce0 | |||
ca2e09fe8b | |||
7e343f11f2 | |||
db20a54861 | |||
ec4f6b7f91 | |||
08a69cdcbd | |||
aa9c095c8e | |||
b257029d3e | |||
cb9062f818 | |||
3a1f623ea3 | |||
47d46e367e | |||
e5534e86ab | |||
e0ca572572 | |||
ca4b86e7ca | |||
f177bd0b51 | |||
11086e8ef8 | |||
1237aef727 | |||
79bfea5970 | |||
50f8ae6fc7 | |||
73ec86797b | |||
703417a760 | |||
3013a87bd2 | |||
833cb2556b | |||
8f4f8fccdb | |||
38e4ab1cea | |||
366a3e2c35 | |||
fcb175ebb3 | |||
a76cc79f88 | |||
1a6d74fcb2 | |||
eb351b1882 | |||
43caace1b6 | |||
167ed7c35b | |||
69735940ec | |||
1a7fad129d | |||
6931e6cf0f | |||
87e021cd2e | |||
d3f5d94afe | |||
a3d8b5e526 | |||
5e46abfa03 | |||
f4740bac65 | |||
dc10e61a20 | |||
82c8aad157 | |||
4ba1f26e4d | |||
f8805e8311 | |||
c815979f2a | |||
54e5ffcac1 | |||
3fd0502cb9 | |||
8aed51180f | |||
e616877fd2 | |||
df15ee4074 | |||
4cf3acd0ec | |||
77ea16e18f | |||
85d993386a | |||
14a7da767b | |||
2f3df71cc1 | |||
dc13e72b89 | |||
2beff9896a | |||
43af6f4792 | |||
04d1a35cb5 | |||
f075b36d4f | |||
c21b1e5fe0 | |||
042c1fd54b | |||
ee6086373b | |||
382310fe51 | |||
d52cb510fc | |||
c1de2788b1 | |||
41dc9e0894 | |||
d3b0d23d8f | |||
f96dcaccbe | |||
2672f205fd | |||
1ba21ed367 | |||
79408d359c | |||
12c76e53cb | |||
ac44426557 | |||
684e5bec15 | |||
70d19530c4 | |||
e34ab6c5ae | |||
b3cb9d8459 | |||
df3fa973ad | |||
1f4895428d | |||
417941a1bf | |||
973a6b21a2 | |||
b50604d070 | |||
a1ecf459b3 | |||
6ea80eadd6 | |||
9dafaa2c0c | |||
50e5736c03 | |||
46fcfb7629 | |||
8a86540090 | |||
785ab8192b | |||
3c386e0c50 | |||
bec48492ab | |||
13a10f8afd | |||
00fe97a55a | |||
9e665e4903 | |||
efc128e681 | |||
6851c5443a | |||
baf08dd688 | |||
67115b3e9b | |||
4ca2697271 | |||
4696bfbb80 | |||
18c62a1987 | |||
d60a4e97d6 | |||
43883a6945 | |||
58ff5c6cc6 | |||
3902e8bff0 | |||
5d6f37017b | |||
ecbf2f1429 | |||
56dc2eb96e | |||
2154a22c90 | |||
3fafe0da07 | |||
983535224e | |||
88effdd9c3 | |||
e9a4843eb0 | |||
c50132b29f | |||
e92a6b3c6c | |||
66a36bf591 | |||
39559f0a09 | |||
712623946f | |||
47ce72e011 | |||
7f8c5c4f64 | |||
64fdff6ace | |||
fa2b413095 | |||
46b7a95ec8 | |||
55922c0568 | |||
1c927868d8 | |||
0a1f0e58d0 | |||
9024c5d7ac | |||
370de395f7 | |||
8ebd2ff9eb | |||
83a1c7283d | |||
695f84e14f | |||
54f3036f86 | |||
446583400e | |||
3776c50986 | |||
4ff0697ee7 | |||
cd43c4983b | |||
0f065ebce6 | |||
a2e0e0ad06 | |||
d721fc2c17 | |||
becaeafeac | |||
99c97707ac | |||
75ed427e04 | |||
d1e35d11d0 | |||
ee0102e86e | |||
776b71bad5 | |||
37da87d9e1 | |||
ea14d74141 | |||
4b60a8deb8 | |||
7cca0a9cd8 | |||
f347aba01b | |||
8c40ded498 | |||
c267a7a7f9 | |||
0959adcfe3 | |||
7d12f47bf8 | |||
dff673533d | |||
816a29d582 | |||
27fbe4cd83 | |||
2ee6f49a93 | |||
0ea2d98768 | |||
b5dc78a968 | |||
ebe7890a49 | |||
ef4231b9c0 | |||
7e2bab48c9 | |||
eebab34b62 | |||
05d613f3a3 | |||
5c6b1fd0cc | |||
f8eab659d9 | |||
4a214d8238 | |||
44b4741e35 | |||
95b9863a70 | |||
4dfe3d21e1 | |||
3568e6d42d | |||
c3ae4542b2 | |||
3031036cb0 | |||
11dc510ae4 | |||
79c25d1dc8 | |||
1fd17b5027 | |||
7a6b2abfeb | |||
fe8e62c6a8 | |||
e937fd07e5 | |||
c668d16a00 | |||
c33e2d1971 | |||
0711a70398 | |||
cb5095370e | |||
8f71920622 | |||
3da0e9e86a | |||
e8c8fecb35 | |||
8bb2c425c6 | |||
1f6d807882 | |||
8d1ed9f88a | |||
d766f865f3 | |||
c540cb5e16 | |||
b36029f6c4 | |||
a8e6bfcb3d | |||
9c7222daea | |||
ab5c0bcfcf | |||
1a2dcf213e | |||
6f8e7f07f3 | |||
0ac4787f1a | |||
16bbe9782b | |||
5594c119f5 | |||
a909017a2b | |||
9a7914eee9 | |||
98c461dcd8 | |||
5991c8dca3 | |||
b9226fbcbd | |||
b6375d3e40 | |||
8dc9ceb2bc | |||
6cb713b0e3 | |||
dafa27fccd | |||
190f2f82ac | |||
43767ab2b9 | |||
3042e647ff | |||
a004ad6088 | |||
addb247c33 | |||
8f129e1c9d | |||
341b6a1290 | |||
6a9b1996e4 | |||
a5f2d289bd | |||
e3a8228065 | |||
fd04c59e63 | |||
f8b1a84b81 | |||
b53d18fe1c | |||
d0a94768a6 | |||
80fde70995 | |||
cbb56b6128 | |||
cbd2188c04 | |||
2b30afa618 | |||
1a65374e21 | |||
3e94f6bc3c | |||
1dbbb4f91c | |||
b3b847fadd | |||
ff25a5e251 | |||
aaf61cbbbe | |||
ca7c4edd41 | |||
028e83181b | |||
31c3783c7c | |||
f63371dadb | |||
9f07a3dd4a | |||
06262a903e | |||
e01e79d9fb | |||
2c627cad10 | |||
b33e51be45 | |||
b17f5b678d | |||
c6a6ae594a | |||
9f02129af3 | |||
6037350380 | |||
5de38cd9f6 | |||
7950c4afdd | |||
e304854fa5 | |||
d7f54213f1 | |||
12a71be076 | |||
855f0dbcad | |||
03db0d630e | |||
5a35c57866 | |||
d11027e8c8 | |||
aa733b5e53 | |||
355ec9ceca | |||
aa30c6a323 | |||
ad5572f275 | |||
9082b4df48 | |||
c3179583c3 | |||
4db87839a6 | |||
b018d49dc3 | |||
83362226cf | |||
1e3796967e | |||
8d7fa54af4 | |||
59cd9b4220 | |||
59dfb5866d | |||
a29ceaa8ec | |||
782e11b96c | |||
fd3be5b7de | |||
0b9a5c3441 | |||
7431c4d611 | |||
ec78dd60fc | |||
b479eec758 | |||
413ace2eb1 | |||
d8390ef77f | |||
e7e56e175a | |||
e875b9cdca | |||
66197b18b6 | |||
ed991f79b1 | |||
48b70f358d | |||
56adbda2dc | |||
7dbc78c95f | |||
3ffa1e35e8 | |||
114fb4219b | |||
4d7d66bc1f | |||
e134d2e5a5 | |||
cca84761a5 | |||
c2a5d6f61b | |||
4449007bb4 | |||
30cd1e84bc | |||
dcf872b485 | |||
c9d51ca775 | |||
96c9f8058b | |||
a3f4bca14e | |||
e55f2dd3b9 | |||
247566ca1d | |||
aa120e0902 | |||
5c7992beef | |||
6a615f30dc | |||
bfea41b771 | |||
a2465e0670 | |||
57044ea9a0 | |||
1392be2952 | |||
9e503aa69c | |||
a004389f25 | |||
d474261912 | |||
144e959cab | |||
626488e659 | |||
5bae5574f0 | |||
ce14d6d9db | |||
519addf6ac | |||
fd87468e36 | |||
75e49610cb | |||
414fe75d02 | |||
26031eb761 | |||
be801dff5f | |||
5fd47ed742 | |||
f7f2f50435 | |||
e0bb15e572 | |||
0e4171a87c | |||
34cb92ff4c | |||
22eea750f3 | |||
c3afe1a83a | |||
904ceba6b2 | |||
a28d639c3b | |||
c0652bcd8f | |||
54dc0fd123 | |||
c22a00afee | |||
2c3ec7846f | |||
46db9edacc | |||
4e6fa56c87 | |||
8970e17911 | |||
ac6c808124 | |||
26d3b1929e | |||
a29507e452 | |||
2c073fb005 | |||
d9c3b83d1e | |||
f5e58c500f | |||
5e865f5bc4 | |||
01a1255967 | |||
dd80f39049 | |||
19e4c953ef | |||
ef218c95fc | |||
eec9dba855 | |||
f46a165886 | |||
ec47bd1604 | |||
bfd4016c1c | |||
63cdb1848e | |||
10a0762fe7 | |||
76472b86ed |
5
.gitignore
vendored
@ -16,6 +16,7 @@ config.log
|
|||||||
config.status
|
config.status
|
||||||
config
|
config
|
||||||
configure
|
configure
|
||||||
|
data/50-gnome-shell-*.xml
|
||||||
data/gnome-shell.desktop
|
data/gnome-shell.desktop
|
||||||
data/gnome-shell.desktop.in
|
data/gnome-shell.desktop.in
|
||||||
data/gnome-shell-extension-prefs.desktop
|
data/gnome-shell-extension-prefs.desktop
|
||||||
@ -37,6 +38,7 @@ docs/reference/*/*.txt
|
|||||||
docs/reference/*/*.types
|
docs/reference/*/*.types
|
||||||
docs/reference/*/html/
|
docs/reference/*/html/
|
||||||
docs/reference/*/xml/
|
docs/reference/*/xml/
|
||||||
|
docs/reference/shell/doc-gen-*
|
||||||
gtk-doc.make
|
gtk-doc.make
|
||||||
js/misc/config.js
|
js/misc/config.js
|
||||||
intltool-extract.in
|
intltool-extract.in
|
||||||
@ -44,6 +46,7 @@ intltool-merge.in
|
|||||||
intltool-update.in
|
intltool-update.in
|
||||||
libtool
|
libtool
|
||||||
m4/
|
m4/
|
||||||
|
man/gnome-shell.1
|
||||||
omf.make
|
omf.make
|
||||||
po/*.gmo
|
po/*.gmo
|
||||||
po/gnome-shell.pot
|
po/gnome-shell.pot
|
||||||
@ -83,6 +86,8 @@ src/test-theme
|
|||||||
src/st.h
|
src/st.h
|
||||||
src/stamp-st.h
|
src/stamp-st.h
|
||||||
src/stamp-st.h.tmp
|
src/stamp-st.h.tmp
|
||||||
|
src/st-scroll-view-fade-generated.c
|
||||||
|
src/stamp-st-scroll-view-fade-generated.c
|
||||||
stamp-h1
|
stamp-h1
|
||||||
tests/run-test.sh
|
tests/run-test.sh
|
||||||
xmldocs.make
|
xmldocs.make
|
||||||
|
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "src/gvc"]
|
||||||
|
path = src/gvc
|
||||||
|
url = git://git.gnome.org/libgnome-volume-control
|
331
HACKING
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
Coding guide
|
||||||
|
============
|
||||||
|
|
||||||
|
Our goal is to have all JavaScript code in GNOME follow a consistent style. In
|
||||||
|
a dynamic language like JavaScript, it is essential to be rigorous about style
|
||||||
|
(and unit tests), or you rapidly end up with a spaghetti-code mess.
|
||||||
|
|
||||||
|
A quick note
|
||||||
|
------------
|
||||||
|
|
||||||
|
Life isn't fun if you can't break the rules. If a rule seems unnecessarily
|
||||||
|
restrictive while you're coding, ignore it, and let the patch reviewer decide
|
||||||
|
what to do.
|
||||||
|
|
||||||
|
Indentation and whitespace
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Use four-space indents. Braces are on the same line as their associated
|
||||||
|
statements. You should only omit braces if *both* sides of the statement are
|
||||||
|
on one line.
|
||||||
|
|
||||||
|
* One space after the `function` keyword. No space between the function name
|
||||||
|
* in a declaration or a call. One space before the parens in the `if`
|
||||||
|
* statements, or `while`, or `for` loops.
|
||||||
|
|
||||||
|
function foo(a, b) {
|
||||||
|
let bar;
|
||||||
|
|
||||||
|
if (a > b)
|
||||||
|
bar = do_thing(a);
|
||||||
|
else
|
||||||
|
bar = do_thing(b);
|
||||||
|
|
||||||
|
if (var == 5) {
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
print(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Semicolons
|
||||||
|
----------
|
||||||
|
|
||||||
|
JavaScript allows omitting semicolons at the end of lines, but don't. Always
|
||||||
|
end statements with a semicolon.
|
||||||
|
|
||||||
|
js2-mode
|
||||||
|
--------
|
||||||
|
|
||||||
|
If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
|
||||||
|
while. emacs now has a built-in JavaScript mode, js-mode, based on
|
||||||
|
espresso-mode. It is the de facto emacs mode for JavaScript.
|
||||||
|
|
||||||
|
File naming and creation
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
|
||||||
|
|
||||||
|
We only use C where gjs/gobject-introspection is not available for the task, or
|
||||||
|
where C would be cleaner. To work around limitations in
|
||||||
|
gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
|
||||||
|
|
||||||
|
Like many other GNOME projects, we prefix our C source filenames with the
|
||||||
|
library name followed by a dash, e.g. `shell-app-system.c`. Create a
|
||||||
|
`-private.h` header when you want to share code internally in the
|
||||||
|
library. These headers are not installed, distributed or introspected.
|
||||||
|
|
||||||
|
Imports
|
||||||
|
-------
|
||||||
|
|
||||||
|
Use UpperCamelCase when importing modules to distinguish them from ordinary
|
||||||
|
variables, e.g.
|
||||||
|
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
|
||||||
|
Imports should be categorized into one of two places. The top-most import block
|
||||||
|
should contain only "environment imports". These are either modules from
|
||||||
|
gobject-introspection or modules added by gjs itself.
|
||||||
|
|
||||||
|
The second block of imports should contain only "application imports". These
|
||||||
|
are the JS code that is in the gnome-shell codebase,
|
||||||
|
e.g. `imports.ui.popupMenu`.
|
||||||
|
|
||||||
|
Each import block should be sorted alphabetically. Don't import modules you
|
||||||
|
don't use.
|
||||||
|
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
|
The alphabetical ordering should be done independently of the location of the
|
||||||
|
location. Never reference `imports` in actual code.
|
||||||
|
|
||||||
|
Constants
|
||||||
|
---------
|
||||||
|
|
||||||
|
We use CONSTANTS_CASE to define constants. All constants should be directly
|
||||||
|
under the imports:
|
||||||
|
|
||||||
|
const MY_DBUS_INTERFACE = 'org.my.Interface';
|
||||||
|
|
||||||
|
Variable declaration
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Always use either `const` or `let` when defining a variable.
|
||||||
|
|
||||||
|
// Iterating over an array
|
||||||
|
for (let i = 0; i < arr.length; ++i) {
|
||||||
|
let item = arr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterating over an object's properties
|
||||||
|
for (let prop in someobj) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
If you use "var" then the variable is added to function scope, not block scope.
|
||||||
|
See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
|
||||||
|
|
||||||
|
Classes
|
||||||
|
-------
|
||||||
|
|
||||||
|
There are many approaches to classes in JavaScript. We use our own class framework
|
||||||
|
(sigh), which is built in gjs. The advantage is that it supports inheriting from
|
||||||
|
GObjects, although this feature isn't used very often in the Shell itself.
|
||||||
|
|
||||||
|
const IconLabelMenuItem = new Lang.Class({
|
||||||
|
Name: 'IconLabelMenuItem',
|
||||||
|
Extends: PopupMenu.PopupMenuBaseItem,
|
||||||
|
|
||||||
|
_init: function(icon, label) {
|
||||||
|
this.parent({ reactive: false });
|
||||||
|
this.addActor(icon);
|
||||||
|
this.addActor(label);
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function() {
|
||||||
|
log("menu opened!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
* 'Name' is required. 'Extends' is optional. If you leave it out, you will
|
||||||
|
automatically inherit from Object.
|
||||||
|
|
||||||
|
* Leave a blank line between the "class header" (Name, Extends, and other
|
||||||
|
things) and the "class body" (methods). Leave a blank line between each
|
||||||
|
method.
|
||||||
|
|
||||||
|
* No space before the colon, one space after.
|
||||||
|
|
||||||
|
* No trailing comma after the last item.
|
||||||
|
|
||||||
|
* Make sure to use a semicolon after the closing paren to the class. It's
|
||||||
|
still a giant function call, even though it may resemble a more
|
||||||
|
conventional syntax.
|
||||||
|
|
||||||
|
GObject Introspection
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
GObject Introspection is a powerful feature that allows us to have native
|
||||||
|
bindings for almost any library built around GObject. If a library requires
|
||||||
|
you to inherit from a type to use it, you can do so:
|
||||||
|
|
||||||
|
const MyClutterActor = new Lang.Class({
|
||||||
|
Name: 'MyClutterActor',
|
||||||
|
Extends: Clutter.Actor,
|
||||||
|
|
||||||
|
vfunc_get_preferred_width: function(actor, forHeight) {
|
||||||
|
return [100, 100];
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_height: function(actor, forWidth) {
|
||||||
|
return [100, 100];
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_paint: function(actor) {
|
||||||
|
let alloc = this.get_allocation_box();
|
||||||
|
Cogl.set_source_color4ub(255, 0, 0, 255);
|
||||||
|
Cogl.rectangle(alloc.x1, alloc.y1,
|
||||||
|
alloc.x2, alloc.y2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Translatable strings, `environment.js`
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
We use gettext to translate the GNOME Shell into all the languages that GNOME
|
||||||
|
supports. The `gettext` function is aliased globally as `_`, you do not need to
|
||||||
|
explicitly import it. This is done through some magic in the
|
||||||
|
[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
|
||||||
|
file. If you can't find a method that's used, it's probably either in gjs itself
|
||||||
|
or installed on the global object from the Environment.
|
||||||
|
|
||||||
|
Use 'single quotes' for programming strings that should not be translated
|
||||||
|
and "double quotes" for strings that the user may see. This allows us to
|
||||||
|
quickly find untranslated or mistranslated strings by grepping through the
|
||||||
|
sources for double quotes without a gettext call around them.
|
||||||
|
|
||||||
|
`actor` and `_delegate`
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
gjs allows us to set so-called "expando properties" on introspected objects,
|
||||||
|
allowing us to treat them like any other. Because the Shell was built before
|
||||||
|
you could inherit from GTypes natively in JS, we usually have a wrapper class
|
||||||
|
that has a property called `actor`. We call this wrapper class the "delegate".
|
||||||
|
|
||||||
|
We sometimes use expando properties to set a property called `_delegate` on
|
||||||
|
the actor itself:
|
||||||
|
|
||||||
|
const MyClass = new Lang.Class({
|
||||||
|
Name: 'MyClass',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.Button({ text: "This is a button" });
|
||||||
|
this.actor._delegate = this;
|
||||||
|
|
||||||
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClicked: function(actor) {
|
||||||
|
actor.set_label("You clicked the button!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
The 'delegate' property is important for anything which trying to get the
|
||||||
|
delegate object from an associated actor. For instance, the drag and drop
|
||||||
|
system calls the `handleDragOver` function on the delegate of a "drop target"
|
||||||
|
when the user drags an item over it. If you do not set the `_delegate`
|
||||||
|
property, your actor will not be able to be dropped onto.
|
||||||
|
|
||||||
|
Functional style
|
||||||
|
----------------
|
||||||
|
|
||||||
|
JavaScript Array objects offer a lot of common functional programming
|
||||||
|
capabilities such as forEach, map, filter and so on. You can use these when
|
||||||
|
they make sense, but please don't have a spaghetti mess of function programming
|
||||||
|
messed in a procedural style. Use your best judgment.
|
||||||
|
|
||||||
|
Closures
|
||||||
|
--------
|
||||||
|
|
||||||
|
`this` will not be captured in a closure, it is relative to how the closure is
|
||||||
|
invoked, not to the value of this where the closure is created, because "this"
|
||||||
|
is a keyword with a value passed in at function invocation time, it is not a
|
||||||
|
variable that can be captured in closures.
|
||||||
|
|
||||||
|
All closures should be wrapped with a Lang.bind.
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
let closure = Lang.bind(this, function() { this._fnorbate(); });
|
||||||
|
|
||||||
|
A more realistic example would be connecting to a signal on a method of a
|
||||||
|
prototype:
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const FnorbLib = imports.fborbLib;
|
||||||
|
|
||||||
|
const MyClass = new Lang.Class({
|
||||||
|
_init: function() {
|
||||||
|
let fnorb = new FnorbLib.Fnorb();
|
||||||
|
fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onFnorbFrobate: function(fnorb) {
|
||||||
|
this._updateFnorb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object literal syntax
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
In JavaScript, these are equivalent:
|
||||||
|
|
||||||
|
foo = { 'bar': 42 };
|
||||||
|
foo = { bar: 42 };
|
||||||
|
|
||||||
|
and so are these:
|
||||||
|
|
||||||
|
var b = foo['bar'];
|
||||||
|
var b = foo.bar;
|
||||||
|
|
||||||
|
If your usage of an object is like an object, then you're defining "member
|
||||||
|
variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
|
||||||
|
42 }` `foo.bar`.
|
||||||
|
|
||||||
|
If your usage of an object is like a hash table (and thus conceptually the keys
|
||||||
|
can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
|
||||||
|
}`, `foo['bar']`.
|
||||||
|
|
||||||
|
Getters, setters, and Tweener
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Getters and setters should be used when you are dealing with an API that is
|
||||||
|
designed around setting properties, like Tweener. If you want to animate an
|
||||||
|
arbitrary property, create a getter and setter, and use Tweener to animate the
|
||||||
|
property.
|
||||||
|
|
||||||
|
const ANIMATION_TIME = 2000;
|
||||||
|
|
||||||
|
const MyClass = new Lang.Class({
|
||||||
|
Name: 'MyClass',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout();
|
||||||
|
this._position = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
get position() {
|
||||||
|
return this._position;
|
||||||
|
},
|
||||||
|
|
||||||
|
set position(value) {
|
||||||
|
this._position = value;
|
||||||
|
this.actor.set_position(value, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let myThing = new MyClass();
|
||||||
|
Tweener.addTween(myThing,
|
||||||
|
{ position: 100,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
10
Makefile.am
@ -1,7 +1,11 @@
|
|||||||
# Point to our macro directory and pick up user flags from the environment
|
# Point to our macro directory and pick up user flags from the environment
|
||||||
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
||||||
|
|
||||||
SUBDIRS = data js src browser-plugin tests po man docs
|
SUBDIRS = data js src browser-plugin tests po docs
|
||||||
|
|
||||||
|
if ENABLE_MAN
|
||||||
|
SUBDIRS += man
|
||||||
|
endif
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
.project \
|
.project \
|
||||||
@ -12,7 +16,9 @@ EXTRA_DIST = \
|
|||||||
# These are files checked into Git that we don't want to distribute
|
# These are files checked into Git that we don't want to distribute
|
||||||
DIST_EXCLUDE = \
|
DIST_EXCLUDE = \
|
||||||
.gitignore \
|
.gitignore \
|
||||||
|
.gitmodules \
|
||||||
gnome-shell.doap \
|
gnome-shell.doap \
|
||||||
|
HACKING \
|
||||||
MAINTAINERS \
|
MAINTAINERS \
|
||||||
tools/build/*
|
tools/build/*
|
||||||
|
|
||||||
@ -20,4 +26,4 @@ distcheck-hook:
|
|||||||
@echo "Checking disted files against files in git"
|
@echo "Checking disted files against files in git"
|
||||||
@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
|
@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
|
||||||
|
|
||||||
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
|
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-man
|
||||||
|
833
NEWS
@ -1,3 +1,836 @@
|
|||||||
|
3.9.3
|
||||||
|
=====
|
||||||
|
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
|
||||||
|
* Tweak timeout for activating windows during XDND [Adel; #700150]
|
||||||
|
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
|
||||||
|
* Fix DND to empty dash [Florian; #684618]
|
||||||
|
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
|
||||||
|
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
|
||||||
|
* Allow session mode specific overrides schema [Florian; #701717]
|
||||||
|
* window-switcher: Only show windows from current workspace by default
|
||||||
|
[Florian; #701214]
|
||||||
|
* logout dialog: Show the correct text right away [Matthias; #702056]
|
||||||
|
* bluetooth: Port to bluez 5 [Emilio; #700891]
|
||||||
|
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
|
||||||
|
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
|
||||||
|
#700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
|
||||||
|
#701849, #702121]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
|
||||||
|
Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
|
||||||
|
Jonh Wendell
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
|
||||||
|
Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
|
||||||
|
eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
|
||||||
|
Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.9.2
|
||||||
|
=====
|
||||||
|
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
|
||||||
|
* Move paint state cache into StWidget [Jasper; #697274]
|
||||||
|
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
|
||||||
|
* Make calendar keyboard navigable [Tanner; #667434]
|
||||||
|
* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
|
||||||
|
* Update how branding appears on login screen [Florian; #694912, #699877]
|
||||||
|
* Allow OSD popups to grow if necessary [Marta; #696523]
|
||||||
|
* Fix offset of shadow offscreen rendering [Lionel; #698301]
|
||||||
|
* Fix insensitive button preventing empty keyring password [Stef; #696304]
|
||||||
|
* Allow cancelling keyring dialog between prompts [Stef; #682830]
|
||||||
|
* modalDialog: Show spinner while working [Stef; #684438]
|
||||||
|
* overview: Only show close buttons for windows that may close [Jasper; #699269]
|
||||||
|
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
|
||||||
|
* Set input-purpose property for password entries [Rui; #700043]
|
||||||
|
* Provide a DBus API for screencasting [Florian; #696247]
|
||||||
|
* overview: Disable hotcorner during DND [Jasper; #698484]
|
||||||
|
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
|
||||||
|
* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
|
||||||
|
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
|
||||||
|
* Update man page [Matthias; #700339]
|
||||||
|
* Add FocusSearch DBus method [Florian; #700536]
|
||||||
|
* Hide frequent view when app monitoring is disabled [Florian; #699714]
|
||||||
|
* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
|
||||||
|
* gdm: Update the session chooser style [Allan; #695742]
|
||||||
|
* Fix some app folders getting truncated at the top [Florian; #694371]
|
||||||
|
* Don't block the message tray while a notification is showing [Jasper; #700639]
|
||||||
|
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
|
||||||
|
* Re-lock screen when restarted after a crash [Colin; #691987]
|
||||||
|
* Synchronize input source switching with key events [Rui; #697007]
|
||||||
|
* Switch input source on modifiers-only accelerator [Rui; #697008]
|
||||||
|
* Allow input source switching in message tray [Rui; #697009]
|
||||||
|
* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
|
||||||
|
Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
|
||||||
|
#698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
|
||||||
|
#700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
|
||||||
|
#700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
|
||||||
|
#700944, #697661, #700854, #700190, #699189, #701097]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||||
|
Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
|
||||||
|
Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
|
||||||
|
Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
|
||||||
|
Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.9.1
|
||||||
|
=====
|
||||||
|
* Add additional toggle-overview keybinding [Matthias; #698251]
|
||||||
|
* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
|
||||||
|
* Disable tray context menu while a notification displays [Jasper; #695800]
|
||||||
|
* Watch GApplication busy state [Cosimo; #697207]
|
||||||
|
* Disable style transitions if animations are disabled [Jasper; #698391]
|
||||||
|
* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
|
||||||
|
* Fix window previews swapping place randomly [Jasper; #694469, #698776]
|
||||||
|
* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
|
||||||
|
* Fix hotcorner regression in RTL locales [Jasper; #698884]
|
||||||
|
* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
|
||||||
|
* Make open-app-menu keybinding a toggle action [Florian; #686756]
|
||||||
|
* Only recognize common URL schemes in notification messages [Monica; #661225]
|
||||||
|
* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
|
||||||
|
#698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
|
||||||
|
#699075, #696720, #649748]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
|
||||||
|
Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
|
||||||
|
Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
|
||||||
|
Žygimantas Beručka [lt]
|
||||||
|
|
||||||
|
3.8.1
|
||||||
|
=====
|
||||||
|
* Clip window group during startup animation [Jasper; #696323]
|
||||||
|
* Check for logind rather than systemd [Martin; #696252]
|
||||||
|
* Don't special-case last remote search provider position [Giovanni; #694974]
|
||||||
|
* Fix memory leaks [Ray, Jasper; ##697119, #697295, #697300, #697395]
|
||||||
|
* AppSwitcherPopup: Activate only the selected window if any [Rui; #697480]
|
||||||
|
* Enable screen recorder keybinding in all modes [Florian; #696200]
|
||||||
|
* Remove box-shadow from screen shield for performance reasons [Adel; #697274]
|
||||||
|
* Add support for -st-natural-width/height CSS properties [Giovanni; #664411]
|
||||||
|
* Remove excessive padding from notification buttons [Allan; #664411]
|
||||||
|
* Fix thumbnail dragging in overview [Jasper; #697504]
|
||||||
|
* theme-node: Add get_url()/lookup_url() methods [Florian; #693688]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Rui, Colin, David, Ray, Matthias:
|
||||||
|
#695859, #696259, #696585, #696436, #697432, #697435, #697560, #697722,
|
||||||
|
#697709]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah, David Gumberg,
|
||||||
|
Rui Matos, Florian Müllner, Martin Pitt, Jasper St. Pierre, Ray Strode,
|
||||||
|
Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Daniel Martinez [an], Bruce Cowan [en_GB], Khaled Hosny [ar],
|
||||||
|
Ihar Hrachyshka [be], Aron Xu [zh_CN], Wojciech Szczęsny [pl],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Kjartan Maraas [nb],
|
||||||
|
Милош Поповић [sr, sr@latin], Trần Ngọc Quân [vi]
|
||||||
|
|
||||||
|
3.8.0.1
|
||||||
|
=======
|
||||||
|
* Background bug fixes [Ray; #696712]
|
||||||
|
|
||||||
|
3.8.0
|
||||||
|
=====
|
||||||
|
* Remove blur and desaturation from lock screen [Jasper; #696322]
|
||||||
|
* Remove scroll view fade near edges [Adel; #696404]
|
||||||
|
* dateMenu: Open calendar component when using Evolution [Florian; #696432]
|
||||||
|
* Fix unlocking on fast user switch [Cosimo; #696287]
|
||||||
|
* Tweak screen shield animation [Rui; #696380]
|
||||||
|
* Fix major memory leak when changing backgrounds [Ray; #696157]
|
||||||
|
* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
|
||||||
|
#696447, #696235]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
|
||||||
|
Jasper St. Pierre, Ray Strode
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
|
||||||
|
Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
|
||||||
|
Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
|
||||||
|
Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
|
||||||
|
Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
|
||||||
|
Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
|
||||||
|
Mantas Kriaučiūnas [lt], Praveen Illa [te]
|
||||||
|
|
||||||
|
3.7.92
|
||||||
|
======
|
||||||
|
* Drop fallback lock implementation [Florian; #693403]
|
||||||
|
* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
|
||||||
|
* Scroll search results when using keynav [Jasper; #689681]
|
||||||
|
* Allow raising the shield by starting to type the password [Jasper; #686740]
|
||||||
|
* Improve tracking of fullscreen windows [Owen; #649748]
|
||||||
|
* Improve animation of new windows in overview [Giovanni; #695582]
|
||||||
|
* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
|
||||||
|
* Give user time to read messages on login screen [Ray; #694688]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
|
||||||
|
Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
|
||||||
|
#695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
|
||||||
|
#695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
|
||||||
|
#693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
|
||||||
|
#694092, #695747, #696007, #693438, #696064, #696102
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
|
||||||
|
Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
|
||||||
|
Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||||
|
Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
|
||||||
|
Ville-Pekka Vainio [fi], Matej Urbančič [sl],
|
||||||
|
Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
|
||||||
|
Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
|
||||||
|
Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
|
||||||
|
Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
|
||||||
|
|
||||||
|
3.7.91
|
||||||
|
======
|
||||||
|
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
|
||||||
|
* overview: Keep open when a Control key is held [Florian; #686984]
|
||||||
|
* Improve login screen => session transition [Ray; #694321]
|
||||||
|
* Center application grid horizontally [Florian; #694261]
|
||||||
|
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
|
||||||
|
* Tweak thresholds of pressure barrier [Jasper; #694467]
|
||||||
|
* Tweak window picker layout [Jasper; #694902]
|
||||||
|
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
|
||||||
|
* Don't always show message tray in overview, add message indicator
|
||||||
|
[Cosimo; #687787]
|
||||||
|
* Tweak startup animation [Ray; #694326]
|
||||||
|
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
|
||||||
|
* Move loupe icon to the start of the search entry [Jasper; #695069]
|
||||||
|
* Don't show the input switcher with less than 2 items [Rui; 695000]
|
||||||
|
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
|
||||||
|
* Ignore workspaces in alt-tab [Florian; #661156]
|
||||||
|
* Disable copying text from password entries [Florian; #695104]
|
||||||
|
* Use standard styling for ibus candidate popups [Allan; #694796]
|
||||||
|
* Fix calendar changing height on month changes [Giovanni; #641383]
|
||||||
|
* Port the hot corner to use pressure barriers [Jasper; #663661]
|
||||||
|
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
|
||||||
|
Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
|
||||||
|
#694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
|
||||||
|
#663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
|
||||||
|
#694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
|
||||||
|
#694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
|
||||||
|
#695126, #687748, #694837, #693907, #679851, #694988]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
|
||||||
|
Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
|
||||||
|
Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
|
||||||
|
Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
|
||||||
|
Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
|
||||||
|
Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
|
||||||
|
|
||||||
|
3.7.90
|
||||||
|
======
|
||||||
|
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
|
||||||
|
* Implement middle-click paste [Jasper; #645019]
|
||||||
|
* Fix top bar transition between session modes [Rui; #692966]
|
||||||
|
* Trigger the message tray with downward pressure [Jasper; #677215]
|
||||||
|
* Don't ask for a password on shutdown [Adel; #693385]
|
||||||
|
* Add a context menu to the message tray [Adel; #691035, #693887]
|
||||||
|
* Use unicode formatting in the date menu [Matthias; #689251]
|
||||||
|
* Use proper ellipsis instead of three dots [Jeremy; #689542]
|
||||||
|
* Tweak screen shield animation [Giovanni; #691964]
|
||||||
|
* Always hide the OSK when showing the message tray [Florian; #662687]
|
||||||
|
* Support sound in notifications [Giovanni; #642831]
|
||||||
|
* Place application popup menus above chrome [Jasper; #633620]
|
||||||
|
* Hide overview elements while searching [Cosimo; #682050]
|
||||||
|
* Implement updated IBus candidate popup designs [Rui; #691902]
|
||||||
|
* Add support for enable-animations preference [Cosimo; #655746]
|
||||||
|
* Don't always show the message tray in the overview [Cosimo; #693987]
|
||||||
|
* Improve arrangement of window previews [Adel; #690313]
|
||||||
|
* Remove builtin settings provider [Giovanni; #690824]
|
||||||
|
* Minimize fullscreen windows when they end up in the background [Adel; #693991]
|
||||||
|
* Add context menu to the background actor [Jasper; #681540]
|
||||||
|
* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
|
||||||
|
* Hide universal access menu when not needed [Giovanni; #681528]
|
||||||
|
* Implement updated app picker designs [Florian; #694192]
|
||||||
|
* Improve login manager -> session transition [Ray; #694062]
|
||||||
|
* Don't use a grid layout in window picker [Adel; #694210]
|
||||||
|
* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
|
||||||
|
* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
|
||||||
|
Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
|
||||||
|
#692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
|
||||||
|
#688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
|
||||||
|
#681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
|
||||||
|
#693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
|
||||||
|
#693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
|
||||||
|
#694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
|
||||||
|
#694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
|
||||||
|
#694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
|
||||||
|
#694265, #694289, #691806, #694290, #694296]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
|
||||||
|
António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
|
||||||
|
Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
|
||||||
|
Daiki Ueno
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
|
||||||
|
Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
|
||||||
|
Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
|
||||||
|
Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
|
||||||
|
Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
|
||||||
|
Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
|
||||||
|
Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
|
||||||
|
|
||||||
|
3.7.5
|
||||||
|
=====
|
||||||
|
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
|
||||||
|
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
|
||||||
|
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
|
||||||
|
* Make list search results span all available horizontal space [Tanner; #691967]
|
||||||
|
* Make Show-Applications button depress when held down [Hashem; #692319]
|
||||||
|
* Set a max width on search results [Cosimo; #692453]
|
||||||
|
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
|
||||||
|
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
|
||||||
|
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
|
||||||
|
* Stop launching applications from empty searches [Hashem; #692391]
|
||||||
|
* Implement per-source notification filtering [Giovanni; #685926]
|
||||||
|
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
|
||||||
|
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
|
||||||
|
* Make previews of minimized windows translucent in overview [Florian; #692999]
|
||||||
|
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
|
||||||
|
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
|
||||||
|
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
|
||||||
|
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
|
||||||
|
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
|
||||||
|
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
|
||||||
|
* general: Consistently use Title Case in top bar [Jeremy; #689589]
|
||||||
|
* panel: Add :overview pseudo class while in overview [Florian; #693218]
|
||||||
|
* sessionMode: Add support for mode-specific styling [Florian; #693219]
|
||||||
|
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
|
||||||
|
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
|
||||||
|
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
|
||||||
|
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
|
||||||
|
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
|
||||||
|
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
|
||||||
|
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
|
||||||
|
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
|
||||||
|
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
|
||||||
|
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
|
||||||
|
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
|
||||||
|
Milo Casagrande [it], Benjamin Steinwender [de]
|
||||||
|
|
||||||
|
3.7.4.1
|
||||||
|
=======
|
||||||
|
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
|
||||||
|
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
|
||||||
|
* screenshot: Immediately show the flash spot [Jasper; #691875]
|
||||||
|
* Misc. bug fixes [Rico, Jeremy]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
|
||||||
|
Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Ihar Hrachyshka [be]
|
||||||
|
|
||||||
|
3.7.4
|
||||||
|
=====
|
||||||
|
* Make menu separators crisp [Giovanni, Allan; #641745]
|
||||||
|
* power: Update for new D-Bus name [Bastien; #690506]
|
||||||
|
* Add smooth scrolling support [Jasper; #687573]
|
||||||
|
* Tweak notification layout [Allan; #688506]
|
||||||
|
* Ping the active window when using the app menu [Giovanni; #684340]
|
||||||
|
* Make password entries insensitive after submission [Jasper; #690594, #690895]
|
||||||
|
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
|
||||||
|
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
|
||||||
|
* lookingGlass: Move to an inspect() function [Jasper; #690726]
|
||||||
|
* Make OSK animation quicker, snappier [Rui; #688642]
|
||||||
|
* Allow to close chat notifications with Escape [Jasper; #690897]
|
||||||
|
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
|
||||||
|
* Add a SelectArea() DBus method [Cosimo; #687954]
|
||||||
|
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
|
||||||
|
* OSK: Fix extended keys popups [Rui; #674955]
|
||||||
|
* Don't hide or show the keyboard immediately [Rui; #688646]
|
||||||
|
* Improve padding in power menu [Giovanni; #689297]
|
||||||
|
* Add per-window input source switching [Rui; #691414]
|
||||||
|
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
|
||||||
|
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
|
||||||
|
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
|
||||||
|
#685513, #689263, #691553, #691720, #691743, #691750]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
|
||||||
|
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
|
||||||
|
Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
|
||||||
|
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
|
||||||
|
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
|
||||||
|
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
|
||||||
|
Aron Xu [zh_CN], Gheyret Kenji [ug]
|
||||||
|
|
||||||
|
3.7.3
|
||||||
|
=====
|
||||||
|
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
|
||||||
|
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
|
||||||
|
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
|
||||||
|
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
|
||||||
|
* Improve login process indication [Stéphane; #687113]
|
||||||
|
* Omit empty categories in apps view [Stéphane; #687970]
|
||||||
|
* Style panel differently according to mode [Florian; #684573]
|
||||||
|
* Make it possible to hide the user name [Matthias; #688577]
|
||||||
|
* Consolidate and improve chat connection notifications [Giovanni; #687213]
|
||||||
|
* Improve notification scrollbar appearance [Carlos; #688393]
|
||||||
|
* Fade scroll view fade near scrolling edges [Jasper; #689249]
|
||||||
|
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
|
||||||
|
* Don't close message tray after using context menus [Giovanni; #689296]
|
||||||
|
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
|
||||||
|
* Remember state of 'Remember Password' checkbox [Ron; #688039]
|
||||||
|
* Improve timestamp format in chat notifications [Carlos; #680989]
|
||||||
|
* Improve style of missed-messages counter [Carlos; #686472]
|
||||||
|
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
|
||||||
|
* Add window-based Alt-Tab popup [Florian; #688913]
|
||||||
|
* Support external session mode definitions [Florian; #689304]
|
||||||
|
* Support session-mode-specific extensions [Florian; #689305]
|
||||||
|
* Support 'parentMode' property in session modes [Florian; #689308]
|
||||||
|
* Support a new org.gnome.ShellSearchProvider2 DBus interface
|
||||||
|
[Cosimo; #689735, #690009]
|
||||||
|
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
|
||||||
|
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
|
||||||
|
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
|
||||||
|
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
|
||||||
|
* Skip diacritical marks in search terms [Aleksander; #648587]
|
||||||
|
* Expose all engine options in input sources [Giovanni, Rui; #682318]
|
||||||
|
* Add input source switcher popup [Rui; #682315]
|
||||||
|
* Add minimal support for InfiniBand in network menu [Dan; #677150]
|
||||||
|
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
|
||||||
|
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
|
||||||
|
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
|
||||||
|
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
|
||||||
|
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
|
||||||
|
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
|
||||||
|
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
|
||||||
|
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
|
||||||
|
#690241, #690312, #690175, #687955, #650843, #688234, #690427
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
|
||||||
|
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
|
||||||
|
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
|
||||||
|
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
|
||||||
|
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
|
||||||
|
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
|
||||||
|
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
|
||||||
|
Daniel Mustieles [es], Dušan Kazik [sk]
|
||||||
|
|
||||||
|
3.7.2
|
||||||
|
=====
|
||||||
|
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
|
||||||
|
* gdm: Move logo into the panel [Florian; #685852]
|
||||||
|
* Hide notifications when closed button is clicked [Jasper, Florian; #682237]
|
||||||
|
* Tweak screenShield animations [Rui; #686745]
|
||||||
|
* Restore Fittsability of summary items in message tray [Florian; #686474]
|
||||||
|
* Save screencasts as recent item [Ray; #680647]
|
||||||
|
* overview: Resize window captions on content change [Giovanni, Alex; #620874]
|
||||||
|
* App search: Match GenericName too [Matthias; #687121]
|
||||||
|
* runDialog: Better match style of other modal dialogs [Florian, Allan; #687127]
|
||||||
|
* Improve the button insensitive style [Stéphane; #687110]
|
||||||
|
* network: Don't use a global switch for all VPN connections [Giovanni; #682929]
|
||||||
|
* appMenu: Update on icon theme changes [Florian; #687224]
|
||||||
|
* Show 'Log out' in more situations [Matthias; #686736]
|
||||||
|
* Add a setting to force the 'Log out' menuitem [Matthias; #686057]
|
||||||
|
* overview: Improve styling of search box [Stéphane; #686479]
|
||||||
|
* Implement 'disable-user-list' in login screen [Ray; #660660]
|
||||||
|
* Fix auto-scroll to bottom in chat notifications [Sjoerd; #686571]
|
||||||
|
* Show feedback notifications when user is busy [Stéphane; #662900]
|
||||||
|
* Disable login button when there is no input [Stéphane; #687112]
|
||||||
|
* Use non-linear overview shade for background [Giovanni, Pierre-Eric; #669798]
|
||||||
|
* Reduce blocking in compositor thread [Simon, Jasper; #687465]
|
||||||
|
* network: new country-specific type to gather providers [Aleksander; #687356]
|
||||||
|
* Update man page [Matthias; #680601]
|
||||||
|
* st-entry: Change the pointer cursor on enter/leave events [Thomas; #687130]
|
||||||
|
* screenShield: Blur and desaturate the background [Giovanni, Cosimo; #682536]
|
||||||
|
* Change height of chat notifications to have more context [Carlos; #665255]
|
||||||
|
* screenShield: Account for motion velocity when hiding [Giovanni; #682537]
|
||||||
|
* screenShield: hide the cursor while the lock screen is on [Giovanni; #682535]
|
||||||
|
* Support remote search provider settings [Cosimo; #687491]
|
||||||
|
* unlockDialog: Improve label of confirmation button [Stéphane; #687656]
|
||||||
|
* userMenu: Rename "System Settings" item to "Settings" [Elad; #687738]
|
||||||
|
* messageTray: Add keybinding to focus current notification [Stéphane; #652082]
|
||||||
|
* Remove shell-screen-grabber [Neil; #685915]
|
||||||
|
* main: Stop using Metacity's keybinding files [Florian; #687672]
|
||||||
|
* Bluetooth: Remove ObexFTP functionality [Bastien; #688160]
|
||||||
|
* a11y: Also set WM theme when HighContrast is switched on [Cosimo; #688256]
|
||||||
|
* network: Rework multiple NIC support [Giovanni; #677142]
|
||||||
|
* Rework keybindings to allow selective blocking/processing [Florian; #688202]
|
||||||
|
* recorder: Show indicator on primary monitor [Adel; #688470]
|
||||||
|
* recorder: Set frame duration to fix broken video headers [Adel; #688487]
|
||||||
|
* Misc. bugfixes and cleanups [Florian, Jasper, Giovanni, Matthew, Stéphane,
|
||||||
|
Allan, Daiki, Owen, Alejandro, Jean-François, Cosimo, Sebastian, Adel, Alban;
|
||||||
|
#686484, #686728, #686805, #686574, #686763, #682428, #687132, #685239,
|
||||||
|
#687189, #687226, #658091, #670687, #687457, #687242, #687287, #687020,
|
||||||
|
#686583, #661194, #687491, #657315, #687958, #683986, #688089, #687708,
|
||||||
|
#686530, #684810, #688181, #688475, #688557, #688507, #638351]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Elad Alfassa, Matthew Barnes, Alban Browaeys, Giovanni Campagna,
|
||||||
|
Cosimo Cecchi, Matthias Clasen, Allan Day, Stéphane Démurget,
|
||||||
|
Jean-François Fortin Tam, Adel Gadllah, Alex Hultman, Sebastian Keller,
|
||||||
|
Rui Matos, Simon McVittie, Aleksander Morgado, Florian Müllner,
|
||||||
|
Bastien Nocera, Pierre-Eric Pelloux-Prayer, Alejandro Piñeiro, Neil Roberts,
|
||||||
|
Sjoerd Simons, Carlos Soriano Sánchez, Jasper St. Pierre, Ray Strode,
|
||||||
|
Owen Taylor, Daiki Ueno, Thomas Wood
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Dušan Kazik [sk], Pavol Klačanský [sk], Piotr Drąg [pl], Yuri Myasoedov [ru],
|
||||||
|
Marek Černocký [cs], Kjartan Maraas [nb], Wolfgang Stöggl [de],
|
||||||
|
Yaron Shahrabani [he], Fran Diéguez [gl], Mattias Põldaru [et]
|
||||||
|
|
||||||
|
3.7.1
|
||||||
|
=====
|
||||||
|
* Add shortcut to open application view directly [Jeremy; #685738]
|
||||||
|
* Expose '<Super>F10' shortcut in System Settings [Florian; #672909]
|
||||||
|
* Clean up timestamp format in chat notifications [Carlos; #680989]
|
||||||
|
* loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247]
|
||||||
|
* Update textures automatically on file changes [Florian; #679268]
|
||||||
|
* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
|
||||||
|
* Add missing translations for GSetting schema [Giovanni; #686413]
|
||||||
|
* Hide workspace switcher completely when it's not necessary [Seif; #686483]
|
||||||
|
* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
|
||||||
|
* Port to GnomeIdleMonitor [Jasper; #682224]
|
||||||
|
* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
|
||||||
|
* Allow testing GDM login dialog from the session [Giovanni; #683725]
|
||||||
|
* Use all available space for windows in window picker [Jasper, Pierre-Eric;
|
||||||
|
#582650]
|
||||||
|
* Use logind for suspend if available [Florian; #686482]
|
||||||
|
* Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426,
|
||||||
|
#686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy,
|
||||||
|
Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer,
|
||||||
|
Carlos Soriano, Jasper St. Pierre
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be],
|
||||||
|
Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja],
|
||||||
|
Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el]
|
||||||
|
|
||||||
|
3.6.1
|
||||||
|
=====
|
||||||
|
* dash: Make padding even on the top/bottom of the dash [Jasper; #684619]
|
||||||
|
* Fix a crash when dragging search results [Jasper; #684888]
|
||||||
|
* workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641]
|
||||||
|
* Really hide 'Show Keyboard Layout' on the lock screen [Matthias]
|
||||||
|
* Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355]
|
||||||
|
* Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian;
|
||||||
|
#684633, #685914]
|
||||||
|
* Disable hotplug sniffer on remote filesystems [Jasper; #684093]
|
||||||
|
* userMenu: Remove 'Switch Session' item [Florian; #685062]
|
||||||
|
* unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444]
|
||||||
|
* messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342]
|
||||||
|
* Fix crash on dragging windows between workspaces [Ryan; #681399]
|
||||||
|
* userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536]
|
||||||
|
* gdm: Fix key-focus on first user [Adel; #684650]
|
||||||
|
* Make grid button insensitive when dragging non-favorites [Jasper; #685313]
|
||||||
|
* Calendar: hide all actions when on the login screen [Matthias; #685142]
|
||||||
|
* Adapt unlock dialog layout for the login screen [Florian; #685201]
|
||||||
|
* Make focus-follows-mouse work better with Shell UI [Florian; #678169]
|
||||||
|
* Improve look of screen shield [Jasper; #685919]
|
||||||
|
* Fix keynav in the login screen [Florian; #684730]
|
||||||
|
* dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050]
|
||||||
|
* unlockDialog: Reset UI on verification failure [Giovanni; #685441]
|
||||||
|
* Show unlock dialog on primary monitor when using keynav [Giovanni; #685855]
|
||||||
|
* Fix height changes of entries when entering text [Florian; #685534]
|
||||||
|
* Fix show-apps label after successful drags [Florian; #684627]
|
||||||
|
* Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jasper St. Pierre
|
||||||
|
Matthias Clasen
|
||||||
|
Owen Taylor
|
||||||
|
Olivier Blin
|
||||||
|
Florian Müllner
|
||||||
|
Ryan Lortie
|
||||||
|
Adel Gadllah
|
||||||
|
Tanner Doshier
|
||||||
|
Tim Lunn
|
||||||
|
Giovanni Campagna
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da],
|
||||||
|
Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB],
|
||||||
|
Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga],
|
||||||
|
Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt],
|
||||||
|
Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da],
|
||||||
|
Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr],
|
||||||
|
Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be],
|
||||||
|
Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN],
|
||||||
|
Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk],
|
||||||
|
Alexandre Franke [fr], Piotr Drąg [pl]
|
||||||
|
|
||||||
|
3.6.0
|
||||||
|
=====
|
||||||
|
* keyboard: Make input source items accessible [Florian; #684462]
|
||||||
|
* Don't show network dialogs in the lock screen [Giovanni; #684384]
|
||||||
|
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
|
||||||
|
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
|
||||||
|
* Fix alignment of search section headers in RTL locales [Florian; #684379]
|
||||||
|
* screenShield: Fix unlock animation [Florian; #684591]
|
||||||
|
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
|
||||||
|
* userMenu: Fix texture updates on icon changes [Florian; #679268]
|
||||||
|
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
|
||||||
|
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
|
||||||
|
#683546]
|
||||||
|
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
|
||||||
|
Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
|
||||||
|
Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
|
||||||
|
Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
|
||||||
|
Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
|
||||||
|
Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
|
||||||
|
Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
|
||||||
|
Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
|
||||||
|
Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
|
||||||
|
YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
|
||||||
|
Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
|
||||||
|
Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
|
||||||
|
Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
|
||||||
|
|
||||||
|
3.5.92
|
||||||
|
======
|
||||||
|
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
|
||||||
|
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
|
||||||
|
* Add zoom out animation on login [Jasper; #683170]
|
||||||
|
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
|
||||||
|
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
|
||||||
|
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
|
||||||
|
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
|
||||||
|
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
|
||||||
|
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
|
||||||
|
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
|
||||||
|
* Change stage background color to grey [Adel; #683514]
|
||||||
|
* messageTray: Update style of summary counters [Debarshi; #682891]
|
||||||
|
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
|
||||||
|
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
|
||||||
|
* Hide the a11y menu in the lock screen, but show it in the login screen
|
||||||
|
[Giovanni; #682542]
|
||||||
|
* Fix show-apps button dropping off the dash [Florian; #683340]
|
||||||
|
* Fix committing strings to shell entries from input method [Florian; #658325]
|
||||||
|
* Make IBus display strings consistent with control-center [Rui; #683124]
|
||||||
|
* Fix missing short codes for some input sources [Rui; #683613]
|
||||||
|
* Remove support for long-press from entry context menus [Jasper; #683509]
|
||||||
|
* screenShield: Add box-shadow to the shield [Florian]
|
||||||
|
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
|
||||||
|
* Fix extension styling [Giovanni; #682128]
|
||||||
|
* Fix on-screen keyboard not working with system-modal dialogs
|
||||||
|
[Giovanni; #664309]
|
||||||
|
* Fix insensitive styling for popup menu items [Giovanni; #683988]
|
||||||
|
* Disable the message tray dwell when the user is interacting [Owen; #683811]
|
||||||
|
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
|
||||||
|
* Autostart fprintd when necessary [Ray; #683131]
|
||||||
|
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
|
||||||
|
* Make Return key dismiss screenshield [Ray; #683889]
|
||||||
|
* Fix keyboard navigation in the message tray [Florian; #682243]
|
||||||
|
* Remove the places & devices search provider [Giovanni; #683506]
|
||||||
|
* Enable hot corner while the message tray is up [Florian; #682255]
|
||||||
|
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
|
||||||
|
* Fix fish flickering [Giovanni; #684154]
|
||||||
|
* Fix extension ordering with !important [Jasper; #684163]
|
||||||
|
* Allow the shell to run without the screenshield [Giovanni; #683060]
|
||||||
|
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
|
||||||
|
* Improve transition to the login dialog [Jasper; #682428]
|
||||||
|
* Keep unlock dialog around until shield animation ends [Florian; #684342]
|
||||||
|
* Expose shell keybindings in System Settings [Florian; #671010]
|
||||||
|
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
|
||||||
|
#672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
|
||||||
|
#683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
|
||||||
|
#683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
|
||||||
|
#684162, #684214, #684343]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
|
||||||
|
Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
|
||||||
|
Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||||
|
Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
|
||||||
|
Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
|
||||||
|
Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
|
||||||
|
Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
|
||||||
|
Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
|
||||||
|
Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
|
||||||
|
Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
|
||||||
|
Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
|
||||||
|
Fran Diéguez [gl], Praveen Illa [te]
|
||||||
|
|
||||||
|
3.5.91
|
||||||
|
======
|
||||||
|
* Improve modal dialog styling of network secret prompts [Jasper; #682412]
|
||||||
|
* Fix visibility of non-active workspaces during overview transition
|
||||||
|
[Florian; #682002]
|
||||||
|
* Improve scrollbar theming [Cosimo; #682476]
|
||||||
|
* Make sure the app menu remains hidden in locked state [Florian; #682475]
|
||||||
|
* Add tooltip to show-applications icon [Jasper; #682445]
|
||||||
|
* Do not add duplicate remote search providers [Florian; #682470]
|
||||||
|
* Handle 'popup-menu' signal on summary items [Florian; #682486]
|
||||||
|
* Fix dwelling during mouse-down [Owen; #682385]
|
||||||
|
* Set label actor for endSessionDialog.ListItem [Alejandro; #677503]
|
||||||
|
* Don't match on comments when searching applications [Florian; #682529]
|
||||||
|
* Make workspace selector more similar to the mockup [Stefano; #662087]
|
||||||
|
* Fix extension installation and reloading [Jasper; #682578]
|
||||||
|
* Hide removable devices in the lock screen [Giovanni; #681143]
|
||||||
|
* Reset cancellable after hitting Escape on login screen [Alban; #681537]
|
||||||
|
* Fix suspend from the user menu [Giovanni; #682746]
|
||||||
|
* Set label actor for summary items in message tray [Alejandro; #677229]
|
||||||
|
* Set label for the "Show applications" dash button [Alejandro; #682366]
|
||||||
|
* Load extensions as late as possible [Jasper; #682822]
|
||||||
|
* Improve mount operation dialogs [Jon; #682645]
|
||||||
|
* Remove "Connect to ..." item from places search [Florian; #682817]
|
||||||
|
* Don't auto-expand notifications with actions [Giovanni; #682738]
|
||||||
|
* Add a new lock screen menu to combine volume network and power
|
||||||
|
[Giovanni; #682540]
|
||||||
|
* Add support for pre-edit to StIMText [Daiki; #664041]
|
||||||
|
* Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540]
|
||||||
|
* Use monitor geometry for dwelling [Florian; #683044]
|
||||||
|
* Add support for surrounding-text to StIMText [Daiki; #683015]
|
||||||
|
* Improve the placement and style of the "No results" text [Jasper; #683135]
|
||||||
|
* Remove broken network device activation policy [Giovanni; #683136]
|
||||||
|
* Hide power status icon when no battery is present [Tim; #683080]
|
||||||
|
* Ensure summary items are square and have spacing [Debarshi; #682248]
|
||||||
|
* Fix close buttons overlapping screen edge [Debarshi; #682343]
|
||||||
|
* Escape the tray when a legacy icon is clicked [Giovanni; #682244]
|
||||||
|
* Update arrow in the screen shield to match latest mockups [Giovanni; #682285]
|
||||||
|
* Allow lifting the screen shield with the mouse wheel [Giovanni; #683164]
|
||||||
|
* Make sure to show the app menu after unlocking the screen [Jasper; #683154]
|
||||||
|
* Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui;
|
||||||
|
#582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546,
|
||||||
|
#682683, #682710, #682998, #683073, #683137, #683156]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
|
||||||
|
Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner,
|
||||||
|
Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb],
|
||||||
|
Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru],
|
||||||
|
Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el],
|
||||||
|
Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it]
|
||||||
|
|
||||||
|
3.5.90
|
||||||
|
======
|
||||||
|
* Use symbolic icons for workspace switch OSD [Jon; #680738]
|
||||||
|
* Lock screen improvements:
|
||||||
|
- Hide user menu and a11y menu in the screen lock [Giovanni; #681143]
|
||||||
|
- Bump the lock screen slightly when pressing a key [Giovanni; #681143]
|
||||||
|
- Constrain vertical movement of the screen shield [Giovanni; #681143]
|
||||||
|
- Return to lock screen on idle [Giovanni; #682041]
|
||||||
|
- Unlock screen automatically after fast-user switching [Giovanni; #682096]
|
||||||
|
- Fix "other user" label [Ray; #681750]
|
||||||
|
* Constrain content of system modals to primary monitor [#681743]
|
||||||
|
* Respect automatic lock setting on suspend/user-switch [Giovanni; #680231]
|
||||||
|
* Improve styling of keyring prompt [Jasper; #681821]
|
||||||
|
* Do not hard-code <super> as overlay-key [Florian; #665547]
|
||||||
|
* Update style of attached modal dialogs [Florian; #681601]
|
||||||
|
* a11y: allow navigation on non reactive items [Alejandro; #667439, #667439]
|
||||||
|
* Implement mode-less overview design [Joost, Florian; #682109]
|
||||||
|
* Implement message-tray redesign:
|
||||||
|
- Restyle the message tray [Ana, Allan, Florian; #677213, #682342]
|
||||||
|
- Move the desktop upwards when showing the tray [Debarshi; #681392]
|
||||||
|
- Add a close button to notifications [Ana, Jasper; #682253]
|
||||||
|
- Add a keybinding to toggle the tray [Debarshi; #681392]
|
||||||
|
- Make the tray keyboard navigable [Debarshi; #681519]
|
||||||
|
- Add dwelling at the bottom of the screen to open the tray [Owen; #682310]
|
||||||
|
- Don't time out banners when the user is inactive [Marina, Jasper]
|
||||||
|
- Misc fixes and cleanups [Jasper, Marina]
|
||||||
|
* Fix showing "Next Week" on Sundays [Sebastian; #682198]
|
||||||
|
* Delay restoring IM presence until the network comes up [Florian; #677982]
|
||||||
|
* Display enterprise login hint [Ray; #681975]
|
||||||
|
* Ignore unrecognized/irrelevant network devices/connections [Dan; #682364]
|
||||||
|
* Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico;
|
||||||
|
#643687, #682045, #682189]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann,
|
||||||
|
Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro,
|
||||||
|
Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor,
|
||||||
|
Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he],
|
||||||
|
Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa],
|
||||||
|
Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl],
|
||||||
|
Мирослав Николић [sr, sr@latin]
|
||||||
|
|
||||||
|
3.5.5
|
||||||
|
=====
|
||||||
|
* Update style to match mockups [Allan]
|
||||||
|
- improve calendar layout and legibility
|
||||||
|
- update notifications and menus
|
||||||
|
- use a common style for entries
|
||||||
|
- update scrollbars to match GTK+
|
||||||
|
- improve clock/unlock button in lock screen
|
||||||
|
- update polkit dialogs [Jasper]
|
||||||
|
* Fix login dialog growing when selecting different users [Florian; #675076]
|
||||||
|
* Implement screen lock in the shell [Giovanni]
|
||||||
|
- restructure login code to be shared with session unlock [#619955]
|
||||||
|
- add initial screen shield / unlock dialog implementation [#619955]
|
||||||
|
- implement (optional) notification list on lock shield [#619955]
|
||||||
|
- update login dialog style to match lock screen [#619955]
|
||||||
|
- filter notifications to only show new ones on the screen lock [#681143]
|
||||||
|
- make notifications scrollable if necessary [#681143]
|
||||||
|
- use correct application names in notifications [#681143]
|
||||||
|
- allow to return to the shield by pressing Escape [#681143]
|
||||||
|
* Minor login dialog improvements [Florian]
|
||||||
|
- update style to match the overall visuals [#660913]
|
||||||
|
- indicate whether users are logged in [#658185]
|
||||||
|
* Add support for background-repeat CSS property [Jasper; #680801]
|
||||||
|
* Add :active pseudo class on scroll handles [Florian]
|
||||||
|
* Remove markup from translated strings [Matthias; #681270]
|
||||||
|
* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
|
||||||
|
Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
|
||||||
|
#681101, #681382]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||||
|
Piotr Drąg , Florian Müllner, Jasper St. Pierre
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
|
||||||
|
Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
|
||||||
|
Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
|
||||||
|
Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
|
||||||
|
Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
|
||||||
|
|
||||||
3.5.4
|
3.5.4
|
||||||
=====
|
=====
|
||||||
* Fix wrong result handling of remote calls [Florian; #678852]
|
* Fix wrong result handling of remote calls [Florian; #678852]
|
||||||
|
@ -13,6 +13,14 @@ PKG_NAME="gnome-shell"
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Fetch submodules if needed
|
||||||
|
if test ! -f src/gvc/Makefile.am;
|
||||||
|
then
|
||||||
|
echo "+ Setting up submodules"
|
||||||
|
git submodule init
|
||||||
|
fi
|
||||||
|
git submodule update
|
||||||
|
|
||||||
which gnome-autogen.sh || {
|
which gnome-autogen.sh || {
|
||||||
echo "You need to install gnome-common from GNOME Git (or from"
|
echo "You need to install gnome-common from GNOME Git (or from"
|
||||||
echo "your OS vendor's package manager)."
|
echo "your OS vendor's package manager)."
|
||||||
|
@ -153,8 +153,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
|||||||
/* global initialization routine, called once when plugin
|
/* global initialization routine, called once when plugin
|
||||||
is loaded */
|
is loaded */
|
||||||
|
|
||||||
g_type_init ();
|
|
||||||
|
|
||||||
g_debug ("plugin loaded");
|
g_debug ("plugin loaded");
|
||||||
|
|
||||||
memcpy (&funcs, pfuncs, sizeof (funcs));
|
memcpy (&funcs, pfuncs, sizeof (funcs));
|
||||||
|
142
configure.ac
@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
AC_INIT([gnome-shell],[3.5.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
AC_INIT([gnome-shell],[3.9.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||||
@ -16,8 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
|||||||
|
|
||||||
# Checks for programs.
|
# Checks for programs.
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
# Needed for per-target cflags, like in gnomeshell-taskpanel
|
|
||||||
AM_PROG_CC_C_O
|
|
||||||
|
|
||||||
# Initialize libtool
|
# Initialize libtool
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
@ -36,6 +34,8 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
|||||||
|
|
||||||
PKG_PROG_PKG_CONFIG([0.22])
|
PKG_PROG_PKG_CONFIG([0.22])
|
||||||
|
|
||||||
|
AC_PATH_PROG([XSLTPROC], [xsltproc])
|
||||||
|
|
||||||
GLIB_GSETTINGS
|
GLIB_GSETTINGS
|
||||||
|
|
||||||
# Get a value to substitute into gnome-shell.in
|
# Get a value to substitute into gnome-shell.in
|
||||||
@ -52,30 +52,30 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
|
|||||||
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||||
AC_MSG_RESULT(yes)
|
AC_MSG_RESULT(yes)
|
||||||
build_recorder=true
|
build_recorder=true
|
||||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
|
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT(no)
|
AC_MSG_RESULT(no)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||||
|
|
||||||
CLUTTER_MIN_VERSION=1.9.16
|
CLUTTER_MIN_VERSION=1.13.4
|
||||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||||
GJS_MIN_VERSION=1.33.2
|
GJS_MIN_VERSION=1.35.4
|
||||||
MUTTER_MIN_VERSION=3.5.4
|
MUTTER_MIN_VERSION=3.9.3
|
||||||
GTK_MIN_VERSION=3.3.9
|
GTK_MIN_VERSION=3.7.9
|
||||||
GIO_MIN_VERSION=2.31.6
|
GIO_MIN_VERSION=2.37.0
|
||||||
LIBECAL_MIN_VERSION=3.5.3
|
LIBECAL_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
|
||||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
|
||||||
POLKIT_MIN_VERSION=0.100
|
POLKIT_MIN_VERSION=0.100
|
||||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||||
GCR_MIN_VERSION=3.3.90
|
GCR_MIN_VERSION=3.7.5
|
||||||
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
|
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
|
||||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||||
|
NETWORKMANAGER_MIN_VERSION=0.9.8
|
||||||
|
PULSE_MIN_VERS=2.0
|
||||||
|
|
||||||
# Collect more than 20 libraries for a prize!
|
# Collect more than 20 libraries for a prize!
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||||
@ -87,43 +87,29 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
|||||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||||
$recorder_modules
|
$recorder_modules
|
||||||
gdk-x11-3.0 libsoup-2.4
|
gdk-x11-3.0 libsoup-2.4
|
||||||
gl
|
|
||||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||||
libcanberra
|
libcanberra libcanberra-gtk3
|
||||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
|
||||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||||
libnm-glib libnm-util gnome-keyring-1
|
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||||
gcr-3 >= $GCR_MIN_VERSION
|
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||||
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
|
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
|
||||||
|
|
||||||
saved_CFLAGS=$CFLAGS
|
|
||||||
saved_LIBS=$LIBS
|
|
||||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
|
||||||
LIBS=$GNOME_SHELL_LIBS
|
|
||||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
|
||||||
CFLAGS=$saved_CFLAGS
|
|
||||||
LIBS=$saved_LIBS
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
PKG_CHECK_MODULES(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.2 x11)
|
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(TRAY, gtk+-3.0)
|
||||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.1)
|
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])
|
AC_MSG_CHECKING([for bluetooth support])
|
||||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
|
||||||
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
|
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
|
||||||
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
|
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
|
||||||
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
||||||
@ -136,36 +122,14 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
|||||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
AC_SUBST([HAVE_BLUETOOTH],[0])
|
||||||
AC_MSG_RESULT([no])])
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
|
||||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||||
|
|
||||||
AC_ARG_WITH(systemd,
|
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||||
AS_HELP_STRING([--with-systemd],
|
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||||
[Add systemd support]),
|
|
||||||
[with_systemd=$withval], [with_systemd=auto])
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(SYSTEMD,
|
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||||
[libsystemd-login libsystemd-daemon],
|
|
||||||
[have_systemd=yes], [have_systemd=no])
|
|
||||||
|
|
||||||
if test "x$with_systemd" = "xauto" ; then
|
|
||||||
if test x$have_systemd = xno ; then
|
|
||||||
use_systemd=no
|
|
||||||
else
|
|
||||||
use_systemd=yes
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
use_systemd=$with_systemd
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test "x$use_systemd" = "xyes"; then
|
|
||||||
if test "x$have_systemd" = "xno"; then
|
|
||||||
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
|
|
||||||
fi
|
|
||||||
|
|
||||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||||
@ -192,45 +156,22 @@ fi
|
|||||||
|
|
||||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||||
AM_PATH_GLIB_2_0()
|
AM_PATH_GLIB_2_0()
|
||||||
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
|
|
||||||
AC_SUBST(G_IR_SCANNER)
|
|
||||||
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
|
|
||||||
AC_SUBST(G_IR_COMPILER)
|
|
||||||
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
|
|
||||||
AC_SUBST(G_IR_GENERATE)
|
|
||||||
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
|
|
||||||
AC_SUBST(GIRDIR)
|
|
||||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
|
|
||||||
AC_SUBST(TYPELIBDIR)
|
|
||||||
|
|
||||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||||
|
|
||||||
# Stay command-line compatible with the gnome-common configure option. Here
|
AC_ARG_ENABLE(man,
|
||||||
# minimum/yes/maximum are the same, however.
|
[AS_HELP_STRING([--enable-man],
|
||||||
AC_ARG_ENABLE(compile_warnings,
|
[generate man pages [default=yes]])],,
|
||||||
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
|
enable_man=yes)
|
||||||
enable_compile_warnings=error)
|
if test "$enable_man" != no; then
|
||||||
|
AC_PATH_PROG([XSLTPROC], [xsltproc])
|
||||||
changequote(,)dnl
|
if test -z "$XSLTPROC"; then
|
||||||
if test "$enable_compile_warnings" != no ; then
|
AC_MSG_ERROR([xsltproc is required for --enable-man])
|
||||||
if test "x$GCC" = "xyes"; then
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
|
||||||
*) CFLAGS="$CFLAGS -Wall" ;;
|
|
||||||
esac
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
|
|
||||||
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
|
|
||||||
esac
|
|
||||||
if test "$enable_compile_warnings" = error ; then
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
|
||||||
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
changequote([,])dnl
|
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||||
|
|
||||||
|
GNOME_COMPILE_WARNINGS([error])
|
||||||
|
|
||||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||||
@ -251,6 +192,7 @@ AC_CONFIG_FILES([
|
|||||||
js/Makefile
|
js/Makefile
|
||||||
src/calendar-server/evolution-calendar.desktop.in
|
src/calendar-server/evolution-calendar.desktop.in
|
||||||
src/Makefile
|
src/Makefile
|
||||||
|
src/gvc/Makefile
|
||||||
browser-plugin/Makefile
|
browser-plugin/Makefile
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
po/Makefile.in
|
po/Makefile.in
|
||||||
|
12
data/50-gnome-shell-screenshot.xml.in
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||||
|
group="system"
|
||||||
|
_name="Screenshots"
|
||||||
|
wm_name="GNOME Shell"
|
||||||
|
package="gnome-shell">
|
||||||
|
|
||||||
|
<KeyListEntry name="toggle-recording"
|
||||||
|
_description="Record a screencast"/>
|
||||||
|
|
||||||
|
</KeyListEntries>
|
||||||
|
|
24
data/50-gnome-shell-system.xml.in
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||||
|
group="system"
|
||||||
|
_name="System"
|
||||||
|
wm_name="GNOME Shell"
|
||||||
|
package="gnome-shell">
|
||||||
|
|
||||||
|
<KeyListEntry name="toggle-message-tray"
|
||||||
|
_description="Show the message tray"/>
|
||||||
|
|
||||||
|
<KeyListEntry name="focus-active-notification"
|
||||||
|
_description="Focus the active notification"/>
|
||||||
|
|
||||||
|
<KeyListEntry name="toggle-overview"
|
||||||
|
_description="Show the overview"/>
|
||||||
|
|
||||||
|
<KeyListEntry name="toggle-application-view"
|
||||||
|
_description="Show all applications"/>
|
||||||
|
|
||||||
|
<KeyListEntry name="open-application-menu"
|
||||||
|
_description="Open the application menu"/>
|
||||||
|
|
||||||
|
</KeyListEntries>
|
||||||
|
|
@ -1,3 +1,6 @@
|
|||||||
|
wandadir = $(pkgdatadir)
|
||||||
|
dist_wanda_DATA = wanda.png
|
||||||
|
|
||||||
desktopdir=$(datadir)/applications
|
desktopdir=$(datadir)/applications
|
||||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||||
|
|
||||||
@ -8,17 +11,14 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
|||||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||||
$< > $@ || rm $@
|
$< > $@ || rm $@
|
||||||
|
|
||||||
# Placeholder until we add intltool
|
@INTLTOOL_DESKTOP_RULE@
|
||||||
%.desktop:%.desktop.in
|
|
||||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
|
||||||
|
|
||||||
searchprovidersdir = $(pkgdatadir)/open-search-providers
|
|
||||||
dist_searchproviders_DATA = \
|
|
||||||
open-search-providers/google.xml \
|
|
||||||
open-search-providers/wikipedia.xml
|
|
||||||
|
|
||||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||||
introspection_DATA = org.gnome.ShellSearchProvider.xml
|
introspection_DATA = \
|
||||||
|
org.gnome.Shell.Screencast.xml \
|
||||||
|
org.gnome.Shell.Screenshot.xml \
|
||||||
|
org.gnome.ShellSearchProvider.xml \
|
||||||
|
org.gnome.ShellSearchProvider2.xml
|
||||||
|
|
||||||
themedir = $(pkgdatadir)/theme
|
themedir = $(pkgdatadir)/theme
|
||||||
dist_theme_DATA = \
|
dist_theme_DATA = \
|
||||||
@ -36,22 +36,31 @@ dist_theme_DATA = \
|
|||||||
theme/dash-placeholder.svg \
|
theme/dash-placeholder.svg \
|
||||||
theme/filter-selected-ltr.svg \
|
theme/filter-selected-ltr.svg \
|
||||||
theme/filter-selected-rtl.svg \
|
theme/filter-selected-rtl.svg \
|
||||||
theme/gdm.css \
|
|
||||||
theme/gnome-shell.css \
|
theme/gnome-shell.css \
|
||||||
|
theme/logged-in-indicator.svg \
|
||||||
|
theme/message-tray-background.png \
|
||||||
|
theme/more-results.svg \
|
||||||
|
theme/noise-texture.png \
|
||||||
theme/panel-button-border.svg \
|
theme/panel-button-border.svg \
|
||||||
theme/panel-button-highlight-narrow.svg \
|
theme/panel-button-highlight-narrow.svg \
|
||||||
theme/panel-button-highlight-wide.svg \
|
theme/panel-button-highlight-wide.svg \
|
||||||
theme/process-working.svg \
|
theme/process-working.svg \
|
||||||
theme/running-indicator.svg \
|
theme/running-indicator.svg \
|
||||||
theme/scroll-hhandle.svg \
|
|
||||||
theme/scroll-vhandle.svg \
|
|
||||||
theme/source-button-border.svg \
|
theme/source-button-border.svg \
|
||||||
|
theme/summary-counter.svg \
|
||||||
theme/toggle-off-us.svg \
|
theme/toggle-off-us.svg \
|
||||||
theme/toggle-off-intl.svg \
|
theme/toggle-off-intl.svg \
|
||||||
theme/toggle-on-us.svg \
|
theme/toggle-on-us.svg \
|
||||||
theme/toggle-on-intl.svg \
|
theme/toggle-on-intl.svg \
|
||||||
theme/ws-switch-arrow-up.svg \
|
theme/ws-switch-arrow-up.png \
|
||||||
theme/ws-switch-arrow-down.svg
|
theme/ws-switch-arrow-down.png
|
||||||
|
|
||||||
|
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||||
|
keys_in_files = \
|
||||||
|
50-gnome-shell-screenshot.xml.in \
|
||||||
|
50-gnome-shell-system.xml.in \
|
||||||
|
$(NULL)
|
||||||
|
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||||
|
|
||||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||||
|
|
||||||
@ -79,12 +88,14 @@ EXTRA_DIST = \
|
|||||||
$(introspection_DATA) \
|
$(introspection_DATA) \
|
||||||
$(menu_DATA) \
|
$(menu_DATA) \
|
||||||
$(convert_DATA) \
|
$(convert_DATA) \
|
||||||
|
$(keys_in_files) \
|
||||||
org.gnome.shell.gschema.xml.in.in
|
org.gnome.shell.gschema.xml.in.in
|
||||||
|
|
||||||
CLEANFILES = \
|
CLEANFILES = \
|
||||||
gnome-shell.desktop.in \
|
gnome-shell.desktop.in \
|
||||||
gnome-shell-extension-prefs.in \
|
gnome-shell-extension-prefs.in \
|
||||||
$(desktop_DATA) \
|
$(desktop_DATA) \
|
||||||
|
$(keys_DATA) \
|
||||||
$(gsettings_SCHEMAS) \
|
$(gsettings_SCHEMAS) \
|
||||||
gschemas.compiled \
|
gschemas.compiled \
|
||||||
org.gnome.shell.gschema.valid \
|
org.gnome.shell.gschema.valid \
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
|
||||||
<ShortName>Google</ShortName>
|
|
||||||
<Description>Google Search</Description>
|
|
||||||
<InputEncoding>UTF-8</InputEncoding>
|
|
||||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
|
|
||||||
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
|
|
||||||
</OpenSearchDescription>
|
|
@ -1,44 +0,0 @@
|
|||||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
|
||||||
<ShortName>Wikipedia</ShortName>
|
|
||||||
<Description>Wikipedia, the free encyclopedia</Description>
|
|
||||||
<InputEncoding>UTF-8</InputEncoding>
|
|
||||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
|
|
||||||
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
|
|
||||||
<!-- The criterion for being below is being listed with more than 100,000
|
|
||||||
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
|
|
||||||
<Language>ar</Language>
|
|
||||||
<Language>bg</Language>
|
|
||||||
<Language>ca</Language>
|
|
||||||
<Language>cs</Language>
|
|
||||||
<Language>da</Language>
|
|
||||||
<Language>de</Language>
|
|
||||||
<Language>en</Language>
|
|
||||||
<Language>eo</Language>
|
|
||||||
<Language>es</Language>
|
|
||||||
<Language>fa</Language>
|
|
||||||
<Language>fi</Language>
|
|
||||||
<Language>fr</Language>
|
|
||||||
<Language>he</Language>
|
|
||||||
<Language>hu</Language>
|
|
||||||
<Language>id</Language>
|
|
||||||
<Language>it</Language>
|
|
||||||
<Language>ja</Language>
|
|
||||||
<Language>ko</Language>
|
|
||||||
<Language>lt</Language>
|
|
||||||
<Language>nl</Language>
|
|
||||||
<Language>no</Language>
|
|
||||||
<Language>pl</Language>
|
|
||||||
<Language>pt</Language>
|
|
||||||
<Language>ro</Language>
|
|
||||||
<Language>ru</Language>
|
|
||||||
<Language>sk</Language>
|
|
||||||
<Language>sl</Language>
|
|
||||||
<Language>sr</Language>
|
|
||||||
<Language>sv</Language>
|
|
||||||
<Language>tr</Language>
|
|
||||||
<Language>uk</Language>
|
|
||||||
<Language>vi</Language>
|
|
||||||
<Language>vo</Language>
|
|
||||||
<Language>war</Language>
|
|
||||||
<Language>zh</Language>
|
|
||||||
</OpenSearchDescription>
|
|
96
data/org.gnome.Shell.Screencast.xml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.Screencast:
|
||||||
|
@short_description: Screencast interface
|
||||||
|
|
||||||
|
The interface used to record screen contents.
|
||||||
|
-->
|
||||||
|
<interface name="org.gnome.Shell.Screencast">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Screencast:
|
||||||
|
@file_template: the template for the filename to use
|
||||||
|
@options: a dictionary of optional parameters
|
||||||
|
@success: whether the screencast was started successfully
|
||||||
|
@filename_used: the file where the screencast is being saved
|
||||||
|
|
||||||
|
Records a screencast of the whole screen and saves it
|
||||||
|
(by default) as webm video under a filename derived from
|
||||||
|
@file_template. The template is either a relative or absolute
|
||||||
|
filename which may contain some escape sequences - %d and %t
|
||||||
|
will be replaced by the start date and time of the recording.
|
||||||
|
If a relative name is used, the screencast will be saved in the
|
||||||
|
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
|
||||||
|
The actual filename of the saved video is returned in @filename_used.
|
||||||
|
The set of optional parameters in @options currently consists of:
|
||||||
|
'draw-cursor'(b): whether the cursor should be included in the
|
||||||
|
recording (true)
|
||||||
|
'framerate'(i): the number of frames per second that should be
|
||||||
|
recorded if possible (30)
|
||||||
|
'pipeline'(s): the GStreamer pipeline used to encode recordings
|
||||||
|
in gst-launch format; if not specified, the
|
||||||
|
recorder will produce vp8 (webm) video (unset)
|
||||||
|
-->
|
||||||
|
<method name="Screencast">
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreencastArea:
|
||||||
|
@x: the X coordinate of the area to capture
|
||||||
|
@y: the Y coordinate of the area to capture
|
||||||
|
@width: the width of the area to capture
|
||||||
|
@height: the height of the area to capture
|
||||||
|
@file_template: the template for the filename to use
|
||||||
|
@options: a dictionary of optional parameters
|
||||||
|
@success: whether the screencast was started successfully
|
||||||
|
@filename_used: the file where the screencast is being saved
|
||||||
|
|
||||||
|
Records a screencast of the passed in area and saves it
|
||||||
|
(by default) as webm video under a filename derived from
|
||||||
|
@file_template. The template is either a relative or absolute
|
||||||
|
filename which may contain some escape sequences - %d and %t
|
||||||
|
will be replaced by the start date and time of the recording.
|
||||||
|
If a relative name is used, the screencast will be saved in the
|
||||||
|
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
|
||||||
|
The actual filename of the saved video is returned in @filename_used.
|
||||||
|
The set of optional parameters in @options currently consists of:
|
||||||
|
'draw-cursor'(b): whether the cursor should be included in the
|
||||||
|
recording (true)
|
||||||
|
'framerate'(i): the number of frames per second that should be
|
||||||
|
recorded if possible (30)
|
||||||
|
'pipeline'(s): the GStreamer pipeline used to encode recordings
|
||||||
|
in gst-launch format; if not specified, the
|
||||||
|
recorder will produce vp8 (webm) video (unset)
|
||||||
|
-->
|
||||||
|
<method name="ScreencastArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
StopScreencast:
|
||||||
|
@success: whether stopping the recording was successful
|
||||||
|
|
||||||
|
Stop the recording started by either Screencast or ScreencastArea.
|
||||||
|
-->
|
||||||
|
<method name="StopScreencast">
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
128
data/org.gnome.Shell.Screenshot.xml
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.Screenshot:
|
||||||
|
@short_description: Screenshot interface
|
||||||
|
|
||||||
|
The interface used to capture pictures of the screen contents.
|
||||||
|
-->
|
||||||
|
<interface name="org.gnome.Shell.Screenshot">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Screenshot:
|
||||||
|
@filename: The filename for the screenshot
|
||||||
|
@include_cursor: Whether to include the cursor image or not
|
||||||
|
@flash: Whether to flash the screen or not
|
||||||
|
@success: whether the screenshot was captured
|
||||||
|
@filename_used: the file where the screenshot was saved
|
||||||
|
|
||||||
|
Takes a screenshot of the whole screen and saves it
|
||||||
|
in @filename as png image, it returns a boolean
|
||||||
|
indicating whether the operation was successful or not.
|
||||||
|
@filename can either be an absolute path or a basename, in
|
||||||
|
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||||
|
or the home directory if it doesn't exist. The filename used
|
||||||
|
to save the screenshot will be returned in @filename_used.
|
||||||
|
-->
|
||||||
|
<method name="Screenshot">
|
||||||
|
<arg type="b" direction="in" name="include_cursor"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="s" direction="in" name="filename"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreenshotWindow:
|
||||||
|
@include_frame: Whether to include the frame or not
|
||||||
|
@include_cursor: Whether to include the cursor image or not
|
||||||
|
@flash: Whether to flash the window area or not
|
||||||
|
@filename: The filename for the screenshot
|
||||||
|
@success: whether the screenshot was captured
|
||||||
|
@filename_used: the file where the screenshot was saved
|
||||||
|
|
||||||
|
Takes a screenshot of the focused window (optionally omitting the frame)
|
||||||
|
and saves it in @filename as png image, it returns a boolean
|
||||||
|
indicating whether the operation was successful or not.
|
||||||
|
@filename can either be an absolute path or a basename, in
|
||||||
|
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||||
|
or the home directory if it doesn't exist. The filename used
|
||||||
|
to save the screenshot will be returned in @filename_used.
|
||||||
|
-->
|
||||||
|
<method name="ScreenshotWindow">
|
||||||
|
<arg type="b" direction="in" name="include_frame"/>
|
||||||
|
<arg type="b" direction="in" name="include_cursor"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="s" direction="in" name="filename"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreenshotArea:
|
||||||
|
@x: the X coordinate of the area to capture
|
||||||
|
@y: the Y coordinate of the area to capture
|
||||||
|
@width: the width of the area to capture
|
||||||
|
@height: the height of the area to capture
|
||||||
|
@flash: whether to flash the area or not
|
||||||
|
@filename: the filename for the screenshot
|
||||||
|
@success: whether the screenshot was captured
|
||||||
|
@filename_used: the file where the screenshot was saved
|
||||||
|
|
||||||
|
Takes a screenshot of the passed in area and saves it
|
||||||
|
in @filename as png image, it returns a boolean
|
||||||
|
indicating whether the operation was successful or not.
|
||||||
|
@filename can either be an absolute path or a basename, in
|
||||||
|
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||||
|
or the home directory if it doesn't exist. The filename used
|
||||||
|
to save the screenshot will be returned in @filename_used.
|
||||||
|
-->
|
||||||
|
<method name="ScreenshotArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="s" direction="in" name="filename"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FlashArea:
|
||||||
|
@x: the X coordinate of the area to flash
|
||||||
|
@y: the Y coordinate of the area to flash
|
||||||
|
@width: the width of the area to flash
|
||||||
|
@height: the height of the area to flash
|
||||||
|
|
||||||
|
Renders a flash spot effect in the specified rectangle of the screen.
|
||||||
|
-->
|
||||||
|
<method name="FlashArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SelectArea:
|
||||||
|
@x: the X coordinate of the selected area
|
||||||
|
@y: the Y coordinate of the selected area
|
||||||
|
@width: the width of the selected area
|
||||||
|
@height: the height of the selected area
|
||||||
|
|
||||||
|
Interactively allows the user to select a rectangular area of
|
||||||
|
the screen, and returns its coordinates.
|
||||||
|
-->
|
||||||
|
<method name="SelectArea">
|
||||||
|
<arg type="i" direction="out" name="x"/>
|
||||||
|
<arg type="i" direction="out" name="y"/>
|
||||||
|
<arg type="i" direction="out" name="width"/>
|
||||||
|
<arg type="i" direction="out" name="height"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
@ -2,146 +2,72 @@
|
|||||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
<node>
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.SearchProvider:
|
||||||
|
@short_description: Search provider interface
|
||||||
|
|
||||||
|
The interface used for integrating into GNOME Shell's search
|
||||||
|
interface. This interface is deprecated, and org.gnome.Shell.SearchProvider2 should be used instead.
|
||||||
|
-->
|
||||||
<interface name="org.gnome.Shell.SearchProvider">
|
<interface name="org.gnome.Shell.SearchProvider">
|
||||||
<doc:doc>
|
|
||||||
<doc:description>
|
|
||||||
<doc:para>
|
|
||||||
The interface used for integrating into GNOME Shell's search
|
|
||||||
interface.
|
|
||||||
</doc:para>
|
|
||||||
</doc:description>
|
|
||||||
</doc:doc>
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetInitialResultSet:
|
||||||
|
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||||
|
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||||
|
|
||||||
|
Called when the user first begins a search.
|
||||||
|
-->
|
||||||
<method name="GetInitialResultSet">
|
<method name="GetInitialResultSet">
|
||||||
<doc:doc>
|
<arg type="as" name="terms" direction="in" />
|
||||||
<doc:description>
|
<arg type="as" name="results" direction="out" />
|
||||||
<doc:para>
|
|
||||||
Called when the user first begins a search.
|
|
||||||
</doc:para>
|
|
||||||
</doc:description>
|
|
||||||
</doc:doc>
|
|
||||||
<arg type="as" direction="in">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
Array of search terms, which the provider should treat as
|
|
||||||
logical AND.
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
<arg type="as" direction="out">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
An array of result identifier strings representing items which
|
|
||||||
match the given search terms. Identifiers must be unique within
|
|
||||||
the provider's domain, but other than that may be chosen freely
|
|
||||||
by the provider.
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetSubsearchResultSet:
|
||||||
|
@previous_results: Array of results previously returned by GetInitialResultSet().
|
||||||
|
@terms: Array of updated search terms, which the provider should treat as logical AND.
|
||||||
|
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||||
|
|
||||||
|
Called when a search is performed which is a "subsearch" of
|
||||||
|
the previous search, e.g. the method may return less results, but
|
||||||
|
not more or different results.
|
||||||
|
|
||||||
|
This allows search providers to only search through the previous
|
||||||
|
result set, rather than possibly performing a full re-query.
|
||||||
|
-->
|
||||||
<method name="GetSubsearchResultSet">
|
<method name="GetSubsearchResultSet">
|
||||||
<doc:doc>
|
<arg type="as" name="previous_results" direction="in" />
|
||||||
<doc:description>
|
<arg type="as" name="terms" direction="in" />
|
||||||
<doc:para>
|
<arg type="as" name="results" direction="out" />
|
||||||
Called when a search is performed which is a "subsearch" of
|
|
||||||
the previous search, e.g. the method may return less results, but
|
|
||||||
not more or different results.
|
|
||||||
|
|
||||||
This allows search providers to only search through the previous
|
|
||||||
result set, rather than possibly performing a full re-query.
|
|
||||||
</doc:para>
|
|
||||||
</doc:description>
|
|
||||||
</doc:doc>
|
|
||||||
<arg type="as" direction="in">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
Array of item identifiers
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
<arg type="as" direction="in">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
Array of updated search terms, which the provider should treat as
|
|
||||||
logical AND.
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
<arg type="as" direction="out">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
An array of result identifier strings representing items which
|
|
||||||
match the given search terms. Identifiers must be unique within
|
|
||||||
the provider's domain, but other than that may be chosen freely
|
|
||||||
by the provider.
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetResultMetas:
|
||||||
|
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
|
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||||
|
|
||||||
|
Return an array of meta data used to display each given result
|
||||||
|
-->
|
||||||
<method name="GetResultMetas">
|
<method name="GetResultMetas">
|
||||||
<doc:doc>
|
<arg type="as" name="identifiers" direction="in" />
|
||||||
<doc:description>
|
<arg type="aa{sv}" name="metas" direction="out" />
|
||||||
<doc:para>
|
|
||||||
Return an array of meta data used to display each given result
|
|
||||||
</doc:para>
|
|
||||||
</doc:description>
|
|
||||||
</doc:doc>
|
|
||||||
<arg type="as" direction="in">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
An array of result identifiers as returned by
|
|
||||||
GetInitialResultSet() or GetSubsearchResultSet()
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
<arg type="a{sv}" direction="out">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
A dictionary describing the given search result, containing
|
|
||||||
'id', 'name' (both strings) and either 'icon' (a serialized
|
|
||||||
GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
|
|
||||||
height, rowstride, has-alpha, bits per sample, channels, data)
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ActivateResult:
|
||||||
|
@identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
|
|
||||||
|
Called when the users chooses a given result. The result should
|
||||||
|
be displayed in the application associated with the corresponding
|
||||||
|
provider.
|
||||||
|
|
||||||
|
This method is deprecated, and providers should implement ActivateResult2()
|
||||||
|
instead.
|
||||||
|
-->
|
||||||
<method name="ActivateResult">
|
<method name="ActivateResult">
|
||||||
<doc:doc>
|
<arg type="s" name="identifier" direction="in" />
|
||||||
<doc:description>
|
|
||||||
<doc:para>
|
|
||||||
Called when the users chooses a given result. The result should
|
|
||||||
be displayed in the application associated with the corresponding
|
|
||||||
provider.
|
|
||||||
</doc:para>
|
|
||||||
</doc:description>
|
|
||||||
</doc:doc>
|
|
||||||
<arg type="s" direction="in">
|
|
||||||
<doc:doc>
|
|
||||||
<doc:summary>
|
|
||||||
<doc:para>
|
|
||||||
A result identifier as returned by GetInitialResultSet() or
|
|
||||||
GetSubsearchResultSet()
|
|
||||||
</doc:para>
|
|
||||||
</doc:summary>
|
|
||||||
</doc:doc>
|
|
||||||
</arg>
|
|
||||||
</method>
|
</method>
|
||||||
</interface>
|
</interface>
|
||||||
</node>
|
</node>
|
||||||
|
87
data/org.gnome.ShellSearchProvider2.xml
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.SearchProvider2:
|
||||||
|
@short_description: Search provider interface
|
||||||
|
|
||||||
|
The interface used for integrating into GNOME Shell's search
|
||||||
|
interface (version 2).
|
||||||
|
-->
|
||||||
|
<interface name="org.gnome.Shell.SearchProvider2">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetInitialResultSet:
|
||||||
|
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||||
|
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||||
|
|
||||||
|
Called when the user first begins a search.
|
||||||
|
-->
|
||||||
|
<method name="GetInitialResultSet">
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="as" name="results" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetSubsearchResultSet:
|
||||||
|
@previous_results: Array of results previously returned by GetInitialResultSet().
|
||||||
|
@terms: Array of updated search terms, which the provider should treat as logical AND.
|
||||||
|
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||||
|
|
||||||
|
Called when a search is performed which is a "subsearch" of
|
||||||
|
the previous search, e.g. the method may return less results, but
|
||||||
|
not more or different results.
|
||||||
|
|
||||||
|
This allows search providers to only search through the previous
|
||||||
|
result set, rather than possibly performing a full re-query.
|
||||||
|
-->
|
||||||
|
<method name="GetSubsearchResultSet">
|
||||||
|
<arg type="as" name="previous_results" direction="in" />
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="as" name="results" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetResultMetas:
|
||||||
|
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
|
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||||
|
|
||||||
|
Return an array of meta data used to display each given result
|
||||||
|
-->
|
||||||
|
<method name="GetResultMetas">
|
||||||
|
<arg type="as" name="identifiers" direction="in" />
|
||||||
|
<arg type="aa{sv}" name="metas" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ActivateResult:
|
||||||
|
@identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
|
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||||
|
@timestamp: A timestamp of the user interaction that triggered this call
|
||||||
|
|
||||||
|
Called when the users chooses a given result. The result should
|
||||||
|
be displayed in the application associated with the corresponding
|
||||||
|
provider. The provided search terms can be used to allow launching a full search in
|
||||||
|
the application.
|
||||||
|
-->
|
||||||
|
<method name="ActivateResult">
|
||||||
|
<arg type="s" name="identifier" direction="in" />
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="u" name="timestamp" direction="in" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
LaunchSearch:
|
||||||
|
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||||
|
@timestamp: A timestamp of the user interaction that triggered this call
|
||||||
|
|
||||||
|
Asks the search provider to launch a full search in the application for the provided terms.
|
||||||
|
-->
|
||||||
|
<method name="LaunchSearch">
|
||||||
|
<arg type="as" name="terms" direction="in" />
|
||||||
|
<arg type="u" name="timestamp" direction="in" />
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
</node>
|
@ -21,16 +21,6 @@
|
|||||||
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
|
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="enable-app-monitoring" type="b">
|
|
||||||
<default>true</default>
|
|
||||||
<_summary>Whether to collect stats about applications usage</_summary>
|
|
||||||
<_description>
|
|
||||||
The shell normally monitors active applications in order to present
|
|
||||||
the most used ones (e.g. in launchers). While this data will be
|
|
||||||
kept private, you may want to disable this for privacy reasons.
|
|
||||||
Please note that doing so won't remove already saved data.
|
|
||||||
</_description>
|
|
||||||
</key>
|
|
||||||
<key name="favorite-apps" type="as">
|
<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', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
|
||||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||||
@ -39,9 +29,13 @@
|
|||||||
will be displayed in the favorites area.
|
will be displayed in the favorites area.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="disabled-open-search-providers" type="as">
|
<key name="app-folder-categories" type="as">
|
||||||
<default>[]</default>
|
<default>[ 'Utilities', 'Sundry' ]</default>
|
||||||
<_summary>disabled OpenSearch providers</_summary>
|
<_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>
|
||||||
<key name="command-history" type="as">
|
<key name="command-history" type="as">
|
||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
@ -61,6 +55,24 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
|
|||||||
<_summary>Internally used to store the last session presence status for the user. The
|
<_summary>Internally used to store the last session presence status for the user. The
|
||||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="always-show-log-out" type="b">
|
||||||
|
<default>false</default>
|
||||||
|
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
|
||||||
|
<_description>
|
||||||
|
This key overrides the automatic hiding of the 'Log out'
|
||||||
|
menuitem in single-user, single-session situations.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key name="remember-mount-password" type="b">
|
||||||
|
<default>false</default>
|
||||||
|
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
||||||
|
<_description>
|
||||||
|
The shell will request a password when an encrypted device or a
|
||||||
|
remote filesystem is mounted. If the password can be saved for
|
||||||
|
future use a 'Remember Password' checkbox will be present.
|
||||||
|
This key sets the default state of the checkbox.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||||
@ -87,6 +99,35 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
Keybinding to open the application menu.
|
Keybinding to open the application menu.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="toggle-application-view" type="as">
|
||||||
|
<default>["<Super>a"]</default>
|
||||||
|
<_summary>Keybinding to open the "Show Applications" view</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to open the "Show Applications" view of the Activities
|
||||||
|
Overview.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key name="toggle-overview" type="as">
|
||||||
|
<default>["<Super>s"]</default>
|
||||||
|
<_summary>Keybinding to open the overview</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to open the Activities Overview.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key name="toggle-message-tray" type="as">
|
||||||
|
<default>["<Super>m"]</default>
|
||||||
|
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to toggle the visibility of the message tray.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key name="focus-active-notification" type="as">
|
||||||
|
<default>["<Super>n"]</default>
|
||||||
|
<_summary>Keybinding to focus the active notification</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to focus the active notification.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
<key name="toggle-recording" type="as">
|
<key name="toggle-recording" type="as">
|
||||||
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
|
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
|
||||||
<_summary>Keybinding to toggle the screen recorder</_summary>
|
<_summary>Keybinding to toggle the screen recorder</_summary>
|
||||||
@ -129,7 +170,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
take care of its own output - this might be used to send the output
|
take care of its own output - this might be used to send the output
|
||||||
to an icecast server via shout2send or similar. When unset or set
|
to an icecast server via shout2send or similar. When unset or set
|
||||||
to an empty value, the default pipeline will be used. This is currently
|
to an empty value, the default pipeline will be used. This is currently
|
||||||
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
|
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
||||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||||
for a guess at the optimal thread count on the system.
|
for a guess at the optimal thread count on the system.
|
||||||
</_description>
|
</_description>
|
||||||
@ -145,44 +186,80 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/">
|
<enum id="org.gnome.shell.window-switcher.AppIconMode">
|
||||||
|
<value value="1" nick="thumbnail-only"/>
|
||||||
|
<value value="2" nick="app-icon-only"/>
|
||||||
|
<value value="3" nick="both"/>
|
||||||
|
</enum>
|
||||||
|
<schema id="org.gnome.shell.window-switcher"
|
||||||
|
path="/org/gnome/shell/window-switcher/"
|
||||||
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
|
<key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode">
|
||||||
|
<default>'both'</default>
|
||||||
|
<_summary>The application icon mode.</_summary>
|
||||||
|
<_description>
|
||||||
|
Configures how the windows are shown in the switcher. Valid possibilities
|
||||||
|
are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only'
|
||||||
|
(shows only the application icon) or 'both'.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key type="b" name="current-workspace-only">
|
||||||
|
<default>true</default>
|
||||||
|
<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>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
|
||||||
|
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
|
||||||
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
<key name="attach-modal-dialogs" type="b">
|
<key name="attach-modal-dialogs" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Attach modal dialog to the parent window</summary>
|
<_summary>Attach modal dialog to the parent window</_summary>
|
||||||
<description>
|
<_description>
|
||||||
This key overrides the key in org.gnome.mutter when running
|
This key overrides the key in org.gnome.mutter when running
|
||||||
GNOME Shell.
|
GNOME Shell.
|
||||||
</description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key name="button-layout" type="s">
|
<key name="button-layout" type="s">
|
||||||
<default>":close"</default>
|
<default>":close"</default>
|
||||||
<summary>Arrangement of buttons on the titlebar</summary>
|
<_summary>Arrangement of buttons on the titlebar</_summary>
|
||||||
<description>
|
<_description>
|
||||||
This key overrides the key in org.gnome.desktop.wm.preferences when
|
This key overrides the key in org.gnome.desktop.wm.preferences when
|
||||||
running GNOME Shell.
|
running GNOME Shell.
|
||||||
</description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key name="edge-tiling" type="b">
|
<key name="edge-tiling" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Enable edge tiling when dropping windows on screen edges</summary>
|
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
|
||||||
<description>
|
<_description>
|
||||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
</description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key name="dynamic-workspaces" type="b">
|
<key name="dynamic-workspaces" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Workspaces are managed dynamically</summary>
|
<_summary>Workspaces are managed dynamically</_summary>
|
||||||
<description>
|
<_description>
|
||||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
</description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key name="workspaces-only-on-primary" type="b">
|
<key name="workspaces-only-on-primary" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Workspaces only on primary monitor</summary>
|
<_summary>Workspaces only on primary monitor</_summary>
|
||||||
|
<_description>
|
||||||
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key name="focus-change-on-pointer-rest" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||||
<description>
|
<description>
|
||||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
</description>
|
</description>
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="28"
|
width="29"
|
||||||
height="25"
|
height="29"
|
||||||
id="svg10621"
|
id="svg10621"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.48.1 r9760"
|
inkscape:version="0.48.2 r9819"
|
||||||
sodipodi:docname="calendar-today.svg">
|
sodipodi:docname="calendar-today.svg">
|
||||||
<defs
|
<defs
|
||||||
id="defs10623">
|
id="defs10623">
|
||||||
@ -118,6 +118,17 @@
|
|||||||
fx="51"
|
fx="51"
|
||||||
fy="30"
|
fy="30"
|
||||||
r="42" />
|
r="42" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34508-1-3"
|
||||||
|
id="radialGradient3113"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||||
|
cx="51"
|
||||||
|
cy="30"
|
||||||
|
fx="51"
|
||||||
|
fy="30"
|
||||||
|
r="42" />
|
||||||
</defs>
|
</defs>
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
id="base"
|
id="base"
|
||||||
@ -127,20 +138,29 @@
|
|||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="15.839192"
|
inkscape:zoom="15.839192"
|
||||||
inkscape:cx="8.3750933"
|
inkscape:cx="20.652108"
|
||||||
inkscape:cy="8.0837211"
|
inkscape:cy="11.839084"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="true"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:window-width="1440"
|
inkscape:window-width="1280"
|
||||||
inkscape:window-height="843"
|
inkscape:window-height="741"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="26"
|
inkscape:window-y="27"
|
||||||
inkscape:window-maximized="1" />
|
inkscape:window-maximized="1"
|
||||||
|
borderlayer="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid3109"
|
||||||
|
empspacing="5"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata10626">
|
id="metadata10626">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
@ -157,31 +177,28 @@
|
|||||||
inkscape:label="Layer 1"
|
inkscape:label="Layer 1"
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
id="layer1"
|
id="layer1"
|
||||||
transform="translate(-469.08263,-536.99307)">
|
transform="translate(-469.08263,-532.99307)">
|
||||||
<g
|
<path
|
||||||
id="g3003">
|
sodipodi:type="arc"
|
||||||
<path
|
style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
inkscape:export-ydpi="90"
|
id="path34506-3"
|
||||||
inkscape:export-xdpi="90"
|
sodipodi:cx="51"
|
||||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
sodipodi:cy="30"
|
||||||
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
|
sodipodi:rx="42"
|
||||||
sodipodi:end="6.2831853"
|
sodipodi:ry="16"
|
||||||
sodipodi:start="3.1415927"
|
d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z"
|
||||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
sodipodi:start="3.1415927"
|
||||||
sodipodi:ry="16"
|
sodipodi:end="6.2831853"
|
||||||
sodipodi:rx="42"
|
transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)"
|
||||||
sodipodi:cy="30"
|
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||||
sodipodi:cx="51"
|
inkscape:export-xdpi="90"
|
||||||
id="path34506-3"
|
inkscape:export-ydpi="90" />
|
||||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
<rect
|
||||||
sodipodi:type="arc" />
|
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
<rect
|
id="rect2996"
|
||||||
y="558.85046"
|
width="31"
|
||||||
x="468.96878"
|
height="3"
|
||||||
height="3.1425927"
|
x="468.08264"
|
||||||
width="28.149134"
|
y="558.99304" />
|
||||||
id="rect2996"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
|
||||||
</g>
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.1 KiB |
@ -1,188 +0,0 @@
|
|||||||
/* Copyright 2011, Red Hat, Inc.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU Lesser General Public License,
|
|
||||||
* version 2.1, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Login Dialog */
|
|
||||||
|
|
||||||
.login-dialog-banner {
|
|
||||||
font-size: 10pt;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
color: #666666;
|
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-title {
|
|
||||||
font-size: 14pt;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #666666;
|
|
||||||
padding-bottom: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog {
|
|
||||||
border-radius: 16px;
|
|
||||||
min-height: 150px;
|
|
||||||
max-height: 700px;
|
|
||||||
min-width: 350px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-prompt-fingerprint-message {
|
|
||||||
font-size: 10.5pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-view {
|
|
||||||
-st-vfade-offset: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list {
|
|
||||||
spacing: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item {
|
|
||||||
color: #666666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item:ltr {
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item:rtl {
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
|
||||||
font-size: 20pt;
|
|
||||||
padding-left: 1em;
|
|
||||||
color: #666666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
|
|
||||||
color: white;
|
|
||||||
text-shadow: black 0px 2px 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item-vertical-layout {
|
|
||||||
spacing: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
|
|
||||||
background-color: rgba(0,0,0,0.0);
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
|
|
||||||
background-color: #666666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-user-list-item-icon {
|
|
||||||
border: 2px solid #8b8b8b;
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-not-listed-button {
|
|
||||||
padding-top: 2em;
|
|
||||||
}
|
|
||||||
.login-dialog-not-listed-label {
|
|
||||||
font-size: 14pt;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #666666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-prompt-layout {
|
|
||||||
padding-bottom: 32px;
|
|
||||||
}
|
|
||||||
.login-dialog-prompt-label {
|
|
||||||
color: white;
|
|
||||||
font-size: 20pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-prompt-entry {
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 2px solid #5656cc;
|
|
||||||
color: black;
|
|
||||||
background-color: white;
|
|
||||||
caret-color: black;
|
|
||||||
caret-size: 1px;
|
|
||||||
width: 15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-prompt-entry .capslock-warning {
|
|
||||||
icon-size: 16px;
|
|
||||||
warning-color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-prompt-entry:insensitive {
|
|
||||||
color: rgba(0,0,0,0.7);
|
|
||||||
border: 2px solid #565656;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list {
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 10.5pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-button {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-button:focus {
|
|
||||||
background-color: #4c4c4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-button:active {
|
|
||||||
background-color: #4c4c4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-button:hover {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-scroll-view {
|
|
||||||
background-gradient-start: rgba(80,80,80,0.3);
|
|
||||||
background-gradient-end: rgba(80,80,80,0.7);
|
|
||||||
background-gradient-direction: vertical;
|
|
||||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid rgba(80,80,80,1.0);
|
|
||||||
padding: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-item:focus {
|
|
||||||
background-color: #666666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-triangle {
|
|
||||||
padding-right: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-item-box {
|
|
||||||
spacing: .25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-item-dot {
|
|
||||||
width: .75em;
|
|
||||||
height: .75em;
|
|
||||||
}
|
|
130
data/theme/logged-in-indicator.svg
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="300"
|
||||||
|
height="80"
|
||||||
|
id="svg7355"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.2 r9819"
|
||||||
|
sodipodi:docname="logged-in-indicator.svg">
|
||||||
|
<metadata
|
||||||
|
id="metadata4175">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#2c1cff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="1"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1440"
|
||||||
|
inkscape:window-height="843"
|
||||||
|
id="namedview4173"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.8760889"
|
||||||
|
inkscape:cx="106.00403"
|
||||||
|
inkscape:cy="80.68078"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g30864" />
|
||||||
|
<defs
|
||||||
|
id="defs7357">
|
||||||
|
<radialGradient
|
||||||
|
xlink:href="#linearGradient36429"
|
||||||
|
id="radialGradient7461"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(2.5919312,0,0,0.57582113,-20.687059,48.400487)"
|
||||||
|
cx="47.428951"
|
||||||
|
cy="167.16817"
|
||||||
|
fx="47.428951"
|
||||||
|
fy="167.16817"
|
||||||
|
r="37" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient36429">
|
||||||
|
<stop
|
||||||
|
id="stop36431"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
id="stop36433"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
xlink:href="#linearGradient36471"
|
||||||
|
id="radialGradient7463"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
|
||||||
|
cx="49.067139"
|
||||||
|
cy="242.50381"
|
||||||
|
fx="49.067139"
|
||||||
|
fy="242.50381"
|
||||||
|
r="37.00671" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient36471">
|
||||||
|
<stop
|
||||||
|
id="stop36473"
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
id="stop36475"
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
r="37.00671"
|
||||||
|
fy="242.50381"
|
||||||
|
fx="49.067139"
|
||||||
|
cy="242.50381"
|
||||||
|
cx="49.067139"
|
||||||
|
gradientTransform="matrix(3.4218418,0,0,0.03365337,-61.309005,138.5071)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient7488"
|
||||||
|
xlink:href="#linearGradient36471" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
id="g30864"
|
||||||
|
transform="translate(255.223,70.118091)">
|
||||||
|
<rect
|
||||||
|
ry="3.4593496"
|
||||||
|
rx="8.8641119"
|
||||||
|
y="76.159348"
|
||||||
|
x="12.596948"
|
||||||
|
height="71.116341"
|
||||||
|
width="182.22595"
|
||||||
|
id="rect14000"
|
||||||
|
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
|
||||||
|
<path
|
||||||
|
id="rect34520"
|
||||||
|
d="m 194.80022,146.83551 -182.559919,0"
|
||||||
|
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:0.61184424;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
connector-curvature="0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
BIN
data/theme/message-tray-background.png
Normal file
After Width: | Height: | Size: 25 KiB |
109
data/theme/more-results.svg
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
id="svg12430"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.3.1 r9886"
|
||||||
|
sodipodi:docname="more-results.svg">
|
||||||
|
<defs
|
||||||
|
id="defs12432" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#7a7a7a"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="1"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="8.3155237"
|
||||||
|
inkscape:cy="0.89548874"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g14642-3-0"
|
||||||
|
showgrid="false"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1376"
|
||||||
|
inkscape:window-x="1200"
|
||||||
|
inkscape:window-y="187"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid13002" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata12435">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1036.3622)">
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
transform="translate(-141.99984,638.37113)"
|
||||||
|
inkscape:label="zoom-in"
|
||||||
|
id="g14642-3-0">
|
||||||
|
<path
|
||||||
|
sodipodi:type="inkscape:offset"
|
||||||
|
inkscape:radius="0"
|
||||||
|
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
|
||||||
|
xlink:href="#rect11749-5-0-1-8"
|
||||||
|
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
|
||||||
|
id="path13004"
|
||||||
|
inkscape:href="#rect11749-5-0-1-8"
|
||||||
|
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
|
||||||
|
transform="translate(0,1)" />
|
||||||
|
<use
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#path13004"
|
||||||
|
id="use11960"
|
||||||
|
transform="translate(1,-1)"
|
||||||
|
width="16"
|
||||||
|
height="16" />
|
||||||
|
<use
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#use11960"
|
||||||
|
id="use11962"
|
||||||
|
transform="translate(-2,0)"
|
||||||
|
width="16"
|
||||||
|
height="16" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||||
|
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
|
||||||
|
id="rect11749-5-0-1-8" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:none"
|
||||||
|
id="rect3620-5-4"
|
||||||
|
width="15.981825"
|
||||||
|
height="16"
|
||||||
|
x="142"
|
||||||
|
y="398"
|
||||||
|
rx="0"
|
||||||
|
ry="0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
BIN
data/theme/noise-texture.png
Normal file
After Width: | Height: | Size: 78 KiB |
@ -1,64 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="10"
|
|
||||||
height="4"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47 r22583"
|
|
||||||
sodipodi:docname="scroll-hhandle.svg">
|
|
||||||
<defs
|
|
||||||
id="defs4">
|
|
||||||
</defs>
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
|
||||||
id="rect3592"
|
|
||||||
width="2"
|
|
||||||
height="4"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#rect3592"
|
|
||||||
id="use2825"
|
|
||||||
transform="translate(8,0)"
|
|
||||||
width="10"
|
|
||||||
height="4" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#use2825"
|
|
||||||
id="use2827"
|
|
||||||
transform="translate(-4,0)"
|
|
||||||
width="10"
|
|
||||||
height="4" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,62 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="4"
|
|
||||||
height="10"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47 r22583"
|
|
||||||
sodipodi:docname="scroll-hhandle.svg">
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
|
||||||
id="rect3592"
|
|
||||||
width="2"
|
|
||||||
height="4"
|
|
||||||
x="0"
|
|
||||||
y="-4"
|
|
||||||
rx="0"
|
|
||||||
ry="0"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#rect3592"
|
|
||||||
id="use3705"
|
|
||||||
transform="translate(0,4)"
|
|
||||||
width="4"
|
|
||||||
height="10" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#use3705"
|
|
||||||
id="use3707"
|
|
||||||
transform="translate(0,4)"
|
|
||||||
width="4"
|
|
||||||
height="10" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
120
data/theme/summary-counter.svg
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
id="Foreground"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 23.272727 23.272727"
|
||||||
|
enable-background="new 0 0 16 16"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.48.2 r9819"
|
||||||
|
sodipodi:docname="summary-counter.svg"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||||
|
id="metadata2399"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs2397"><linearGradient
|
||||||
|
id="linearGradient3173"><stop
|
||||||
|
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3175" /><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3177" /></linearGradient><inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 8 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="16 : 8 : 1"
|
||||||
|
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||||
|
id="perspective2401" /><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter16494-4"
|
||||||
|
x="-0.20989846"
|
||||||
|
width="1.4197969"
|
||||||
|
y="-0.20903821"
|
||||||
|
height="1.4180764"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.3282637"
|
||||||
|
id="feGaussianBlur16496-8" /></filter><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient16498-6"
|
||||||
|
id="radialGradient16504-1"
|
||||||
|
cx="7.6582627"
|
||||||
|
cy="5.8191104"
|
||||||
|
fx="7.6582627"
|
||||||
|
fy="5.8191104"
|
||||||
|
r="8.6928644"
|
||||||
|
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient16498-6"><stop
|
||||||
|
style="stop-color:#9FD0FF;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop16500-8" /><stop
|
||||||
|
style="stop-color:#3465A4;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
inkscape:window-height="709"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
guidetolerance="10.0"
|
||||||
|
gridtolerance="10.0"
|
||||||
|
objecttolerance="10.0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#000000"
|
||||||
|
id="base"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="11.313708"
|
||||||
|
inkscape:cx="15.386407"
|
||||||
|
inkscape:cy="13.739577"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="1179"
|
||||||
|
inkscape:current-layer="g16402-8"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:window-maximized="1"><inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11246"
|
||||||
|
empspacing="5"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
|
||||||
|
style="display:inline"
|
||||||
|
id="g16402-8"
|
||||||
|
transform="translate(4.7533483,2.8238929)"><g
|
||||||
|
id="g3175-4"
|
||||||
|
transform="translate(-0.89995416,0.94028614)"><path
|
||||||
|
sodipodi:type="inkscape:offset"
|
||||||
|
inkscape:radius="0"
|
||||||
|
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
|
||||||
|
xlink:href="#path2394-32"
|
||||||
|
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
|
||||||
|
id="path16480-5"
|
||||||
|
inkscape:href="#path2394-32"
|
||||||
|
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
|
||||||
|
transform="translate(0,1.028519)" /><path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
|
||||||
|
id="path2394-32"
|
||||||
|
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
sodipodi:nodetypes="csssc"
|
||||||
|
inkscape:connector-curvature="0" /><g
|
||||||
|
id="g3172-6" /></g></g></svg>
|
After Width: | Height: | Size: 5.4 KiB |
BIN
data/theme/ws-switch-arrow-down.png
Normal file
After Width: | Height: | Size: 850 B |
@ -1,376 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
version="1.1"
|
|
||||||
width="96"
|
|
||||||
height="96"
|
|
||||||
id="svg25070"
|
|
||||||
inkscape:version="0.48.0 r9654"
|
|
||||||
sodipodi:docname="ws-switch-arrow-down.svg">
|
|
||||||
<metadata
|
|
||||||
id="metadata3353">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="718"
|
|
||||||
inkscape:window-height="480"
|
|
||||||
id="namedview3351"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="2.6979167"
|
|
||||||
inkscape:cx="48"
|
|
||||||
inkscape:cy="48"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg25070" />
|
|
||||||
<defs
|
|
||||||
id="defs25072">
|
|
||||||
<linearGradient
|
|
||||||
x1="-86.552246"
|
|
||||||
y1="185.439"
|
|
||||||
x2="-83.37072"
|
|
||||||
y2="197.31261"
|
|
||||||
id="linearGradient24957"
|
|
||||||
xlink:href="#linearGradient4034-0-4"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(6,0)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4034-0-4">
|
|
||||||
<stop
|
|
||||||
id="stop4036-5-7"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4038-9-6"
|
|
||||||
style="stop-color:#babdb6;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter24765">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix24767" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix24769" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
x1="-74.520325"
|
|
||||||
y1="169.06032"
|
|
||||||
x2="-74.520325"
|
|
||||||
y2="205.94189"
|
|
||||||
id="linearGradient24955"
|
|
||||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-5,0)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4632-1-3-9-3-2">
|
|
||||||
<stop
|
|
||||||
id="stop4634-1-8-3-9-0"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4636-1-9-9-8-8"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0.0274937" />
|
|
||||||
<stop
|
|
||||||
id="stop4638-8-3-9-6-6"
|
|
||||||
style="stop-color:#f2f2f2;stop-opacity:1"
|
|
||||||
offset="0.274937" />
|
|
||||||
<stop
|
|
||||||
id="stop4640-8-5-7-8-9"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0.38707438" />
|
|
||||||
<stop
|
|
||||||
id="stop4642-5-41-9-6-9"
|
|
||||||
style="stop-color:#d9dad8;stop-opacity:1"
|
|
||||||
offset="0.66528589" />
|
|
||||||
<stop
|
|
||||||
id="stop4644-5-2-7-9-2"
|
|
||||||
style="stop-color:#dfe0dd;stop-opacity:1"
|
|
||||||
offset="0.76745707" />
|
|
||||||
<stop
|
|
||||||
id="stop4646-3-2-3-7-3"
|
|
||||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
r="2.3554697"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
id="radialGradient24959"
|
|
||||||
xlink:href="#linearGradient4869-4-1"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-1">
|
|
||||||
<stop
|
|
||||||
id="stop4871-6-2"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-4"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0.31807542" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-1"
|
|
||||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
|
||||||
offset="0.74691135" />
|
|
||||||
<stop
|
|
||||||
id="stop4873-1-0"
|
|
||||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25011">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25013" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25015" />
|
|
||||||
</filter>
|
|
||||||
<radialGradient
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
r="2.3554697"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
id="radialGradient24961"
|
|
||||||
xlink:href="#linearGradient4869-4-0"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-0">
|
|
||||||
<stop
|
|
||||||
id="stop4871-6-8"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-5"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0.31807542" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-5"
|
|
||||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
|
||||||
offset="0.74691135" />
|
|
||||||
<stop
|
|
||||||
id="stop4873-1-4"
|
|
||||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25023">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25025" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25027" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898"
|
|
||||||
id="linearGradient24963"
|
|
||||||
xlink:href="#linearGradient4941"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4941">
|
|
||||||
<stop
|
|
||||||
id="stop4943"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4945"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25033">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25035" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25037" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898"
|
|
||||||
id="linearGradient24965"
|
|
||||||
xlink:href="#linearGradient4941-7"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4941-7">
|
|
||||||
<stop
|
|
||||||
id="stop4943-2"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4945-5"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25043">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25045" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25047" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25049">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25051" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25053" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25055">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25057" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25059" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
|
|
||||||
id="layer1">
|
|
||||||
<g
|
|
||||||
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
|
|
||||||
id="g4030-1-8"
|
|
||||||
style="stroke:#000000;stroke-opacity:1;display:inline">
|
|
||||||
<path
|
|
||||||
d="m -72.5,173.5 -14,14 14,14"
|
|
||||||
id="path3165-7-3"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
|
|
||||||
id="path4050-2-7-9-4"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
|
|
||||||
id="path4050-2-7-9-4-8"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
|
|
||||||
id="path4050-2-7-9-4-0"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
|
|
||||||
id="path4050-2-7-9-4-0-9"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
|
|
||||||
id="path3165-7-3-1"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
|
|
||||||
id="path3165-7-3-1-9"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 13 KiB |
BIN
data/theme/ws-switch-arrow-up.png
Normal file
After Width: | Height: | Size: 841 B |
@ -1,447 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="96"
|
|
||||||
height="96"
|
|
||||||
id="svg25070"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.48.0 r9654"
|
|
||||||
sodipodi:docname="ws-switch-arrow-up.svg">
|
|
||||||
<defs
|
|
||||||
id="defs25072">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 24 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="48 : 24 : 1"
|
|
||||||
inkscape:persp3d-origin="24 : 16 : 1"
|
|
||||||
id="perspective25078" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective24985"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4034-0-4"
|
|
||||||
id="linearGradient24957"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(6)"
|
|
||||||
x1="-86.552246"
|
|
||||||
y1="185.439"
|
|
||||||
x2="-83.37072"
|
|
||||||
y2="197.31261" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4034-0-4">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4036-5-7" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4038-9-6" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter24765"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix24767"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix24769"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
|
||||||
id="linearGradient24955"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-5)"
|
|
||||||
x1="-74.520325"
|
|
||||||
y1="169.06032"
|
|
||||||
x2="-74.520325"
|
|
||||||
y2="205.94189" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4632-1-3-9-3-2">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4634-1-8-3-9-0" />
|
|
||||||
<stop
|
|
||||||
id="stop4636-1-9-9-8-8"
|
|
||||||
offset="0.0274937"
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4638-8-3-9-6-6"
|
|
||||||
offset="0.274937"
|
|
||||||
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4640-8-5-7-8-9"
|
|
||||||
offset="0.38707438"
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4642-5-41-9-6-9"
|
|
||||||
offset="0.66528589"
|
|
||||||
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4644-5-2-7-9-2"
|
|
||||||
offset="0.76745707"
|
|
||||||
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4646-3-2-3-7-3" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4869-4-1"
|
|
||||||
id="radialGradient24959"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
r="2.3554697" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-1">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4871-6-2" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-4"
|
|
||||||
offset="0.31807542"
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-1"
|
|
||||||
offset="0.74691135"
|
|
||||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4873-1-0" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25011"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25013"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25015"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4869-4-0"
|
|
||||||
id="radialGradient24961"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
r="2.3554697" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-0">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4871-6-8" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-5"
|
|
||||||
offset="0.31807542"
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-5"
|
|
||||||
offset="0.74691135"
|
|
||||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4873-1-4" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25023"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25025"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25027"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4941"
|
|
||||||
id="linearGradient24963"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4941">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4943" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4945" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25033"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25035"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25037"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4941-7"
|
|
||||||
id="linearGradient24965"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4941-7">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4943-2" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4945-5" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25043"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25045"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25047"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
id="filter25049"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25051"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25053"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
id="filter25055"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25057"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25059"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="2.8284271"
|
|
||||||
inkscape:cx="-12.356322"
|
|
||||||
inkscape:cy="57.536221"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1200"
|
|
||||||
inkscape:window-height="840"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="0" />
|
|
||||||
<metadata
|
|
||||||
id="metadata25075">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0, 48)">
|
|
||||||
<g
|
|
||||||
id="g3181"
|
|
||||||
transform="matrix(0,1,-1,0,48.0003,-48)">
|
|
||||||
<g
|
|
||||||
style="stroke:#000000;stroke-opacity:1;display:inline"
|
|
||||||
transform="matrix(2,0,0,2,193.25,-374.967)"
|
|
||||||
id="g4030-1-8">
|
|
||||||
<path
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
d="m -72.5,173.5 -14,14 14,14"
|
|
||||||
id="path3165-7-3"
|
|
||||||
sodipodi:nodetypes="ccc"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4-8"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4-0"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4-0-9"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
|
||||||
sodipodi:nodetypes="ccccscccsc"
|
|
||||||
id="path3165-7-3-1"
|
|
||||||
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
|
||||||
sodipodi:nodetypes="ccccccc"
|
|
||||||
id="path3165-7-3-1-9"
|
|
||||||
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 16 KiB |
BIN
data/wanda.png
Normal file
After Width: | Height: | Size: 13 KiB |
@ -76,9 +76,30 @@ endif
|
|||||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
||||||
HTML_IMAGES=
|
HTML_IMAGES=
|
||||||
|
|
||||||
|
doc-gen-org.gnome.Shell.SearchProvider.xml: $(top_srcdir)/data/org.gnome.ShellSearchProvider.xml
|
||||||
|
gdbus-codegen \
|
||||||
|
--interface-prefix org.gnome.ShellSearchProvider. \
|
||||||
|
--generate-docbook doc-gen \
|
||||||
|
$(top_srcdir)/data/org.gnome.ShellSearchProvider.xml
|
||||||
|
|
||||||
|
doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||||
|
gdbus-codegen \
|
||||||
|
--interface-prefix org.gnome.ShellSearchProvider2. \
|
||||||
|
--generate-docbook doc-gen \
|
||||||
|
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||||
|
|
||||||
|
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||||
|
gdbus-codegen \
|
||||||
|
--interface-prefix org.gnome.Shell.Screenshot. \
|
||||||
|
--generate-docbook doc-gen \
|
||||||
|
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||||
|
|
||||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||||
content_files=
|
content_files= \
|
||||||
|
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
||||||
|
doc-gen-org.gnome.Shell.SearchProvider2.xml \
|
||||||
|
doc-gen-org.gnome.Shell.Screenshot.xml
|
||||||
|
|
||||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||||
# These files must be listed here *and* in content_files
|
# These files must be listed here *and* in content_files
|
||||||
|
@ -29,8 +29,6 @@
|
|||||||
<chapter>
|
<chapter>
|
||||||
<title>Search</title>
|
<title>Search</title>
|
||||||
<xi:include href="xml/shell-app-system.xml"/>
|
<xi:include href="xml/shell-app-system.xml"/>
|
||||||
<xi:include href="xml/shell-contact-system.xml"/>
|
|
||||||
<xi:include href="xml/shell-doc-system.xml"/>
|
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter>
|
<chapter>
|
||||||
<title>Tray Icons</title>
|
<title>Tray Icons</title>
|
||||||
@ -42,16 +40,17 @@
|
|||||||
<chapter>
|
<chapter>
|
||||||
<title>Recorder</title>
|
<title>Recorder</title>
|
||||||
<xi:include href="xml/shell-recorder.xml"/>
|
<xi:include href="xml/shell-recorder.xml"/>
|
||||||
<xi:include href="xml/shell-recorder-src.xml"/>
|
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter>
|
<chapter>
|
||||||
<title>Integration helpers and utilities</title>
|
<title>Integration helpers and utilities</title>
|
||||||
|
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
|
||||||
|
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
|
||||||
<xi:include href="xml/shell-global.xml"/>
|
<xi:include href="xml/shell-global.xml"/>
|
||||||
|
<xi:include href="xml/shell-keybinding-modes.xml"/>
|
||||||
<xi:include href="xml/shell-wm.xml"/>
|
<xi:include href="xml/shell-wm.xml"/>
|
||||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
||||||
<xi:include href="xml/shell-util.xml"/>
|
<xi:include href="xml/shell-util.xml"/>
|
||||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||||
<xi:include href="xml/shell-mobile-providers.xml"/>
|
|
||||||
<xi:include href="xml/shell-network-agent.xml"/>
|
<xi:include href="xml/shell-network-agent.xml"/>
|
||||||
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
||||||
<xi:include href="xml/shell-tp-client.xml"/>
|
<xi:include href="xml/shell-tp-client.xml"/>
|
||||||
|
@ -52,13 +52,6 @@ its dependencies to build from tarballs.</description>
|
|||||||
<gnome:userid>walters</gnome:userid>
|
<gnome:userid>walters</gnome:userid>
|
||||||
</foaf:Person>
|
</foaf:Person>
|
||||||
</maintainer>
|
</maintainer>
|
||||||
<maintainer>
|
|
||||||
<foaf:Person>
|
|
||||||
<foaf:name>Dan Winship</foaf:name>
|
|
||||||
<foaf:mbox rdf:resource="mailto:danw@gnome.org" />
|
|
||||||
<gnome:userid>danw</gnome:userid>
|
|
||||||
</foaf:Person>
|
|
||||||
</maintainer>
|
|
||||||
<maintainer>
|
<maintainer>
|
||||||
<foaf:Person>
|
<foaf:Person>
|
||||||
<foaf:name>Marina Zhurakhinskaya</foaf:name>
|
<foaf:name>Marina Zhurakhinskaya</foaf:name>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
NULL =
|
||||||
|
|
||||||
EXTRA_DIST = misc/config.js.in
|
EXTRA_DIST = misc/config.js.in
|
||||||
CLEANFILES = misc/config.js
|
CLEANFILES = misc/config.js
|
||||||
@ -17,28 +18,30 @@ jsdir = $(pkgdatadir)/js
|
|||||||
|
|
||||||
nobase_dist_js_DATA = \
|
nobase_dist_js_DATA = \
|
||||||
gdm/batch.js \
|
gdm/batch.js \
|
||||||
gdm/consoleKit.js \
|
|
||||||
gdm/fingerprint.js \
|
gdm/fingerprint.js \
|
||||||
gdm/loginDialog.js \
|
gdm/loginDialog.js \
|
||||||
gdm/powerMenu.js \
|
gdm/powerMenu.js \
|
||||||
gdm/systemd.js \
|
gdm/realmd.js \
|
||||||
|
gdm/util.js \
|
||||||
extensionPrefs/main.js \
|
extensionPrefs/main.js \
|
||||||
misc/config.js \
|
misc/config.js \
|
||||||
misc/extensionUtils.js \
|
misc/extensionUtils.js \
|
||||||
misc/fileUtils.js \
|
misc/fileUtils.js \
|
||||||
misc/gnomeSession.js \
|
misc/gnomeSession.js \
|
||||||
|
misc/hash.js \
|
||||||
misc/history.js \
|
misc/history.js \
|
||||||
misc/jsParse.js \
|
misc/jsParse.js \
|
||||||
|
misc/loginManager.js \
|
||||||
misc/modemManager.js \
|
misc/modemManager.js \
|
||||||
misc/params.js \
|
misc/params.js \
|
||||||
misc/screenSaver.js \
|
|
||||||
misc/util.js \
|
misc/util.js \
|
||||||
perf/core.js \
|
perf/core.js \
|
||||||
ui/altTab.js \
|
ui/altTab.js \
|
||||||
|
ui/animation.js \
|
||||||
ui/appDisplay.js \
|
ui/appDisplay.js \
|
||||||
ui/appFavorites.js \
|
ui/appFavorites.js \
|
||||||
ui/automountManager.js \
|
ui/backgroundMenu.js \
|
||||||
ui/autorunManager.js \
|
ui/background.js \
|
||||||
ui/boxpointer.js \
|
ui/boxpointer.js \
|
||||||
ui/calendar.js \
|
ui/calendar.js \
|
||||||
ui/checkBox.js \
|
ui/checkBox.js \
|
||||||
@ -47,14 +50,13 @@ nobase_dist_js_DATA = \
|
|||||||
ui/dateMenu.js \
|
ui/dateMenu.js \
|
||||||
ui/dnd.js \
|
ui/dnd.js \
|
||||||
ui/endSessionDialog.js \
|
ui/endSessionDialog.js \
|
||||||
ui/environment.js \
|
|
||||||
ui/extensionSystem.js \
|
ui/extensionSystem.js \
|
||||||
ui/extensionDownloader.js \
|
ui/extensionDownloader.js \
|
||||||
ui/flashspot.js \
|
ui/environment.js \
|
||||||
ui/ibusCandidatePopup.js\
|
ui/ibusCandidatePopup.js\
|
||||||
|
ui/grabHelper.js \
|
||||||
ui/iconGrid.js \
|
ui/iconGrid.js \
|
||||||
ui/keyboard.js \
|
ui/keyboard.js \
|
||||||
ui/keyringPrompt.js \
|
|
||||||
ui/layout.js \
|
ui/layout.js \
|
||||||
ui/lightbox.js \
|
ui/lightbox.js \
|
||||||
ui/lookingGlass.js \
|
ui/lookingGlass.js \
|
||||||
@ -63,33 +65,41 @@ nobase_dist_js_DATA = \
|
|||||||
ui/main.js \
|
ui/main.js \
|
||||||
ui/messageTray.js \
|
ui/messageTray.js \
|
||||||
ui/modalDialog.js \
|
ui/modalDialog.js \
|
||||||
ui/networkAgent.js \
|
ui/separator.js \
|
||||||
ui/sessionMode.js \
|
ui/sessionMode.js \
|
||||||
ui/shellEntry.js \
|
ui/shellEntry.js \
|
||||||
ui/shellMountOperation.js \
|
ui/shellMountOperation.js \
|
||||||
|
ui/slider.js \
|
||||||
ui/notificationDaemon.js \
|
ui/notificationDaemon.js \
|
||||||
|
ui/osdWindow.js \
|
||||||
ui/overview.js \
|
ui/overview.js \
|
||||||
|
ui/overviewControls.js \
|
||||||
ui/panel.js \
|
ui/panel.js \
|
||||||
ui/panelMenu.js \
|
ui/panelMenu.js \
|
||||||
ui/placeDisplay.js \
|
ui/pointerWatcher.js \
|
||||||
ui/polkitAuthenticationAgent.js \
|
|
||||||
ui/popupMenu.js \
|
ui/popupMenu.js \
|
||||||
ui/remoteSearch.js \
|
ui/remoteSearch.js \
|
||||||
|
ui/remoteMenu.js \
|
||||||
ui/runDialog.js \
|
ui/runDialog.js \
|
||||||
|
ui/screencast.js \
|
||||||
|
ui/screenshot.js \
|
||||||
|
ui/screenShield.js \
|
||||||
ui/scripting.js \
|
ui/scripting.js \
|
||||||
ui/search.js \
|
ui/search.js \
|
||||||
ui/searchDisplay.js \
|
ui/searchDisplay.js \
|
||||||
ui/shellDBus.js \
|
ui/shellDBus.js \
|
||||||
ui/statusIconDispatcher.js \
|
|
||||||
ui/status/accessibility.js \
|
ui/status/accessibility.js \
|
||||||
ui/status/keyboard.js \
|
ui/status/keyboard.js \
|
||||||
|
ui/status/lockScreenMenu.js \
|
||||||
ui/status/network.js \
|
ui/status/network.js \
|
||||||
ui/status/power.js \
|
ui/status/power.js \
|
||||||
ui/status/volume.js \
|
ui/status/volume.js \
|
||||||
ui/status/bluetooth.js \
|
ui/status/bluetooth.js \
|
||||||
ui/telepathyClient.js \
|
ui/switcherPopup.js \
|
||||||
ui/tweener.js \
|
ui/tweener.js \
|
||||||
|
ui/unlockDialog.js \
|
||||||
ui/userMenu.js \
|
ui/userMenu.js \
|
||||||
|
ui/userWidget.js \
|
||||||
ui/viewSelector.js \
|
ui/viewSelector.js \
|
||||||
ui/wanda.js \
|
ui/wanda.js \
|
||||||
ui/windowAttentionHandler.js \
|
ui/windowAttentionHandler.js \
|
||||||
@ -98,4 +108,13 @@ nobase_dist_js_DATA = \
|
|||||||
ui/workspaceThumbnail.js \
|
ui/workspaceThumbnail.js \
|
||||||
ui/workspacesView.js \
|
ui/workspacesView.js \
|
||||||
ui/workspaceSwitcherPopup.js \
|
ui/workspaceSwitcherPopup.js \
|
||||||
ui/xdndHandler.js
|
ui/xdndHandler.js \
|
||||||
|
ui/components/__init__.js \
|
||||||
|
ui/components/autorunManager.js \
|
||||||
|
ui/components/automountManager.js \
|
||||||
|
ui/components/networkAgent.js \
|
||||||
|
ui/components/polkitAgent.js \
|
||||||
|
ui/components/recorder.js \
|
||||||
|
ui/components/telepathyClient.js \
|
||||||
|
ui/components/keyring.js \
|
||||||
|
$(NULL)
|
||||||
|
@ -13,8 +13,7 @@ const _ = Gettext.gettext;
|
|||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
|
||||||
|
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
||||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
|
||||||
<signal name="ExtensionStatusChanged">
|
<signal name="ExtensionStatusChanged">
|
||||||
<arg type="s" name="uuid"/>
|
<arg type="s" name="uuid"/>
|
||||||
<arg type="i" name="state"/>
|
<arg type="i" name="state"/>
|
||||||
@ -46,6 +45,7 @@ const Application = new Lang.Class({
|
|||||||
this._extensionPrefsModules = {};
|
this._extensionPrefsModules = {};
|
||||||
|
|
||||||
this._extensionIters = {};
|
this._extensionIters = {};
|
||||||
|
this._startupUuid = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_buildModel: function() {
|
_buildModel: function() {
|
||||||
@ -162,7 +162,7 @@ const Application = new Lang.Class({
|
|||||||
vbox.add(toolbar);
|
vbox.add(toolbar);
|
||||||
let toolitem;
|
let toolitem;
|
||||||
|
|
||||||
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
|
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
|
||||||
use_markup: true });
|
use_markup: true });
|
||||||
toolitem = new Gtk.ToolItem({ child: label });
|
toolitem = new Gtk.ToolItem({ child: label });
|
||||||
toolbar.add(toolitem);
|
toolbar.add(toolitem);
|
||||||
@ -175,7 +175,7 @@ const Application = new Lang.Class({
|
|||||||
let renderer = new Gtk.CellRendererText();
|
let renderer = new Gtk.CellRendererText();
|
||||||
this._extensionSelector.pack_start(renderer, true);
|
this._extensionSelector.pack_start(renderer, true);
|
||||||
this._extensionSelector.add_attribute(renderer, 'text', 1);
|
this._extensionSelector.add_attribute(renderer, 'text', 1);
|
||||||
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
|
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
|
||||||
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
|
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
|
||||||
|
|
||||||
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
|
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
|
||||||
@ -204,15 +204,21 @@ const Application = new Lang.Class({
|
|||||||
_scanExtensions: function() {
|
_scanExtensions: function() {
|
||||||
let finder = new ExtensionUtils.ExtensionFinder();
|
let finder = new ExtensionUtils.ExtensionFinder();
|
||||||
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||||
|
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
|
||||||
finder.scanExtensions();
|
finder.scanExtensions();
|
||||||
},
|
},
|
||||||
|
|
||||||
_extensionFound: function(signals, extension) {
|
_extensionFound: function(signals, extension) {
|
||||||
let iter = this._model.append();
|
let iter = this._model.append();
|
||||||
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||||
this._extensionIters[uuid] = iter;
|
this._extensionIters[extension.uuid] = iter;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_extensionsLoaded: function() {
|
||||||
|
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
|
||||||
|
this._selectExtension(this._startupUuid);
|
||||||
|
this._startupUuid = null;
|
||||||
|
},
|
||||||
|
|
||||||
_onActivate: function() {
|
_onActivate: function() {
|
||||||
this._window.present();
|
this._window.present();
|
||||||
@ -233,10 +239,10 @@ const Application = new Lang.Class({
|
|||||||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||||||
uuid = stripPrefix(uuid, "extension:///");
|
uuid = stripPrefix(uuid, "extension:///");
|
||||||
|
|
||||||
if (!this._extensionAvailable(uuid))
|
if (this._extensionAvailable(uuid))
|
||||||
return 1;
|
this._selectExtension(uuid);
|
||||||
|
else
|
||||||
this._selectExtension(uuid);
|
this._startupUuid = uuid;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
|
|
||||||
const ConsoleKitManagerIface = <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' />
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
|
||||||
|
|
||||||
function ConsoleKitManager() {
|
|
||||||
return new ConsoleKitProxy(Gio.DBus.system,
|
|
||||||
'org.freedesktop.ConsoleKit',
|
|
||||||
'/org/freedesktop/ConsoleKit/Manager');
|
|
||||||
};
|
|
@ -19,8 +19,7 @@ function FprintManager() {
|
|||||||
g_interface_info: FprintManagerInfo,
|
g_interface_info: FprintManagerInfo,
|
||||||
g_name: 'net.reactivated.Fprint',
|
g_name: 'net.reactivated.Fprint',
|
||||||
g_object_path: '/net/reactivated/Fprint/Manager',
|
g_object_path: '/net/reactivated/Fprint/Manager',
|
||||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
|
||||||
|
|
||||||
self.init(null);
|
self.init(null);
|
||||||
return self;
|
return self;
|
||||||
|
@ -18,12 +18,12 @@
|
|||||||
* 02111-1307, USA.
|
* 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const UPowerGlib = imports.gi.UPowerGlib;
|
|
||||||
|
|
||||||
const ConsoleKit = imports.gdm.consoleKit;
|
const LoginManager = imports.misc.loginManager;
|
||||||
const Systemd = imports.gdm.systemd;
|
|
||||||
|
|
||||||
|
const GdmUtil = imports.gdm.util;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
@ -32,20 +32,17 @@ const PowerMenuButton = new Lang.Class({
|
|||||||
Extends: PanelMenu.SystemStatusButton,
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent('system-shutdown', null);
|
/* Translators: accessible name of the power menu in the login screen */
|
||||||
this._upClient = new UPowerGlib.Client();
|
this.parent('system-shutdown-symbolic', _("Power"));
|
||||||
|
|
||||||
if (Systemd.haveSystemd())
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
this._systemdLoginManager = new Systemd.SystemdLoginManager();
|
|
||||||
else
|
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||||
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
|
this._settings.connect('changed::disable-restart-buttons',
|
||||||
|
Lang.bind(this, this._updateVisibility));
|
||||||
|
|
||||||
this._createSubMenu();
|
this._createSubMenu();
|
||||||
|
|
||||||
this._upClient.connect('notify::can-suspend',
|
|
||||||
Lang.bind(this, this._updateHaveSuspend));
|
|
||||||
this._updateHaveSuspend();
|
|
||||||
|
|
||||||
// ConsoleKit doesn't send notifications when shutdown/reboot
|
// ConsoleKit doesn't send notifications when shutdown/reboot
|
||||||
// are disabled, so we update the menu item each time the menu opens
|
// are disabled, so we update the menu item each time the menu opens
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||||
@ -53,75 +50,41 @@ const PowerMenuButton = new Lang.Class({
|
|||||||
if (open) {
|
if (open) {
|
||||||
this._updateHaveShutdown();
|
this._updateHaveShutdown();
|
||||||
this._updateHaveRestart();
|
this._updateHaveRestart();
|
||||||
|
this._updateHaveSuspend();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._updateHaveShutdown();
|
this._updateHaveShutdown();
|
||||||
this._updateHaveRestart();
|
this._updateHaveRestart();
|
||||||
|
this._updateHaveSuspend();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||||
this.actor.visible = shouldBeVisible;
|
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateHaveShutdown: function() {
|
_updateHaveShutdown: function() {
|
||||||
|
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
|
||||||
if (Systemd.haveSystemd()) {
|
this._haveShutdown = result;
|
||||||
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
|
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||||
function(result, error) {
|
this._updateVisibility();
|
||||||
if (!error)
|
}));
|
||||||
this._haveShutdown = result[0] != 'no';
|
|
||||||
else
|
|
||||||
this._haveShutdown = false;
|
|
||||||
|
|
||||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._consoleKitManager.CanStopRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error)
|
|
||||||
this._haveShutdown = result[0];
|
|
||||||
else
|
|
||||||
this._haveShutdown = false;
|
|
||||||
|
|
||||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateHaveRestart: function() {
|
_updateHaveRestart: function() {
|
||||||
|
this._loginManager.canReboot(Lang.bind(this, function(result) {
|
||||||
if (Systemd.haveSystemd()) {
|
this._haveRestart = result;
|
||||||
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
|
this._restartItem.actor.visible = this._haveRestart;
|
||||||
function(result, error) {
|
this._updateVisibility();
|
||||||
if (!error)
|
}));
|
||||||
this._haveRestart = result[0] != 'no';
|
|
||||||
else
|
|
||||||
this._haveRestart = false;
|
|
||||||
|
|
||||||
this._restartItem.actor.visible = this._haveRestart;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error)
|
|
||||||
this._haveRestart = result[0];
|
|
||||||
else
|
|
||||||
this._haveRestart = false;
|
|
||||||
|
|
||||||
this._restartItem.actor.visible = this._haveRestart;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateHaveSuspend: function() {
|
_updateHaveSuspend: function() {
|
||||||
this._haveSuspend = this._upClient.get_can_suspend();
|
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||||
this._suspendItem.actor.visible = this._haveSuspend;
|
this._haveSuspend = result;
|
||||||
this._updateVisibility();
|
this._suspendItem.actor.visible = this._haveSuspend;
|
||||||
|
this._updateVisibility();
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_createSubMenu: function() {
|
_createSubMenu: function() {
|
||||||
@ -144,27 +107,23 @@ const PowerMenuButton = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onActivateSuspend: function() {
|
_onActivateSuspend: function() {
|
||||||
if (this._haveSuspend)
|
if (!this._haveSuspend)
|
||||||
this._upClient.suspend_sync(null);
|
return;
|
||||||
|
|
||||||
|
this._loginManager.suspend();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onActivateRestart: function() {
|
_onActivateRestart: function() {
|
||||||
if (!this._haveRestart)
|
if (!this._haveRestart)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Systemd.haveSystemd())
|
this._loginManager.reboot();
|
||||||
this._systemdLoginManager.RebootRemote(true);
|
|
||||||
else
|
|
||||||
this._consoleKitManager.RestartRemote();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onActivatePowerOff: function() {
|
_onActivatePowerOff: function() {
|
||||||
if (!this._haveShutdown)
|
if (!this._haveShutdown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Systemd.haveSystemd())
|
this._loginManager.powerOff();
|
||||||
this._systemdLoginManager.PowerOffRemote(true);
|
|
||||||
else
|
|
||||||
this._consoleKitManager.StopRemote();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
151
js/gdm/realmd.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
|
||||||
|
<property name="Name" type="s" access="read"/>
|
||||||
|
<property name="Version" type="s" access="read"/>
|
||||||
|
<property name="Realms" type="ao" access="read"/>
|
||||||
|
<method name="Discover">
|
||||||
|
<arg name="string" type="s" direction="in"/>
|
||||||
|
<arg name="options" type="a{sv}" direction="in"/>
|
||||||
|
<arg name="relevance" type="i" direction="out"/>
|
||||||
|
<arg name="realm" type="ao" direction="out"/>
|
||||||
|
</method>
|
||||||
|
</interface>;
|
||||||
|
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
|
||||||
|
|
||||||
|
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
|
||||||
|
<method name="Cancel">
|
||||||
|
<arg name="operation" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="Release" />
|
||||||
|
<method name="SetLocale">
|
||||||
|
<arg name="locale" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<signal name="Diagnostics">
|
||||||
|
<arg name="data" type="s"/>
|
||||||
|
<arg name="operation" type="s"/>
|
||||||
|
</signal>
|
||||||
|
</interface>;
|
||||||
|
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
|
||||||
|
|
||||||
|
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
|
||||||
|
<property name="Name" type="s" access="read"/>
|
||||||
|
<property name="Configured" type="s" access="read"/>
|
||||||
|
<property name="Details" type="a(ss)" access="read"/>
|
||||||
|
<property name="LoginFormats" type="as" access="read"/>
|
||||||
|
<property name="LoginPolicy" type="s" access="read"/>
|
||||||
|
<property name="PermittedLogins" type="as" access="read"/>
|
||||||
|
<property name="SupportedInterfaces" type="as" access="read"/>
|
||||||
|
<method name="ChangeLoginPolicy">
|
||||||
|
<arg name="login_policy" type="s" direction="in"/>
|
||||||
|
<arg name="permitted_add" type="as" direction="in"/>
|
||||||
|
<arg name="permitted_remove" type="as" direction="in"/>
|
||||||
|
<arg name="options" type="a{sv}" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="Deconfigure">
|
||||||
|
<arg name="options" type="a{sv}" direction="in"/>
|
||||||
|
</method>
|
||||||
|
</interface>;
|
||||||
|
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
|
||||||
|
|
||||||
|
const Manager = new Lang.Class({
|
||||||
|
Name: 'Manager',
|
||||||
|
|
||||||
|
_init: function(parentActor) {
|
||||||
|
this._aggregateProvider = Provider(Gio.DBus.system,
|
||||||
|
'org.freedesktop.realmd',
|
||||||
|
'/org/freedesktop/realmd',
|
||||||
|
Lang.bind(this, this._reloadRealms))
|
||||||
|
this._realms = {};
|
||||||
|
|
||||||
|
this._signalId = this._aggregateProvider.connect('g-properties-changed',
|
||||||
|
Lang.bind(this, function(proxy, properties) {
|
||||||
|
if ('Realms' in properties.deep_unpack())
|
||||||
|
this._reloadRealms();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadRealms: function() {
|
||||||
|
let realmPaths = this._aggregateProvider.Realms;
|
||||||
|
|
||||||
|
if (!realmPaths)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (let i = 0; i < realmPaths.length; i++) {
|
||||||
|
let realm = Realm(Gio.DBus.system,
|
||||||
|
'org.freedesktop.realmd',
|
||||||
|
realmPaths[i],
|
||||||
|
Lang.bind(this, this._onRealmLoaded));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadRealm: function(realm) {
|
||||||
|
if (!realm.Configured) {
|
||||||
|
if (this._realms[realm.get_object_path()])
|
||||||
|
delete this._realms[realm.get_object_path()];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._realms[realm.get_object_path()] = realm;
|
||||||
|
|
||||||
|
this._updateLoginFormat();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRealmLoaded: function(realm, error) {
|
||||||
|
if (error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._reloadRealm(realm);
|
||||||
|
|
||||||
|
realm.connect('g-properties-changed',
|
||||||
|
Lang.bind(this, function(proxy, properties) {
|
||||||
|
if ('Configured' in properties.deep_unpack())
|
||||||
|
this._reloadRealm(realm);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLoginFormat: function() {
|
||||||
|
let newLoginFormat;
|
||||||
|
|
||||||
|
for (let realmPath in this._realms) {
|
||||||
|
let realm = this._realms[realmPath];
|
||||||
|
if (realm.LoginFormats && realm.LoginFormats.length > 0) {
|
||||||
|
newLoginFormat = realm.LoginFormats[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._loginFormat != newLoginFormat) {
|
||||||
|
this._loginFormat = newLoginFormat;
|
||||||
|
this.emit('login-format-changed', newLoginFormat);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get loginFormat() {
|
||||||
|
if (this._loginFormat !== undefined)
|
||||||
|
return this._loginFormat;
|
||||||
|
|
||||||
|
this._updateLoginFormat();
|
||||||
|
|
||||||
|
return this._loginFormat;
|
||||||
|
},
|
||||||
|
|
||||||
|
release: function() {
|
||||||
|
Service(Gio.DBus.system,
|
||||||
|
'org.freedesktop.realmd',
|
||||||
|
'/org/freedesktop/realmd',
|
||||||
|
function(service) {
|
||||||
|
service.ReleaseRemote();
|
||||||
|
});
|
||||||
|
this._aggregateProvider.disconnect(this._signalId);
|
||||||
|
this._realms = { };
|
||||||
|
this._updateLoginFormat();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(Manager.prototype)
|
@ -1,31 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const GLib = imports.gi.GLib;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
|
|
||||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
|
||||||
<method name='PowerOff'>
|
|
||||||
<arg type='b' direction='in'/>
|
|
||||||
</method>
|
|
||||||
<method name='Reboot'>
|
|
||||||
<arg type='b' direction='in'/>
|
|
||||||
</method>
|
|
||||||
<method name='CanPowerOff'>
|
|
||||||
<arg type='s' direction='out'/>
|
|
||||||
</method>
|
|
||||||
<method name='CanReboot'>
|
|
||||||
<arg type='s' direction='out'/>
|
|
||||||
</method>
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const SystemdLoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
|
||||||
|
|
||||||
function SystemdLoginManager() {
|
|
||||||
return new SystemdLoginManagerProxy(Gio.DBus.system,
|
|
||||||
'org.freedesktop.login1',
|
|
||||||
'/org/freedesktop/login1');
|
|
||||||
};
|
|
||||||
|
|
||||||
function haveSystemd() {
|
|
||||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
|
||||||
}
|
|
461
js/gdm/util.js
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
// -*- 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;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const Batch = imports.gdm.batch;
|
||||||
|
const Fprint = imports.gdm.fingerprint;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||||
|
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||||
|
const FADE_ANIMATION_TIME = 0.16;
|
||||||
|
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
||||||
|
|
||||||
|
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||||
|
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||||
|
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||||
|
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||||
|
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||||
|
|
||||||
|
const LOGO_KEY = 'logo';
|
||||||
|
const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
||||||
|
|
||||||
|
// Give user 16ms to read each character of a PAM message
|
||||||
|
const USER_READ_TIME = 16
|
||||||
|
|
||||||
|
function fadeInActor(actor) {
|
||||||
|
if (actor.opacity == 255 && actor.visible)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let hold = new Batch.Hold();
|
||||||
|
actor.show();
|
||||||
|
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
|
||||||
|
|
||||||
|
actor.opacity = 0;
|
||||||
|
actor.set_height(0);
|
||||||
|
Tweener.addTween(actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
height: naturalHeight,
|
||||||
|
time: FADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
this.set_height(-1);
|
||||||
|
hold.release();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return hold;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fadeOutActor(actor) {
|
||||||
|
if (!actor.visible || actor.opacity == 0) {
|
||||||
|
actor.opacity = 0;
|
||||||
|
actor.hide();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hold = new Batch.Hold();
|
||||||
|
Tweener.addTween(actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
height: 0,
|
||||||
|
time: FADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
this.hide();
|
||||||
|
this.set_height(-1);
|
||||||
|
hold.release();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return hold;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneAndFadeOutActor(actor) {
|
||||||
|
// Immediately hide actor so its sibling can have its space
|
||||||
|
// and position, but leave a non-reactive clone on-screen,
|
||||||
|
// so from the user's point of view it smoothly fades away
|
||||||
|
// and reveals its sibling.
|
||||||
|
actor.hide();
|
||||||
|
|
||||||
|
let clone = new Clutter.Clone({ source: actor,
|
||||||
|
reactive: false });
|
||||||
|
|
||||||
|
Main.uiGroup.add_child(clone);
|
||||||
|
|
||||||
|
let [x, y] = actor.get_transformed_position();
|
||||||
|
clone.set_position(x, y);
|
||||||
|
|
||||||
|
let hold = new Batch.Hold();
|
||||||
|
Tweener.addTween(clone,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: CLONE_FADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
clone.destroy();
|
||||||
|
hold.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hold;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShellUserVerifier = new Lang.Class({
|
||||||
|
Name: 'ShellUserVerifier',
|
||||||
|
|
||||||
|
_init: function(client, params) {
|
||||||
|
params = Params.parse(params, { reauthenticationOnly: false });
|
||||||
|
this._reauthOnly = params.reauthenticationOnly;
|
||||||
|
|
||||||
|
this._client = client;
|
||||||
|
|
||||||
|
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||||
|
|
||||||
|
this._fprintManager = new Fprint.FprintManager();
|
||||||
|
this._messageQueue = [];
|
||||||
|
this._messageQueueTimeoutId = 0;
|
||||||
|
this.hasPendingMessages = false;
|
||||||
|
|
||||||
|
this._failCounter = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
begin: function(userName, hold) {
|
||||||
|
this._cancellable = new Gio.Cancellable();
|
||||||
|
this._hold = hold;
|
||||||
|
this._userName = userName;
|
||||||
|
|
||||||
|
this._checkForFingerprintReader();
|
||||||
|
|
||||||
|
if (userName) {
|
||||||
|
// If possible, reauthenticate an already running session,
|
||||||
|
// so any session specific credentials get updated appropriately
|
||||||
|
this._client.open_reauthentication_channel(userName, this._cancellable,
|
||||||
|
Lang.bind(this, this._reauthenticationChannelOpened));
|
||||||
|
} else {
|
||||||
|
this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
if (this._cancellable)
|
||||||
|
this._cancellable.cancel();
|
||||||
|
|
||||||
|
if (this._userVerifier)
|
||||||
|
this._userVerifier.call_cancel_sync(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function() {
|
||||||
|
if (this._cancellable) {
|
||||||
|
this._cancellable.cancel();
|
||||||
|
this._cancellable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._userVerifier) {
|
||||||
|
this._userVerifier.run_dispose();
|
||||||
|
this._userVerifier = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearMessageQueue();
|
||||||
|
},
|
||||||
|
|
||||||
|
answerQuery: function(serviceName, answer) {
|
||||||
|
if (!this._userVerifier.hasPendingMessages) {
|
||||||
|
this._clearMessageQueue();
|
||||||
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||||
|
} else {
|
||||||
|
let signalId = this._userVerifier.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._userVerifier.disconnect(signalId);
|
||||||
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getIntervalForMessage: function(message) {
|
||||||
|
// We probably could be smarter here
|
||||||
|
return message.length * USER_READ_TIME;
|
||||||
|
},
|
||||||
|
|
||||||
|
finishMessageQueue: function() {
|
||||||
|
if (!this.hasPendingMessages)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._messageQueue = [];
|
||||||
|
|
||||||
|
this.hasPendingMessages = false;
|
||||||
|
this.emit('no-more-messages');
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueMessageTimeout: function() {
|
||||||
|
if (this._messageQueue.length == 0) {
|
||||||
|
this.finishMessageQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._messageQueueTimeoutId != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let message = this._messageQueue.shift();
|
||||||
|
this.emit('show-message', message.text, message.iconName);
|
||||||
|
|
||||||
|
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
message.interval,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._messageQueueTimeoutId = 0;
|
||||||
|
this._queueMessageTimeout();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueMessage: function(message, iconName) {
|
||||||
|
let interval = this._getIntervalForMessage(message);
|
||||||
|
|
||||||
|
this.hasPendingMessages = true;
|
||||||
|
this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
|
||||||
|
this._queueMessageTimeout();
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearMessageQueue: function() {
|
||||||
|
this.finishMessageQueue();
|
||||||
|
|
||||||
|
if (this._messageQueueTimeoutId != 0) {
|
||||||
|
GLib.source_remove(this._messageQueueTimeoutId);
|
||||||
|
this._messageQueueTimeoutId = 0;
|
||||||
|
}
|
||||||
|
this.emit('show-message', null, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkForFingerprintReader: function() {
|
||||||
|
this._haveFingerprintReader = false;
|
||||||
|
|
||||||
|
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
||||||
|
function(device, error) {
|
||||||
|
if (!error && device)
|
||||||
|
this._haveFingerprintReader = true;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_reportInitError: function(where, error) {
|
||||||
|
logError(error, where);
|
||||||
|
this._hold.release();
|
||||||
|
|
||||||
|
this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
|
||||||
|
this._verificationFailed(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_reauthenticationChannelOpened: function(client, result) {
|
||||||
|
try {
|
||||||
|
this._userVerifier = client.open_reauthentication_channel_finish(result);
|
||||||
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||||
|
return;
|
||||||
|
} catch(e if e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
|
||||||
|
!this._reauthOnly) {
|
||||||
|
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there is
|
||||||
|
// no session to reauthenticate. Fall back to performing verification
|
||||||
|
// from this login session
|
||||||
|
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
this._reportInitError('Failed to open reauthentication channel', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._connectSignals();
|
||||||
|
this._beginVerification();
|
||||||
|
this._hold.release();
|
||||||
|
},
|
||||||
|
|
||||||
|
_userVerifierGot: function(client, result) {
|
||||||
|
try {
|
||||||
|
this._userVerifier = client.get_user_verifier_finish(result);
|
||||||
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
this._reportInitError('Failed to obtain user verifier', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._connectSignals();
|
||||||
|
this._beginVerification();
|
||||||
|
this._hold.release();
|
||||||
|
},
|
||||||
|
|
||||||
|
_connectSignals: function() {
|
||||||
|
this._userVerifier.connect('info', Lang.bind(this, this._onInfo));
|
||||||
|
this._userVerifier.connect('problem', Lang.bind(this, this._onProblem));
|
||||||
|
this._userVerifier.connect('info-query', Lang.bind(this, this._onInfoQuery));
|
||||||
|
this._userVerifier.connect('secret-info-query', Lang.bind(this, this._onSecretInfoQuery));
|
||||||
|
this._userVerifier.connect('conversation-stopped', Lang.bind(this, this._onConversationStopped));
|
||||||
|
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||||
|
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||||
|
},
|
||||||
|
|
||||||
|
_beginVerification: function() {
|
||||||
|
this._hold.acquire();
|
||||||
|
|
||||||
|
if (this._userName) {
|
||||||
|
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
||||||
|
this._userName,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, function(obj, result) {
|
||||||
|
try {
|
||||||
|
obj.call_begin_verification_for_user_finish(result);
|
||||||
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
this._reportInitError('Failed to start verification for user', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hold.release();
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (this._haveFingerprintReader) {
|
||||||
|
this._hold.acquire();
|
||||||
|
|
||||||
|
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
||||||
|
this._userName,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, function(obj, result) {
|
||||||
|
try {
|
||||||
|
obj.call_begin_verification_for_user_finish(result);
|
||||||
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
this._reportInitError('Failed to start fingerprint verification for user', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hold.release();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, function(obj, result) {
|
||||||
|
try {
|
||||||
|
obj.call_begin_verification_finish(result);
|
||||||
|
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
this._reportInitError('Failed to start verification', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hold.release();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInfo: function(client, serviceName, info) {
|
||||||
|
// We don't display fingerprint messages, because they
|
||||||
|
// have words like UPEK in them. Instead we use the messages
|
||||||
|
// as a cue to display our own message.
|
||||||
|
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||||
|
this._haveFingerprintReader) {
|
||||||
|
|
||||||
|
// Translators: this message is shown below the password entry field
|
||||||
|
// to indicate the user can swipe their finger instead
|
||||||
|
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||||
|
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||||
|
this._queueMessage(info, 'login-dialog-message-info');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onProblem: function(client, serviceName, problem) {
|
||||||
|
// we don't want to show auth failed messages to
|
||||||
|
// users who haven't enrolled their fingerprint.
|
||||||
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
|
return;
|
||||||
|
this._queueMessage(problem, 'login-dialog-message-warning');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInfoQuery: function(client, serviceName, question) {
|
||||||
|
// We only expect questions to come from the main auth service
|
||||||
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.emit('ask-question', serviceName, question, '');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||||
|
// We only expect secret requests to come from the main auth service
|
||||||
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onReset: function() {
|
||||||
|
// Clear previous attempts to authenticate
|
||||||
|
this._failCounter = 0;
|
||||||
|
|
||||||
|
this.emit('reset');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVerificationComplete: function() {
|
||||||
|
this.emit('verification-complete');
|
||||||
|
},
|
||||||
|
|
||||||
|
_cancelAndReset: function() {
|
||||||
|
this.cancel();
|
||||||
|
this._onReset();
|
||||||
|
},
|
||||||
|
|
||||||
|
_retry: function() {
|
||||||
|
this.begin(this._userName, new Batch.Hold());
|
||||||
|
},
|
||||||
|
|
||||||
|
_verificationFailed: function(retry) {
|
||||||
|
// For Not Listed / enterprise logins, immediately reset
|
||||||
|
// the dialog
|
||||||
|
// Otherwise, we allow ALLOWED_FAILURES attempts. After that, we
|
||||||
|
// go back to the welcome screen.
|
||||||
|
|
||||||
|
this._failCounter++;
|
||||||
|
let canRetry = retry && this._userName &&
|
||||||
|
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||||
|
|
||||||
|
if (canRetry) {
|
||||||
|
if (!this._userVerifier.hasPendingMessages) {
|
||||||
|
this._retry();
|
||||||
|
} else {
|
||||||
|
let signalId = this._userVerifier.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._userVerifier.disconnect(signalId);
|
||||||
|
this._retry();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this._userVerifier.hasPendingMessages) {
|
||||||
|
this._cancelAndReset();
|
||||||
|
} else {
|
||||||
|
let signalId = this._userVerifier.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._userVerifier.disconnect(signalId);
|
||||||
|
this._cancelAndReset();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('verification-failed');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onConversationStopped: function(client, serviceName) {
|
||||||
|
// if the password service fails, then cancel everything.
|
||||||
|
// But if, e.g., fingerprint fails, still give
|
||||||
|
// password authentication a chance to succeed
|
||||||
|
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||||
|
this._verificationFailed(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('hide-login-hint');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
@ -11,6 +11,7 @@ const Gio = imports.gi.Gio;
|
|||||||
const ShellJS = imports.gi.ShellJS;
|
const ShellJS = imports.gi.ShellJS;
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
|
const FileUtils = imports.misc.fileUtils;
|
||||||
|
|
||||||
const ExtensionType = {
|
const ExtensionType = {
|
||||||
SYSTEM: 1,
|
SYSTEM: 1,
|
||||||
@ -120,11 +121,6 @@ function createExtensionObject(uuid, dir, type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encourage people to add this
|
|
||||||
if (!meta.url) {
|
|
||||||
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uuid != meta.uuid) {
|
if (uuid != meta.uuid) {
|
||||||
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||||
}
|
}
|
||||||
@ -155,52 +151,40 @@ function installImporter(extension) {
|
|||||||
const ExtensionFinder = new Lang.Class({
|
const ExtensionFinder = new Lang.Class({
|
||||||
Name: 'ExtensionFinder',
|
Name: 'ExtensionFinder',
|
||||||
|
|
||||||
_scanExtensionsInDirectory: function(dir, type) {
|
_loadExtension: function(extensionDir, info, perUserDir) {
|
||||||
let fileEnum;
|
let fileType = info.get_file_type();
|
||||||
let file, info;
|
if (fileType != Gio.FileType.DIRECTORY)
|
||||||
try {
|
return;
|
||||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
let uuid = info.get_name();
|
||||||
} catch(e) {
|
let existing = extensions[uuid];
|
||||||
logError(e, 'Could not enumerate extensions directory');
|
if (existing) {
|
||||||
|
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((info = fileEnum.next_file(null)) != null) {
|
let extension;
|
||||||
let fileType = info.get_file_type();
|
let type = extensionDir.has_prefix(perUserDir) ? ExtensionType.PER_USER
|
||||||
if (fileType != Gio.FileType.DIRECTORY)
|
: ExtensionType.SYSTEM;
|
||||||
continue;
|
try {
|
||||||
let uuid = info.get_name();
|
extension = createExtensionObject(uuid, extensionDir, type);
|
||||||
let extensionDir = dir.get_child(uuid);
|
} catch(e) {
|
||||||
|
logError(e, 'Could not load extension %s'.format(uuid));
|
||||||
let existing = extensions[uuid];
|
return;
|
||||||
if (existing) {
|
|
||||||
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let extension;
|
|
||||||
try {
|
|
||||||
extension = createExtensionObject(uuid, extensionDir, type);
|
|
||||||
} catch(e) {
|
|
||||||
logError(e, 'Could not load extension %s'.format(uuid));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.emit('extension-found', extension);
|
|
||||||
}
|
}
|
||||||
fileEnum.close(null);
|
this.emit('extension-found', extension);
|
||||||
|
},
|
||||||
|
|
||||||
|
_extensionsLoaded: function() {
|
||||||
|
this.emit('extensions-loaded');
|
||||||
},
|
},
|
||||||
|
|
||||||
scanExtensions: function() {
|
scanExtensions: function() {
|
||||||
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
|
let perUserDir = Gio.File.new_for_path(global.userdatadir);
|
||||||
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
|
FileUtils.collectFromDatadirsAsync('extensions',
|
||||||
|
{ processFile: Lang.bind(this, this._loadExtension),
|
||||||
let systemDataDirs = GLib.get_system_data_dirs();
|
loadedCallback: Lang.bind(this, this._extensionsLoaded),
|
||||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
includeUserDir: true,
|
||||||
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
|
data: perUserDir });
|
||||||
let dir = Gio.file_new_for_path(dirPath);
|
|
||||||
if (dir.query_exists(null))
|
|
||||||
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ExtensionFinder.prototype);
|
Signals.addSignalMethods(ExtensionFinder.prototype);
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
function listDirAsync(file, callback) {
|
function listDirAsync(file, callback) {
|
||||||
let allFiles = [];
|
let allFiles = [];
|
||||||
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME,
|
file.enumerate_children_async('standard::name,standard::type',
|
||||||
Gio.FileQueryInfoFlags.NONE,
|
Gio.FileQueryInfoFlags.NONE,
|
||||||
GLib.PRIORITY_LOW, null, function (obj, res) {
|
GLib.PRIORITY_LOW, null, function (obj, res) {
|
||||||
let enumerator = obj.enumerate_children_finish(res);
|
let enumerator = obj.enumerate_children_finish(res);
|
||||||
@ -23,6 +25,63 @@ function listDirAsync(file, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _collectFromDirectoryAsync(dir, loadState) {
|
||||||
|
function done() {
|
||||||
|
loadState.numLoading--;
|
||||||
|
if (loadState.loadedCallback &&
|
||||||
|
loadState.numLoading == 0)
|
||||||
|
loadState.loadedCallback(loadState.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
|
||||||
|
GLib.PRIORITY_DEFAULT, null, function(object, res) {
|
||||||
|
try {
|
||||||
|
object.query_info_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||||
|
log(e.message);
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listDirAsync(dir, Lang.bind(this, function(infos) {
|
||||||
|
for (let i = 0; i < infos.length; i++)
|
||||||
|
loadState.processFile(dir.get_child(infos[i].get_name()),
|
||||||
|
infos[i], loadState.data);
|
||||||
|
done();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectFromDatadirsAsync(subdir, params) {
|
||||||
|
params = Params.parse(params, { includeUserDir: false,
|
||||||
|
processFile: null,
|
||||||
|
loadedCallback: null,
|
||||||
|
data: null });
|
||||||
|
let loadState = { data: params.data,
|
||||||
|
numLoading: 0,
|
||||||
|
loadedCallback: params.loadedCallback,
|
||||||
|
processFile: params.processFile };
|
||||||
|
|
||||||
|
if (params.processFile == null) {
|
||||||
|
if (params.loadedCallback)
|
||||||
|
params.loadedCallback(params.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataDirs = GLib.get_system_data_dirs();
|
||||||
|
if (params.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function deleteGFile(file) {
|
function deleteGFile(file) {
|
||||||
// Work around 'delete' being a keyword in JS.
|
// Work around 'delete' being a keyword in JS.
|
||||||
return file['delete'](null);
|
return file['delete'](null);
|
||||||
|
@ -58,6 +58,7 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
|||||||
<arg type="u" direction="in" />
|
<arg type="u" direction="in" />
|
||||||
<arg type="b" direction="out" />
|
<arg type="b" direction="out" />
|
||||||
</method>
|
</method>
|
||||||
|
<property name="SessionIsActive" type="b" access="read"/>
|
||||||
<signal name="InhibitorAdded">
|
<signal name="InhibitorAdded">
|
||||||
<arg type="o" direction="out"/>
|
<arg type="o" direction="out"/>
|
||||||
</signal>
|
</signal>
|
||||||
|
144
js/misc/hash.js
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// -*- 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;
|
||||||
|
},
|
||||||
|
});
|
@ -41,24 +41,26 @@ const HistoryManager = new Lang.Class({
|
|||||||
this._historyIndex = this._history.length;
|
this._historyIndex = this._history.length;
|
||||||
},
|
},
|
||||||
|
|
||||||
prevItem: function(text) {
|
_setPrevItem: function(text) {
|
||||||
if (this._historyIndex <= 0)
|
if (this._historyIndex <= 0)
|
||||||
return text;
|
return false;
|
||||||
|
|
||||||
if (text)
|
if (text)
|
||||||
this._history[this._historyIndex] = text;
|
this._history[this._historyIndex] = text;
|
||||||
this._historyIndex--;
|
this._historyIndex--;
|
||||||
return this._indexChanged();
|
this._indexChanged();
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
nextItem: function(text) {
|
_setNextItem: function(text) {
|
||||||
if (this._historyIndex >= this._history.length)
|
if (this._historyIndex >= this._history.length)
|
||||||
return text;
|
return false;
|
||||||
|
|
||||||
if (text)
|
if (text)
|
||||||
this._history[this._historyIndex] = text;
|
this._history[this._historyIndex] = text;
|
||||||
this._historyIndex++;
|
this._historyIndex++;
|
||||||
return this._indexChanged();
|
this._indexChanged();
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
lastItem: function() {
|
lastItem: function() {
|
||||||
@ -83,11 +85,9 @@ const HistoryManager = new Lang.Class({
|
|||||||
_onEntryKeyPress: function(entry, event) {
|
_onEntryKeyPress: function(entry, event) {
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Up) {
|
if (symbol == Clutter.KEY_Up) {
|
||||||
this.prevItem(entry.get_text());
|
return this._setPrevItem(entry.get_text());
|
||||||
return true;
|
|
||||||
} else if (symbol == Clutter.KEY_Down) {
|
} else if (symbol == Clutter.KEY_Down) {
|
||||||
this.nextItem(entry.get_text());
|
return this._setNextItem(entry.get_text());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -98,8 +98,6 @@ const HistoryManager = new Lang.Class({
|
|||||||
|
|
||||||
if (this._entry)
|
if (this._entry)
|
||||||
this._entry.set_text(current);
|
this._entry.set_text(current);
|
||||||
|
|
||||||
return current;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_save: function() {
|
_save: function() {
|
||||||
|
310
js/misc/loginManager.js
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
// -*- 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 Mainloop = imports.mainloop;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||||
|
<method name='PowerOff'>
|
||||||
|
<arg type='b' direction='in'/>
|
||||||
|
</method>
|
||||||
|
<method name='Reboot'>
|
||||||
|
<arg type='b' direction='in'/>
|
||||||
|
</method>
|
||||||
|
<method name='Suspend'>
|
||||||
|
<arg type='b' direction='in'/>
|
||||||
|
</method>
|
||||||
|
<method name='CanPowerOff'>
|
||||||
|
<arg type='s' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<method name='CanReboot'>
|
||||||
|
<arg type='s' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<method name='CanSuspend'>
|
||||||
|
<arg type='s' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<method name='Inhibit'>
|
||||||
|
<arg type='s' direction='in'/>
|
||||||
|
<arg type='s' direction='in'/>
|
||||||
|
<arg type='s' direction='in'/>
|
||||||
|
<arg type='s' direction='in'/>
|
||||||
|
<arg type='h' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<method name='GetSession'>
|
||||||
|
<arg type='s' direction='in'/>
|
||||||
|
<arg type='o' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<method name='ListSessions'>
|
||||||
|
<arg name='sessions' type='a(susso)' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<signal name='PrepareForSleep'>
|
||||||
|
<arg type='b' direction='out'/>
|
||||||
|
</signal>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||||
|
<signal name='Lock' />
|
||||||
|
<signal name='Unlock' />
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
||||||
|
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||||
|
|
||||||
|
const ConsoleKitManagerIface = <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>;
|
||||||
|
|
||||||
|
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
|
||||||
|
<signal name='Lock' />
|
||||||
|
<signal name='Unlock' />
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
||||||
|
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||||
|
|
||||||
|
function haveSystemd() {
|
||||||
|
return GLib.access("/run/systemd/seats", 0) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function versionCompare(required, reference) {
|
||||||
|
required = required.split('.');
|
||||||
|
reference = reference.split('.');
|
||||||
|
|
||||||
|
for (let i = 0; i < required.length; i++) {
|
||||||
|
if (required[i] != reference[i])
|
||||||
|
return required[i] < reference[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canLock() {
|
||||||
|
try {
|
||||||
|
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
|
||||||
|
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
|
||||||
|
'/org/gnome/DisplayManager/Manager',
|
||||||
|
'org.freedesktop.DBus.Properties',
|
||||||
|
'Get', params, null,
|
||||||
|
Gio.DBusCallFlags.NONE,
|
||||||
|
-1, null);
|
||||||
|
|
||||||
|
let version = result.deep_unpack()[0].deep_unpack();
|
||||||
|
return versionCompare('3.5.91', version);
|
||||||
|
} catch(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _loginManager = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoginManager:
|
||||||
|
* An abstraction over systemd/logind and ConsoleKit.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function getLoginManager() {
|
||||||
|
if (_loginManager == null) {
|
||||||
|
if (haveSystemd())
|
||||||
|
_loginManager = new LoginManagerSystemd();
|
||||||
|
else
|
||||||
|
_loginManager = new LoginManagerConsoleKit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _loginManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoginManagerSystemd = new Lang.Class({
|
||||||
|
Name: 'LoginManagerSystemd',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._proxy = new SystemdLoginManager(Gio.DBus.system,
|
||||||
|
'org.freedesktop.login1',
|
||||||
|
'/org/freedesktop/login1');
|
||||||
|
this._proxy.connectSignal('PrepareForSleep',
|
||||||
|
Lang.bind(this, this._prepareForSleep));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||||
|
// 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.GetSessionRemote(GLib.getenv('XDG_SESSION_ID'), Lang.bind(this,
|
||||||
|
function(result, error) {
|
||||||
|
if (error) {
|
||||||
|
logError(error, 'Could not get a proxy for the current session');
|
||||||
|
} else {
|
||||||
|
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
|
||||||
|
'org.freedesktop.login1',
|
||||||
|
result[0]);
|
||||||
|
callback(this._currentSession);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
canPowerOff: function(asyncCallback) {
|
||||||
|
this._proxy.CanPowerOffRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0] != 'no');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
canReboot: function(asyncCallback) {
|
||||||
|
this._proxy.CanRebootRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0] != 'no');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
canSuspend: function(asyncCallback) {
|
||||||
|
this._proxy.CanSuspendRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0] != 'no');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
listSessions: function(asyncCallback) {
|
||||||
|
this._proxy.ListSessionsRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback([]);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
powerOff: function() {
|
||||||
|
this._proxy.PowerOffRemote(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
reboot: function() {
|
||||||
|
this._proxy.RebootRemote(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
suspend: function() {
|
||||||
|
this._proxy.SuspendRemote(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
inhibit: function(reason, callback) {
|
||||||
|
let inVariant = GLib.Variant.new('(ssss)',
|
||||||
|
['sleep',
|
||||||
|
'GNOME Shell',
|
||||||
|
reason,
|
||||||
|
'delay']);
|
||||||
|
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
|
||||||
|
Lang.bind(this, function(proxy, result) {
|
||||||
|
let fd = -1;
|
||||||
|
try {
|
||||||
|
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
|
||||||
|
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
|
||||||
|
callback(new Gio.UnixInputStream({ fd: fd }));
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, "Error getting systemd inhibitor");
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
|
||||||
|
this.emit('prepare-for-sleep', aboutToSuspend);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(LoginManagerSystemd.prototype);
|
||||||
|
|
||||||
|
const LoginManagerConsoleKit = new Lang.Class({
|
||||||
|
Name: 'LoginManagerConsoleKit',
|
||||||
|
|
||||||
|
_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);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
canPowerOff: function(asyncCallback) {
|
||||||
|
this._proxy.CanStopRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
canReboot: function(asyncCallback) {
|
||||||
|
this._proxy.CanRestartRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
canSuspend: function(asyncCallback) {
|
||||||
|
asyncCallback(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
listSessions: function(asyncCallback) {
|
||||||
|
asyncCallback([]);
|
||||||
|
},
|
||||||
|
|
||||||
|
powerOff: function() {
|
||||||
|
this._proxy.StopRemote();
|
||||||
|
},
|
||||||
|
|
||||||
|
reboot: function() {
|
||||||
|
this._proxy.RestartRemote();
|
||||||
|
},
|
||||||
|
|
||||||
|
suspend: function() {
|
||||||
|
this.emit('prepare-for-sleep', true);
|
||||||
|
this.emit('prepare-for-sleep', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
inhibit: function(reason, callback) {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
|
@ -2,9 +2,93 @@
|
|||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const NMGtk = imports.gi.NMGtk;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
// _getMobileProvidersDatabase:
|
||||||
|
//
|
||||||
|
// Gets the database of mobile providers, with references between MCCMNC/SID and
|
||||||
|
// operator name
|
||||||
|
//
|
||||||
|
let _mpd;
|
||||||
|
function _getMobileProvidersDatabase() {
|
||||||
|
if (_mpd == null) {
|
||||||
|
try {
|
||||||
|
_mpd = new NMGtk.MobileProvidersDatabase();
|
||||||
|
_mpd.init(null);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.message);
|
||||||
|
_mpd = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mpd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _findProviderForMccMnc:
|
||||||
|
// @operator_name: operator name
|
||||||
|
// @operator_code: operator code
|
||||||
|
//
|
||||||
|
// Given an operator name string (which may not be a real operator name) and an
|
||||||
|
// operator code string, tries to find a proper operator name to display.
|
||||||
|
//
|
||||||
|
function _findProviderForMccMnc(operator_name, operator_code) {
|
||||||
|
if (operator_name) {
|
||||||
|
if (operator_name.length != 0 &&
|
||||||
|
(operator_name.length > 6 || operator_name.length < 5)) {
|
||||||
|
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||||
|
// devices return when not yet connected
|
||||||
|
return operator_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(parseInt(operator_name))) {
|
||||||
|
// name is definitely not a MCCMNC, so it may be a name
|
||||||
|
// after all; return that
|
||||||
|
return operator_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let needle;
|
||||||
|
if ((!operator_name || operator_name.length == 0) && operator_code)
|
||||||
|
needle = operator_code;
|
||||||
|
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
|
||||||
|
needle = operator_name;
|
||||||
|
else // nothing to search
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let mpd = _getMobileProvidersDatabase();
|
||||||
|
if (mpd) {
|
||||||
|
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
|
||||||
|
if (provider)
|
||||||
|
return provider.get_name();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _findProviderForSid:
|
||||||
|
// @sid: System Identifier of the serving CDMA network
|
||||||
|
//
|
||||||
|
// Tries to find the operator name corresponding to the given SID
|
||||||
|
//
|
||||||
|
function _findProviderForSid(sid) {
|
||||||
|
if (sid == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let mpd = _getMobileProvidersDatabase();
|
||||||
|
if (mpd) {
|
||||||
|
let provider = mpd.lookup_cdma_sid(sid);
|
||||||
|
if (provider)
|
||||||
|
return provider.get_name();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Support for the old ModemManager interface (MM < 0.7)
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
// The following are not the complete interfaces, just the methods we need
|
// The following are not the complete interfaces, just the methods we need
|
||||||
// (or may need in the future)
|
// (or may need in the future)
|
||||||
|
|
||||||
@ -42,14 +126,6 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
|
|||||||
|
|
||||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
||||||
|
|
||||||
let _providersTable;
|
|
||||||
function _getProvidersTable() {
|
|
||||||
if (_providersTable)
|
|
||||||
return _providersTable;
|
|
||||||
let [providers, countryCodes] = Shell.mobile_providers_parse();
|
|
||||||
return _providersTable = providers;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ModemGsm = new Lang.Class({
|
const ModemGsm = new Lang.Class({
|
||||||
Name: 'ModemGsm',
|
Name: 'ModemGsm',
|
||||||
|
|
||||||
@ -65,7 +141,7 @@ const ModemGsm = new Lang.Class({
|
|||||||
this.emit('notify::signal-quality');
|
this.emit('notify::signal-quality');
|
||||||
}));
|
}));
|
||||||
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
||||||
this.operator_name = this._findOperatorName(name, code);
|
this.operator_name = _findProviderForMccMnc(name, code);
|
||||||
this.emit('notify::operator-name');
|
this.emit('notify::operator-name');
|
||||||
}));
|
}));
|
||||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
||||||
@ -75,7 +151,7 @@ const ModemGsm = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let [status, code, name] = result;
|
let [status, code, name] = result;
|
||||||
this.operator_name = this._findOperatorName(name, code);
|
this.operator_name = _findProviderForMccMnc(name, code);
|
||||||
this.emit('notify::operator-name');
|
this.emit('notify::operator-name');
|
||||||
}));
|
}));
|
||||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||||
@ -88,67 +164,6 @@ const ModemGsm = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this.emit('notify::signal-quality');
|
this.emit('notify::signal-quality');
|
||||||
}));
|
}));
|
||||||
},
|
|
||||||
|
|
||||||
_findOperatorName: function(name, opCode) {
|
|
||||||
if (name.length != 0 && (name.length > 6 || name.length < 5)) {
|
|
||||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
|
||||||
// devices return when not yet connected
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
if (isNaN(parseInt(name))) {
|
|
||||||
// name is definitely not a MCCMNC, so it may be a name
|
|
||||||
// after all; return that
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
let needle;
|
|
||||||
if (name.length == 0 && opCode)
|
|
||||||
needle = opCode;
|
|
||||||
else if (name.length == 6 || name.length == 5)
|
|
||||||
needle = name;
|
|
||||||
else // nothing to search
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return this._findProviderForMCCMNC(needle);
|
|
||||||
},
|
|
||||||
|
|
||||||
_findProviderForMCCMNC: function(needle) {
|
|
||||||
let table = _getProvidersTable();
|
|
||||||
let needlemcc = needle.substring(0, 3);
|
|
||||||
let needlemnc = needle.substring(3, needle.length);
|
|
||||||
|
|
||||||
let name2, name3;
|
|
||||||
for (let iter in table) {
|
|
||||||
let providers = table[iter];
|
|
||||||
|
|
||||||
// Search through each country's providers
|
|
||||||
for (let i = 0; i < providers.length; i++) {
|
|
||||||
let provider = providers[i];
|
|
||||||
|
|
||||||
// Search through MCC/MNC list
|
|
||||||
let list = provider.get_gsm_mcc_mnc();
|
|
||||||
for (let j = 0; j < list.length; j++) {
|
|
||||||
let mccmnc = list[j];
|
|
||||||
|
|
||||||
// Match both 2-digit and 3-digit MNC; prefer a
|
|
||||||
// 3-digit match if found, otherwise a 2-digit one.
|
|
||||||
if (mccmnc.mcc != needlemcc)
|
|
||||||
continue; // MCC was wrong
|
|
||||||
|
|
||||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
|
||||||
name3 = provider.name;
|
|
||||||
|
|
||||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
|
||||||
name2 = provider.name;
|
|
||||||
|
|
||||||
if (name2 && name3)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return name3 || name2 || null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ModemGsm.prototype);
|
Signals.addSignalMethods(ModemGsm.prototype);
|
||||||
@ -188,40 +203,99 @@ const ModemCdma = new Lang.Class({
|
|||||||
// it will return an error if the device is not connected
|
// it will return an error if the device is not connected
|
||||||
this.operator_name = null;
|
this.operator_name = null;
|
||||||
} else {
|
} else {
|
||||||
let [bandClass, band, id] = result;
|
let [bandClass, band, sid] = result;
|
||||||
if (name.length > 0)
|
|
||||||
this.operator_name = this._findProviderForSid(id);
|
this.operator_name = _findProviderForSid(sid)
|
||||||
else
|
|
||||||
this.operator_name = null;
|
|
||||||
}
|
}
|
||||||
this.emit('notify::operator-name');
|
this.emit('notify::operator-name');
|
||||||
}));
|
}));
|
||||||
},
|
|
||||||
|
|
||||||
_findProviderForSid: function(sid) {
|
|
||||||
if (sid == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
let table = _getProvidersTable();
|
|
||||||
|
|
||||||
// Search through each country
|
|
||||||
for (let iter in table) {
|
|
||||||
let providers = table[iter];
|
|
||||||
|
|
||||||
// Search through each country's providers
|
|
||||||
for (let i = 0; i < providers.length; i++) {
|
|
||||||
let provider = providers[i];
|
|
||||||
let cdma_sid = provider.get_cdma_sid();
|
|
||||||
|
|
||||||
// Search through CDMA SID list
|
|
||||||
for (let j = 0; j < cdma_sid.length; j++) {
|
|
||||||
if (cdma_sid[j] == sid)
|
|
||||||
return provider.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ModemCdma.prototype);
|
Signals.addSignalMethods(ModemCdma.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Support for the new ModemManager1 interface (MM >= 0.7)
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
|
||||||
|
<property name="SignalQuality" type="(ub)" access="read" />
|
||||||
|
</interface>;
|
||||||
|
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
|
||||||
|
|
||||||
|
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
|
||||||
|
<property name="OperatorCode" type="s" access="read" />
|
||||||
|
<property name="OperatorName" type="s" access="read" />
|
||||||
|
</interface>;
|
||||||
|
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
|
||||||
|
|
||||||
|
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
|
||||||
|
<property name="Sid" type="u" access="read" />
|
||||||
|
</interface>;
|
||||||
|
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
|
||||||
|
|
||||||
|
const BroadbandModem = new Lang.Class({
|
||||||
|
Name: 'BroadbandModem',
|
||||||
|
|
||||||
|
_init: function(path, capabilities) {
|
||||||
|
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||||
|
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||||
|
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||||
|
this._capabilities = capabilities;
|
||||||
|
|
||||||
|
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||||
|
if ('SignalQuality' in properties.deep_unpack())
|
||||||
|
this._reloadSignalQuality();
|
||||||
|
}));
|
||||||
|
this._reloadSignalQuality();
|
||||||
|
|
||||||
|
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||||
|
let unpacked = properties.deep_unpack();
|
||||||
|
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
|
||||||
|
this._reload3gppOperatorName();
|
||||||
|
}));
|
||||||
|
this._reload3gppOperatorName();
|
||||||
|
|
||||||
|
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||||
|
let unpacked = properties.deep_unpack();
|
||||||
|
if ('Nid' in unpacked || 'Sid' in unpacked)
|
||||||
|
this._reloadCdmaOperatorName();
|
||||||
|
}));
|
||||||
|
this._reloadCdmaOperatorName();
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadSignalQuality: function() {
|
||||||
|
let [quality, recent] = this._proxy.SignalQuality;
|
||||||
|
this.signal_quality = quality;
|
||||||
|
this.emit('notify::signal-quality');
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadOperatorName: function() {
|
||||||
|
let new_name = "";
|
||||||
|
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
|
||||||
|
new_name += this.operator_name_3gpp;
|
||||||
|
|
||||||
|
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
|
||||||
|
if (new_name != "")
|
||||||
|
new_name += ", ";
|
||||||
|
new_name += this.operator_name_cdma;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.operator_name = new_name;
|
||||||
|
this.emit('notify::operator-name');
|
||||||
|
},
|
||||||
|
|
||||||
|
_reload3gppOperatorName: function() {
|
||||||
|
let name = this._proxy_3gpp.OperatorName;
|
||||||
|
let code = this._proxy_3gpp.OperatorCode;
|
||||||
|
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
|
||||||
|
this._reloadOperatorName();
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadCdmaOperatorName: function() {
|
||||||
|
let sid = this._proxy_cdma.Sid;
|
||||||
|
this.operator_name_cdma = _findProviderForSid(sid);
|
||||||
|
this._reloadOperatorName();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(BroadbandModem.prototype);
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
|
|
||||||
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
|
|
||||||
<method name="GetActive">
|
|
||||||
<arg type="b" direction="out" />
|
|
||||||
</method>
|
|
||||||
<method name="Lock" />
|
|
||||||
<method name="SetActive">
|
|
||||||
<arg type="b" direction="in" />
|
|
||||||
</method>
|
|
||||||
<signal name="ActiveChanged">
|
|
||||||
<arg type="b" direction="out" />
|
|
||||||
</signal>
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface);
|
|
||||||
|
|
||||||
function ScreenSaverProxy() {
|
|
||||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
|
||||||
g_interface_name: ScreenSaverInfo.name,
|
|
||||||
g_interface_info: ScreenSaverInfo,
|
|
||||||
g_name: 'org.gnome.ScreenSaver',
|
|
||||||
g_object_path: '/org/gnome/ScreenSaver',
|
|
||||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
|
||||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
|
||||||
self.init(null);
|
|
||||||
self.screenSaverActive = false;
|
|
||||||
|
|
||||||
self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) {
|
|
||||||
self.screenSaverActive = isActive;
|
|
||||||
});
|
|
||||||
self.connect('notify::g-name-owner', function() {
|
|
||||||
if (self.g_name_owner) {
|
|
||||||
self.GetActiveRemote(function(result, excp) {
|
|
||||||
if (result) {
|
|
||||||
let [isActive] = result;
|
|
||||||
self.screenSaverActive = isActive;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
self.screenSaverActive = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
154
js/misc/util.js
@ -1,11 +1,13 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Gdk = imports.gi.Gdk;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Shell = imports.gi.Shell;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const SCROLL_TIME = 0.1;
|
||||||
|
|
||||||
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
|
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
|
||||||
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
|
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
|
||||||
@ -16,7 +18,7 @@ const _urlRegexp = new RegExp(
|
|||||||
'(^|' + _leadingJunk + ')' +
|
'(^|' + _leadingJunk + ')' +
|
||||||
'(' +
|
'(' +
|
||||||
'(?:' +
|
'(?:' +
|
||||||
'[a-z][\\w-]+://' + // scheme://
|
'(?:http|https|ftp)://' + // scheme://
|
||||||
'|' +
|
'|' +
|
||||||
'www\\d{0,3}[.]' + // www.
|
'www\\d{0,3}[.]' + // www.
|
||||||
'|' +
|
'|' +
|
||||||
@ -87,7 +89,7 @@ function trySpawn(argv)
|
|||||||
try {
|
try {
|
||||||
[success, pid] = GLib.spawn_async(null, argv, null,
|
[success, pid] = GLib.spawn_async(null, argv, null,
|
||||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||||
null, null);
|
null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
/* Rewrite the error in case of ENOENT */
|
/* Rewrite the error in case of ENOENT */
|
||||||
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
|
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
|
||||||
@ -153,7 +155,7 @@ function killall(processName) {
|
|||||||
// whatever...
|
// whatever...
|
||||||
|
|
||||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
|
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||||
// It might be useful to return success/failure, but we'd need
|
// It might be useful to return success/failure, but we'd need
|
||||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||||
// the current callers care, we don't bother.
|
// the current callers care, we don't bother.
|
||||||
@ -162,86 +164,6 @@ function killall(processName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This was ported from network-manager-applet
|
|
||||||
// Copyright 2007 - 2011 Red Hat, Inc.
|
|
||||||
// Author: Dan Williams <dcbw@redhat.com>
|
|
||||||
|
|
||||||
const _IGNORED_WORDS = [
|
|
||||||
'Semiconductor',
|
|
||||||
'Components',
|
|
||||||
'Corporation',
|
|
||||||
'Communications',
|
|
||||||
'Company',
|
|
||||||
'Corp.',
|
|
||||||
'Corp',
|
|
||||||
'Co.',
|
|
||||||
'Inc.',
|
|
||||||
'Inc',
|
|
||||||
'Incorporated',
|
|
||||||
'Ltd.',
|
|
||||||
'Limited.',
|
|
||||||
'Intel',
|
|
||||||
'chipset',
|
|
||||||
'adapter',
|
|
||||||
'[hex]',
|
|
||||||
'NDIS',
|
|
||||||
'Module'
|
|
||||||
];
|
|
||||||
|
|
||||||
const _IGNORED_PHRASES = [
|
|
||||||
'Multiprotocol MAC/baseband processor',
|
|
||||||
'Wireless LAN Controller',
|
|
||||||
'Wireless LAN Adapter',
|
|
||||||
'Wireless Adapter',
|
|
||||||
'Network Connection',
|
|
||||||
'Wireless Cardbus Adapter',
|
|
||||||
'Wireless CardBus Adapter',
|
|
||||||
'54 Mbps Wireless PC Card',
|
|
||||||
'Wireless PC Card',
|
|
||||||
'Wireless PC',
|
|
||||||
'PC Card with XJACK(r) Antenna',
|
|
||||||
'Wireless cardbus',
|
|
||||||
'Wireless LAN PC Card',
|
|
||||||
'Technology Group Ltd.',
|
|
||||||
'Communication S.p.A.',
|
|
||||||
'Business Mobile Networks BV',
|
|
||||||
'Mobile Broadband Minicard Composite Device',
|
|
||||||
'Mobile Communications AB',
|
|
||||||
'(PC-Suite Mode)'
|
|
||||||
];
|
|
||||||
|
|
||||||
function fixupPCIDescription(desc) {
|
|
||||||
desc = desc.replace(/[_,]/, ' ');
|
|
||||||
|
|
||||||
/* Attempt to shorten ID by ignoring certain phrases */
|
|
||||||
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
|
||||||
let item = _IGNORED_PHRASES[i];
|
|
||||||
let pos = desc.indexOf(item);
|
|
||||||
if (pos != -1) {
|
|
||||||
let before = desc.substring(0, pos);
|
|
||||||
let after = desc.substring(pos + item.length, desc.length);
|
|
||||||
desc = before + after;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attmept to shorten ID by ignoring certain individual words */
|
|
||||||
let words = desc.split(' ');
|
|
||||||
let out = [ ];
|
|
||||||
for (let i = 0; i < words.length; i++) {
|
|
||||||
let item = words[i];
|
|
||||||
|
|
||||||
// skip empty items (that come out from consecutive spaces)
|
|
||||||
if (item.length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (_IGNORED_WORDS.indexOf(item) == -1) {
|
|
||||||
out.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// lowerBound:
|
// lowerBound:
|
||||||
// @array: an array or array-like object, already sorted
|
// @array: an array or array-like object, already sorted
|
||||||
// according to @cmp
|
// according to @cmp
|
||||||
@ -291,3 +213,63 @@ function insertSorted(array, val, cmp) {
|
|||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeCloseButton() {
|
||||||
|
let closeButton = new St.Button({ style_class: 'notification-close'});
|
||||||
|
|
||||||
|
// This is a bit tricky. St.Bin has its own x-align/y-align properties
|
||||||
|
// that compete with Clutter's properties. This should be fixed for
|
||||||
|
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
|
||||||
|
// setters are a workaround to get Clutter's version.
|
||||||
|
closeButton.set_x_align(Clutter.ActorAlign.END);
|
||||||
|
closeButton.set_y_align(Clutter.ActorAlign.START);
|
||||||
|
|
||||||
|
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
|
||||||
|
// to respect the alignments.
|
||||||
|
closeButton.set_x_expand(true);
|
||||||
|
closeButton.set_y_expand(true);
|
||||||
|
|
||||||
|
closeButton.connect('style-changed', function() {
|
||||||
|
let themeNode = closeButton.get_theme_node();
|
||||||
|
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
|
||||||
|
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
|
||||||
|
});
|
||||||
|
|
||||||
|
return closeButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureActorVisibleInScrollView(scrollView, actor) {
|
||||||
|
let adjustment = scrollView.vscroll.adjustment;
|
||||||
|
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
let vfade = scrollView.get_effect("fade");
|
||||||
|
if (vfade)
|
||||||
|
offset = vfade.vfade_offset;
|
||||||
|
|
||||||
|
let box = actor.get_allocation_box();
|
||||||
|
let y1 = box.y1, y2 = box.y2;
|
||||||
|
|
||||||
|
let parent = actor.get_parent();
|
||||||
|
while (parent != scrollView) {
|
||||||
|
if (!parent)
|
||||||
|
throw new Error("actor not in scroll view");
|
||||||
|
|
||||||
|
let box = parent.get_allocation_box();
|
||||||
|
y1 += box.y1;
|
||||||
|
y2 += box.y1;
|
||||||
|
parent = parent.get_parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y1 < value + offset)
|
||||||
|
value = Math.max(0, y1 - offset);
|
||||||
|
else if (y2 > value + pageSize - offset)
|
||||||
|
value = Math.min(upper, y2 + offset - pageSize);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tweener.addTween(adjustment,
|
||||||
|
{ value: value,
|
||||||
|
time: SCROLL_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
}
|
||||||
|
@ -115,10 +115,10 @@ function run() {
|
|||||||
|
|
||||||
for (let i = 0; i < 2; i++) {
|
for (let i = 0; i < 2; i++) {
|
||||||
Scripting.scriptEvent('applicationsShowStart');
|
Scripting.scriptEvent('applicationsShowStart');
|
||||||
Main.overview._viewSelector.switchTab('applications');
|
Main.overview._dash.showAppsButton.checked = true;
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
Scripting.scriptEvent('applicationsShowDone');
|
Scripting.scriptEvent('applicationsShowDone');
|
||||||
Main.overview._viewSelector.switchTab('windows');
|
Main.overview._dash.showAppsButton.checked = false;
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
947
js/ui/altTab.js
84
js/ui/animation.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||||
|
|
||||||
|
const Animation = new Lang.Class({
|
||||||
|
Name: 'Animation',
|
||||||
|
|
||||||
|
_init: function(filename, width, height, speed) {
|
||||||
|
this.actor = new St.Bin();
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
this._speed = speed;
|
||||||
|
|
||||||
|
this._isLoaded = false;
|
||||||
|
this._isPlaying = false;
|
||||||
|
this._timeoutId = 0;
|
||||||
|
this._frame = 0;
|
||||||
|
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||||
|
Lang.bind(this, this._animationsLoaded));
|
||||||
|
this.actor.set_child(this._animations);
|
||||||
|
},
|
||||||
|
|
||||||
|
play: function() {
|
||||||
|
if (this._isLoaded && this._timeoutId == 0) {
|
||||||
|
if (this._frame == 0)
|
||||||
|
this._showFrame(0);
|
||||||
|
|
||||||
|
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isPlaying = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function() {
|
||||||
|
if (this._timeoutId > 0) {
|
||||||
|
Mainloop.source_remove(this._timeoutId);
|
||||||
|
this._timeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isPlaying = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_showFrame: function(frame) {
|
||||||
|
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
||||||
|
if (oldFrameActor)
|
||||||
|
oldFrameActor.hide();
|
||||||
|
|
||||||
|
this._frame = (frame % this._animations.get_n_children());
|
||||||
|
|
||||||
|
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
||||||
|
if (newFrameActor)
|
||||||
|
newFrameActor.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function() {
|
||||||
|
this._showFrame(this._frame + 1);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_animationsLoaded: function() {
|
||||||
|
this._isLoaded = true;
|
||||||
|
|
||||||
|
if (this._isPlaying)
|
||||||
|
this.play();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const AnimatedIcon = new Lang.Class({
|
||||||
|
Name: 'AnimatedIcon',
|
||||||
|
Extends: Animation,
|
||||||
|
|
||||||
|
_init: function(filename, size) {
|
||||||
|
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||||
|
}
|
||||||
|
});
|
@ -84,9 +84,12 @@ const AppFavorites = new Lang.Class({
|
|||||||
|
|
||||||
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||||
|
|
||||||
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()),
|
||||||
this._removeFavorite(appId);
|
{ forFeedback: true,
|
||||||
}));
|
undoCallback: Lang.bind(this, function () {
|
||||||
|
this._removeFavorite(appId);
|
||||||
|
})
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addFavorite: function(appId) {
|
addFavorite: function(appId) {
|
||||||
@ -116,9 +119,11 @@ const AppFavorites = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||||
Lang.bind(this, function () {
|
{ forFeedback: true,
|
||||||
this._addFavorite(appId, pos);
|
undoCallback: Lang.bind(this, function () {
|
||||||
}));
|
this._addFavorite(appId, pos);
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(AppFavorites.prototype);
|
Signals.addSignalMethods(AppFavorites.prototype);
|
||||||
|
807
js/ui/background.js
Normal file
@ -0,0 +1,807 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
|
||||||
|
const PRIMARY_COLOR_KEY = 'primary-color';
|
||||||
|
const SECONDARY_COLOR_KEY = 'secondary-color';
|
||||||
|
const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
|
||||||
|
const BACKGROUND_STYLE_KEY = 'picture-options';
|
||||||
|
const PICTURE_OPACITY_KEY = 'picture-opacity';
|
||||||
|
const PICTURE_URI_KEY = 'picture-uri';
|
||||||
|
|
||||||
|
const FADE_ANIMATION_TIME = 1.0;
|
||||||
|
|
||||||
|
// These parameters affect how often we redraw.
|
||||||
|
// The first is how different (percent crossfaded) the slide show
|
||||||
|
// has to look before redrawing and the second is the minimum
|
||||||
|
// frequency (in seconds) we're willing to wake up
|
||||||
|
const ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
|
||||||
|
const ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
|
||||||
|
|
||||||
|
let _backgroundCache = null;
|
||||||
|
|
||||||
|
const BackgroundCache = new Lang.Class({
|
||||||
|
Name: 'BackgroundCache',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._patterns = [];
|
||||||
|
this._images = [];
|
||||||
|
this._pendingFileLoads = [];
|
||||||
|
this._fileMonitors = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
getPatternContent: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
color: null,
|
||||||
|
secondColor: null,
|
||||||
|
shadingType: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE });
|
||||||
|
|
||||||
|
let content = null;
|
||||||
|
let candidateContent = null;
|
||||||
|
for (let i = 0; i < this._patterns.length; i++) {
|
||||||
|
if (!this._patterns[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this._patterns[i].get_shading() != params.shadingType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!params.color.equal(this._patterns[i].get_color()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
|
||||||
|
!params.secondColor.equal(this._patterns[i].get_second_color()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidateContent = this._patterns[i];
|
||||||
|
|
||||||
|
if (params.effects != this._patterns[i].effects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateContent) {
|
||||||
|
content = candidateContent.copy(params.monitorIndex, params.effects);
|
||||||
|
} else {
|
||||||
|
content = new Meta.Background({ meta_screen: global.screen,
|
||||||
|
monitor: params.monitorIndex,
|
||||||
|
effects: params.effects });
|
||||||
|
|
||||||
|
if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
|
||||||
|
content.load_color(params.color);
|
||||||
|
} else {
|
||||||
|
content.load_gradient(params.shadingType, params.color, params.secondColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._patterns.push(content);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
|
||||||
|
_monitorFile: function(filename) {
|
||||||
|
if (this._fileMonitors[filename])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let file = Gio.File.new_for_path(filename);
|
||||||
|
let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||||
|
|
||||||
|
let signalId = monitor.connect('changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
for (let i = 0; i < this._images.length; i++) {
|
||||||
|
if (this._images[i].get_filename() == filename)
|
||||||
|
this._images.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.disconnect(signalId);
|
||||||
|
|
||||||
|
this.emit('file-changed', filename);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._fileMonitors[filename] = monitor;
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeContent: function(contentList, content) {
|
||||||
|
let index = contentList.indexOf(content);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
contentList.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
removePatternContent: function(content) {
|
||||||
|
this._removeContent(this._patterns, content);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeImageContent: function(content) {
|
||||||
|
let filename = content.get_filename();
|
||||||
|
|
||||||
|
if (filename && this._fileMonitors[filename])
|
||||||
|
delete this._fileMonitors[filename];
|
||||||
|
|
||||||
|
this._removeContent(this._images, content);
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadImageContent: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
style: null,
|
||||||
|
filename: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
cancellable: null,
|
||||||
|
onFinished: null });
|
||||||
|
|
||||||
|
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||||
|
if (this._pendingFileLoads[i].filename == params.filename &&
|
||||||
|
this._pendingFileLoads[i].style == params.style) {
|
||||||
|
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
|
||||||
|
monitorIndex: params.monitorIndex,
|
||||||
|
effects: params.effects,
|
||||||
|
onFinished: params.onFinished });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pendingFileLoads.push({ filename: params.filename,
|
||||||
|
style: params.style,
|
||||||
|
callers: [{ shouldCopy: false,
|
||||||
|
monitorIndex: params.monitorIndex,
|
||||||
|
effects: params.effects,
|
||||||
|
onFinished: params.onFinished }] });
|
||||||
|
|
||||||
|
let content = new Meta.Background({ meta_screen: global.screen,
|
||||||
|
monitor: params.monitorIndex,
|
||||||
|
effects: params.effects });
|
||||||
|
|
||||||
|
content.load_file_async(params.filename,
|
||||||
|
params.style,
|
||||||
|
params.cancellable,
|
||||||
|
Lang.bind(this,
|
||||||
|
function(object, result) {
|
||||||
|
try {
|
||||||
|
content.load_file_finish(result);
|
||||||
|
|
||||||
|
this._monitorFile(params.filename);
|
||||||
|
this._images.push(content);
|
||||||
|
} catch(e) {
|
||||||
|
content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||||
|
let pendingLoad = this._pendingFileLoads[i];
|
||||||
|
if (pendingLoad.filename != params.filename ||
|
||||||
|
pendingLoad.style != params.style)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (let j = 0; j < pendingLoad.callers.length; j++) {
|
||||||
|
if (pendingLoad.callers[j].onFinished) {
|
||||||
|
if (content && pendingLoad.callers[j].shouldCopy) {
|
||||||
|
content = object.copy(pendingLoad.callers[j].monitorIndex,
|
||||||
|
pendingLoad.callers[j].effects);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingLoad.callers[j].onFinished(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pendingFileLoads.splice(i, 1);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
getImageContent: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
style: null,
|
||||||
|
filename: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
cancellable: null,
|
||||||
|
onFinished: null });
|
||||||
|
|
||||||
|
let content = null;
|
||||||
|
let candidateContent = null;
|
||||||
|
for (let i = 0; i < this._images.length; i++) {
|
||||||
|
if (!this._images[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this._images[i].get_style() != params.style)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this._images[i].get_filename() != params.filename)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
|
||||||
|
this._images[i].monitor_index != this._monitorIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidateContent = this._images[i];
|
||||||
|
|
||||||
|
if (params.effects != this._images[i].effects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateContent) {
|
||||||
|
content = candidateContent.copy(params.monitorIndex, params.effects);
|
||||||
|
|
||||||
|
if (params.cancellable && params.cancellable.is_cancelled())
|
||||||
|
content = null;
|
||||||
|
else
|
||||||
|
this._images.push(content);
|
||||||
|
|
||||||
|
if (params.onFinished)
|
||||||
|
params.onFinished(content);
|
||||||
|
} else {
|
||||||
|
this._loadImageContent({ filename: params.filename,
|
||||||
|
style: params.style,
|
||||||
|
effects: params.effects,
|
||||||
|
monitorIndex: params.monitorIndex,
|
||||||
|
cancellable: params.cancellable,
|
||||||
|
onFinished: params.onFinished });
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getAnimation: function(params) {
|
||||||
|
params = Params.parse(params, { filename: null,
|
||||||
|
onLoaded: null });
|
||||||
|
|
||||||
|
if (this._animationFilename == params.filename) {
|
||||||
|
if (params.onLoaded) {
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||||
|
params.onLoaded(this._animation);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let animation = new Animation({ filename: params.filename });
|
||||||
|
|
||||||
|
animation.load(Lang.bind(this, function() {
|
||||||
|
this._monitorFile(params.filename);
|
||||||
|
this._animationFilename = params.filename;
|
||||||
|
this._animation = animation;
|
||||||
|
|
||||||
|
if (params.onLoaded) {
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||||
|
params.onLoaded(this._animation);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(BackgroundCache.prototype);
|
||||||
|
|
||||||
|
function getBackgroundCache() {
|
||||||
|
if (!_backgroundCache)
|
||||||
|
_backgroundCache = new BackgroundCache();
|
||||||
|
return _backgroundCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Background = new Lang.Class({
|
||||||
|
Name: 'Background',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
layoutManager: Main.layoutManager,
|
||||||
|
effects: Meta.BackgroundEffects.NONE });
|
||||||
|
this.actor = new Meta.BackgroundGroup();
|
||||||
|
this.actor._delegate = this;
|
||||||
|
|
||||||
|
this._destroySignalId = this.actor.connect('destroy',
|
||||||
|
Lang.bind(this, this._destroy));
|
||||||
|
|
||||||
|
this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
|
||||||
|
this._monitorIndex = params.monitorIndex;
|
||||||
|
this._layoutManager = params.layoutManager;
|
||||||
|
this._effects = params.effects;
|
||||||
|
this._fileWatches = {};
|
||||||
|
this._pattern = null;
|
||||||
|
// contains a single image for static backgrounds and
|
||||||
|
// two images (from and to) for slide shows
|
||||||
|
this._images = {};
|
||||||
|
|
||||||
|
this._brightness = 1.0;
|
||||||
|
this._vignetteSharpness = 0.2;
|
||||||
|
this._saturation = 1.0;
|
||||||
|
this._cancellable = new Gio.Cancellable();
|
||||||
|
this.isLoaded = false;
|
||||||
|
|
||||||
|
this._settings.connect('changed', Lang.bind(this, function() {
|
||||||
|
this.emit('changed');
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._load();
|
||||||
|
},
|
||||||
|
|
||||||
|
_destroy: function() {
|
||||||
|
this._cancellable.cancel();
|
||||||
|
|
||||||
|
if (this._updateAnimationTimeoutId) {
|
||||||
|
GLib.source_remove (this._updateAnimationTimeoutId);
|
||||||
|
this._updateAnimationTimeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i;
|
||||||
|
let keys = Object.keys(this._fileWatches);
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
this._cache.disconnect(this._fileWatches[keys[i]]);
|
||||||
|
}
|
||||||
|
this._fileWatches = null;
|
||||||
|
|
||||||
|
if (this._pattern) {
|
||||||
|
if (this._pattern.content)
|
||||||
|
this._cache.removePatternContent(this._pattern.content);
|
||||||
|
|
||||||
|
this._pattern.destroy();
|
||||||
|
this._pattern = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = Object.keys(this._images);
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
let actor = this._images[keys[i]];
|
||||||
|
|
||||||
|
if (actor.content)
|
||||||
|
this._cache.removeImageContent(actor.content);
|
||||||
|
|
||||||
|
actor.destroy();
|
||||||
|
this._images[keys[i]] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actor.disconnect(this._destroySignalId);
|
||||||
|
this._destroySignalId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setLoaded: function() {
|
||||||
|
if (this.isLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.isLoaded = true;
|
||||||
|
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||||
|
this.emit('loaded');
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadPattern: function() {
|
||||||
|
let colorString, res, color, secondColor;
|
||||||
|
|
||||||
|
colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
|
||||||
|
[res, color] = Clutter.Color.from_string(colorString);
|
||||||
|
colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
|
||||||
|
[res, secondColor] = Clutter.Color.from_string(colorString);
|
||||||
|
|
||||||
|
let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
|
||||||
|
|
||||||
|
let content = this._cache.getPatternContent({ monitorIndex: this._monitorIndex,
|
||||||
|
effects: this._effects,
|
||||||
|
color: color,
|
||||||
|
secondColor: secondColor,
|
||||||
|
shadingType: shadingType });
|
||||||
|
|
||||||
|
this._pattern = new Meta.BackgroundActor();
|
||||||
|
this.actor.add_child(this._pattern);
|
||||||
|
|
||||||
|
this._pattern.content = content;
|
||||||
|
},
|
||||||
|
|
||||||
|
_watchCacheFile: function(filename) {
|
||||||
|
if (this._fileWatches[filename])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let signalId = this._cache.connect('file-changed',
|
||||||
|
Lang.bind(this, function(cache, changedFile) {
|
||||||
|
if (changedFile == filename) {
|
||||||
|
this.emit('changed');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this._fileWatches[filename] = signalId;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addImage: function(content, index, filename) {
|
||||||
|
content.saturation = this._saturation;
|
||||||
|
content.brightness = this._brightness;
|
||||||
|
content.vignette_sharpness = this._vignetteSharpness;
|
||||||
|
|
||||||
|
let actor = new Meta.BackgroundActor();
|
||||||
|
actor.content = content;
|
||||||
|
|
||||||
|
// The background pattern is the first actor in
|
||||||
|
// the group, and all images should be above that.
|
||||||
|
this.actor.insert_child_at_index(actor, index + 1);
|
||||||
|
|
||||||
|
this._images[index] = actor;
|
||||||
|
this._watchCacheFile(filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateImage: function(content, index, filename) {
|
||||||
|
content.saturation = this._saturation;
|
||||||
|
content.brightness = this._brightness;
|
||||||
|
content.vignette_sharpness = this._vignetteSharpness;
|
||||||
|
|
||||||
|
this._cache.removeImageContent(this._images[index].content);
|
||||||
|
this._images[index].content = content;
|
||||||
|
this._watchCacheFile(filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateAnimationProgress: function() {
|
||||||
|
if (this._images[1])
|
||||||
|
this._images[1].opacity = this._animation.transitionProgress * 255;
|
||||||
|
|
||||||
|
this._queueUpdateAnimation();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateAnimation: function() {
|
||||||
|
this._updateAnimationTimeoutId = 0;
|
||||||
|
|
||||||
|
this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
|
||||||
|
let files = this._animation.keyFrameFiles;
|
||||||
|
|
||||||
|
if (files.length == 0) {
|
||||||
|
this._setLoaded();
|
||||||
|
this._queueUpdateAnimation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let numPendingImages = files.length;
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
if (this._images[i] && this._images[i].content &&
|
||||||
|
this._images[i].content.get_filename() == files[i]) {
|
||||||
|
|
||||||
|
numPendingImages--;
|
||||||
|
if (numPendingImages == 0)
|
||||||
|
this._updateAnimationProgress();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
|
||||||
|
effects: this._effects,
|
||||||
|
style: this._style,
|
||||||
|
filename: files[i],
|
||||||
|
cancellable: this._cancellable,
|
||||||
|
onFinished: Lang.bind(this, function(content, i) {
|
||||||
|
numPendingImages--;
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
this._setLoaded();
|
||||||
|
if (numPendingImages == 0)
|
||||||
|
this._updateAnimationProgress();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._images[i]) {
|
||||||
|
this._addImage(content, i, files[i]);
|
||||||
|
} else {
|
||||||
|
this._updateImage(content, i, files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numPendingImages == 0) {
|
||||||
|
this._setLoaded();
|
||||||
|
this._updateAnimationProgress();
|
||||||
|
}
|
||||||
|
}, i)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueUpdateAnimation: function() {
|
||||||
|
if (this._updateAnimationTimeoutId != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._cancellable || this._cancellable.is_cancelled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._animation.transitionDuration)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
|
||||||
|
let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
|
||||||
|
|
||||||
|
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
|
||||||
|
timePerStep);
|
||||||
|
|
||||||
|
if (interval > GLib.MAXUINT32)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
interval,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._updateAnimationTimeoutId = 0;
|
||||||
|
this._updateAnimation();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadAnimation: function(filename) {
|
||||||
|
this._cache.getAnimation({ filename: filename,
|
||||||
|
onLoaded: Lang.bind(this, function(animation) {
|
||||||
|
this._animation = animation;
|
||||||
|
|
||||||
|
if (!this._animation || this._cancellable.is_cancelled()) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateAnimation();
|
||||||
|
this._watchCacheFile(filename);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadFile: function(filename) {
|
||||||
|
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
|
||||||
|
effects: this._effects,
|
||||||
|
style: this._style,
|
||||||
|
filename: filename,
|
||||||
|
cancellable: this._cancellable,
|
||||||
|
onFinished: Lang.bind(this, function(content) {
|
||||||
|
if (!content) {
|
||||||
|
if (!this._cancellable.is_cancelled())
|
||||||
|
this._loadAnimation(filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._addImage(content, 0, filename);
|
||||||
|
this._setLoaded();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_load: function () {
|
||||||
|
this._cache = getBackgroundCache();
|
||||||
|
|
||||||
|
this._loadPattern(this._cache);
|
||||||
|
|
||||||
|
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
|
||||||
|
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = this._settings.get_string(PICTURE_URI_KEY);
|
||||||
|
let filename;
|
||||||
|
if (GLib.uri_parse_scheme(uri) != null)
|
||||||
|
filename = Gio.File.new_for_uri(uri).get_path();
|
||||||
|
else
|
||||||
|
filename = uri;
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loadFile(filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
get saturation() {
|
||||||
|
return this._saturation;
|
||||||
|
},
|
||||||
|
|
||||||
|
set saturation(saturation) {
|
||||||
|
this._saturation = saturation;
|
||||||
|
|
||||||
|
if (this._pattern && this._pattern.content)
|
||||||
|
this._pattern.content.saturation = saturation;
|
||||||
|
|
||||||
|
let keys = Object.keys(this._images);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
let image = this._images[keys[i]];
|
||||||
|
if (image && image.content)
|
||||||
|
image.content.saturation = saturation;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get brightness() {
|
||||||
|
return this._brightness;
|
||||||
|
},
|
||||||
|
|
||||||
|
set brightness(factor) {
|
||||||
|
this._brightness = factor;
|
||||||
|
if (this._pattern && this._pattern.content)
|
||||||
|
this._pattern.content.brightness = factor;
|
||||||
|
|
||||||
|
let keys = Object.keys(this._images);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
let image = this._images[keys[i]];
|
||||||
|
if (image && image.content)
|
||||||
|
image.content.brightness = factor;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get vignetteSharpness() {
|
||||||
|
return this._vignetteSharpness;
|
||||||
|
},
|
||||||
|
|
||||||
|
set vignetteSharpness(sharpness) {
|
||||||
|
this._vignetteSharpness = sharpness;
|
||||||
|
if (this._pattern && this._pattern.content)
|
||||||
|
this._pattern.content.vignette_sharpness = sharpness;
|
||||||
|
|
||||||
|
let keys = Object.keys(this._images);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
let image = this._images[keys[i]];
|
||||||
|
if (image && image.content)
|
||||||
|
image.content.vignette_sharpness = sharpness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(Background.prototype);
|
||||||
|
|
||||||
|
const SystemBackground = new Lang.Class({
|
||||||
|
Name: 'SystemBackground',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._cache = getBackgroundCache();
|
||||||
|
this.actor = new Meta.BackgroundActor();
|
||||||
|
|
||||||
|
this._cache.getImageContent({ style: GDesktopEnums.BackgroundStyle.WALLPAPER,
|
||||||
|
filename: global.datadir + '/theme/noise-texture.png',
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
onFinished: Lang.bind(this, function(content) {
|
||||||
|
this.actor.content = content;
|
||||||
|
this.emit('loaded');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(SystemBackground.prototype);
|
||||||
|
|
||||||
|
const Animation = new Lang.Class({
|
||||||
|
Name: 'Animation',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { filename: null });
|
||||||
|
|
||||||
|
this.filename = params.filename;
|
||||||
|
this.keyFrameFiles = [];
|
||||||
|
this.transitionProgress = 0.0;
|
||||||
|
this.transitionDuration = 0.0;
|
||||||
|
this.loaded = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
load: function(callback) {
|
||||||
|
let file = Gio.File.new_for_path(this.filename);
|
||||||
|
|
||||||
|
this._show = new GnomeDesktop.BGSlideShow({ filename: this.filename });
|
||||||
|
|
||||||
|
this._show.load_async(null,
|
||||||
|
Lang.bind(this,
|
||||||
|
function(object, result) {
|
||||||
|
this.loaded = true;
|
||||||
|
if (callback)
|
||||||
|
callback();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(monitor) {
|
||||||
|
this.keyFrameFiles = [];
|
||||||
|
|
||||||
|
if (!this._show)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._show.get_num_slides() < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
|
||||||
|
|
||||||
|
this.transitionDuration = duration;
|
||||||
|
this.transitionProgress = progress;
|
||||||
|
|
||||||
|
if (file1)
|
||||||
|
this.keyFrameFiles.push(file1);
|
||||||
|
|
||||||
|
if (file2)
|
||||||
|
this.keyFrameFiles.push(file2);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(Animation.prototype);
|
||||||
|
|
||||||
|
const BackgroundManager = new Lang.Class({
|
||||||
|
Name: 'BackgroundManager',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { container: null,
|
||||||
|
layoutManager: Main.layoutManager,
|
||||||
|
monitorIndex: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
controlPosition: true });
|
||||||
|
|
||||||
|
this._container = params.container;
|
||||||
|
this._layoutManager = params.layoutManager;
|
||||||
|
this._effects = params.effects;
|
||||||
|
this._monitorIndex = params.monitorIndex;
|
||||||
|
this._controlPosition = params.controlPosition;
|
||||||
|
|
||||||
|
this.background = this._createBackground();
|
||||||
|
this._newBackground = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._newBackground) {
|
||||||
|
this._newBackground.actor.destroy();
|
||||||
|
this._newBackground = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.background) {
|
||||||
|
this.background.actor.destroy();
|
||||||
|
this.background = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBackground: function(background, monitorIndex) {
|
||||||
|
let newBackground = this._createBackground(monitorIndex);
|
||||||
|
newBackground.vignetteSharpness = background.vignetteSharpness;
|
||||||
|
newBackground.brightness = background.brightness;
|
||||||
|
newBackground.saturation = background.saturation;
|
||||||
|
newBackground.visible = background.visible;
|
||||||
|
|
||||||
|
newBackground.loadedSignalId = newBackground.connect('loaded',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
newBackground.disconnect(newBackground.loadedSignalId);
|
||||||
|
newBackground.loadedSignalId = 0;
|
||||||
|
Tweener.addTween(background.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: FADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
if (this._newBackground == newBackground) {
|
||||||
|
this.background = newBackground;
|
||||||
|
this._newBackground = null;
|
||||||
|
} else {
|
||||||
|
newBackground.actor.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
background.actor.destroy();
|
||||||
|
|
||||||
|
this.emit('changed');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._newBackground = newBackground;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createBackground: function() {
|
||||||
|
let background = new Background({ monitorIndex: this._monitorIndex,
|
||||||
|
layoutManager: this._layoutManager,
|
||||||
|
effects: this._effects });
|
||||||
|
this._container.add_child(background.actor);
|
||||||
|
|
||||||
|
let monitor = this._layoutManager.monitors[this._monitorIndex];
|
||||||
|
|
||||||
|
background.actor.set_size(monitor.width, monitor.height);
|
||||||
|
if (this._controlPosition) {
|
||||||
|
background.actor.set_position(monitor.x, monitor.y);
|
||||||
|
background.actor.lower_bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
|
||||||
|
background.disconnect(background.changeSignalId);
|
||||||
|
background.changeSignalId = 0;
|
||||||
|
this._updateBackground(background, this._monitorIndex);
|
||||||
|
}));
|
||||||
|
|
||||||
|
background.actor.connect('destroy', Lang.bind(this, function() {
|
||||||
|
if (background.changeSignalId)
|
||||||
|
background.disconnect(background.changeSignalId);
|
||||||
|
|
||||||
|
if (background.loadedSignalId)
|
||||||
|
background.disconnect(background.loadedSignalId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return background;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(BackgroundManager.prototype);
|
68
js/ui/backgroundMenu.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
const BackgroundMenu = new Lang.Class({
|
||||||
|
Name: 'BackgroundMenu',
|
||||||
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
|
_init: function(source) {
|
||||||
|
this.parent(source, 0, St.Side.TOP);
|
||||||
|
|
||||||
|
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
|
||||||
|
|
||||||
|
this.actor.add_style_class_name('background-menu');
|
||||||
|
|
||||||
|
Main.uiGroup.add_actor(this.actor);
|
||||||
|
this.actor.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function addBackgroundMenu(actor) {
|
||||||
|
let cursor = new St.Bin({ opacity: 0 });
|
||||||
|
Main.uiGroup.add_actor(cursor);
|
||||||
|
|
||||||
|
actor.reactive = true;
|
||||||
|
actor._backgroundMenu = new BackgroundMenu(cursor);
|
||||||
|
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);
|
||||||
|
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
let clickAction = new Clutter.ClickAction();
|
||||||
|
clickAction.connect('long-press', function(action, actor, state) {
|
||||||
|
if (state == Clutter.LongPressState.QUERY)
|
||||||
|
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
|
||||||
|
if (state == Clutter.LongPressState.ACTIVATE) {
|
||||||
|
openMenu();
|
||||||
|
actor._backgroundManager.ignoreRelease();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
clickAction.connect('clicked', function(action) {
|
||||||
|
if (action.get_button() == 3)
|
||||||
|
openMenu();
|
||||||
|
});
|
||||||
|
actor.add_action(clickAction);
|
||||||
|
|
||||||
|
actor.connect('destroy', function() {
|
||||||
|
actor._backgroundMenu.destroy();
|
||||||
|
actor._backgroundMenu = null;
|
||||||
|
actor._backgroundManager = null;
|
||||||
|
|
||||||
|
cursor.destroy();
|
||||||
|
});
|
||||||
|
}
|
@ -38,6 +38,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
this._arrowSide = arrowSide;
|
this._arrowSide = arrowSide;
|
||||||
this._userArrowSide = arrowSide;
|
this._userArrowSide = arrowSide;
|
||||||
this._arrowOrigin = 0;
|
this._arrowOrigin = 0;
|
||||||
|
this._arrowActor = null;
|
||||||
this.actor = new St.Bin({ x_fill: true,
|
this.actor = new St.Bin({ x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
this._container = new Shell.GenericContainer();
|
this._container = new Shell.GenericContainer();
|
||||||
@ -141,6 +142,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
|
|
||||||
this._muteInput();
|
this._muteInput();
|
||||||
|
|
||||||
|
Tweener.removeTweens(this);
|
||||||
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
||||||
xOffset: xOffset,
|
xOffset: xOffset,
|
||||||
yOffset: yOffset,
|
yOffset: yOffset,
|
||||||
@ -219,31 +221,27 @@ const BoxPointer = new Lang.Class({
|
|||||||
this.bin.allocate(childBox, flags);
|
this.bin.allocate(childBox, flags);
|
||||||
|
|
||||||
if (this._sourceActor && this._sourceActor.mapped) {
|
if (this._sourceActor && this._sourceActor.mapped) {
|
||||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
this._reposition();
|
||||||
|
this._updateFlip();
|
||||||
if (this._shouldFlip()) {
|
|
||||||
switch (this._arrowSide) {
|
|
||||||
case St.Side.TOP:
|
|
||||||
this._arrowSide = St.Side.BOTTOM;
|
|
||||||
break;
|
|
||||||
case St.Side.BOTTOM:
|
|
||||||
this._arrowSide = St.Side.TOP;
|
|
||||||
break;
|
|
||||||
case St.Side.LEFT:
|
|
||||||
this._arrowSide = St.Side.RIGHT;
|
|
||||||
break;
|
|
||||||
case St.Side.RIGHT:
|
|
||||||
this._arrowSide = St.Side.LEFT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_drawBorder: function(area) {
|
_drawBorder: function(area) {
|
||||||
let themeNode = this.actor.get_theme_node();
|
let themeNode = this.actor.get_theme_node();
|
||||||
|
|
||||||
|
if (this._arrowActor) {
|
||||||
|
let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
|
||||||
|
let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
|
||||||
|
let [absX, absY] = this.actor.get_transformed_position();
|
||||||
|
|
||||||
|
if (this._arrowSide == St.Side.TOP ||
|
||||||
|
this._arrowSide == St.Side.BOTTOM) {
|
||||||
|
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
|
||||||
|
} else {
|
||||||
|
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||||
let base = themeNode.get_length('-arrow-base');
|
let base = themeNode.get_length('-arrow-base');
|
||||||
let rise = themeNode.get_length('-arrow-rise');
|
let rise = themeNode.get_length('-arrow-rise');
|
||||||
@ -252,7 +250,6 @@ const BoxPointer = new Lang.Class({
|
|||||||
let halfBorder = borderWidth / 2;
|
let halfBorder = borderWidth / 2;
|
||||||
let halfBase = Math.floor(base/2);
|
let halfBase = Math.floor(base/2);
|
||||||
|
|
||||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
|
||||||
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
||||||
|
|
||||||
let [width, height] = area.get_surface_size();
|
let [width, height] = area.get_surface_size();
|
||||||
@ -263,7 +260,6 @@ const BoxPointer = new Lang.Class({
|
|||||||
boxWidth -= rise;
|
boxWidth -= rise;
|
||||||
}
|
}
|
||||||
let cr = area.get_context();
|
let cr = area.get_context();
|
||||||
Clutter.cairo_set_source_color(cr, borderColor);
|
|
||||||
|
|
||||||
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
||||||
// with the arrow poking out of that
|
// with the arrow poking out of that
|
||||||
@ -276,14 +272,51 @@ const BoxPointer = new Lang.Class({
|
|||||||
let [x1, y1] = [halfBorder, halfBorder];
|
let [x1, y1] = [halfBorder, halfBorder];
|
||||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
||||||
|
|
||||||
|
let skipTopLeft = false;
|
||||||
|
let skipTopRight = false;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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.LEFT:
|
||||||
|
if (this._arrowOrigin == y1)
|
||||||
|
skipTopLeft = true;
|
||||||
|
else if (this._arrowOrigin == y2)
|
||||||
|
skipBottomLeft = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
cr.moveTo(x1 + borderRadius, y1);
|
cr.moveTo(x1 + borderRadius, y1);
|
||||||
if (this._arrowSide == St.Side.TOP) {
|
if (this._arrowSide == St.Side.TOP) {
|
||||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.moveTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
cr.lineTo(x1, y1 - rise);
|
||||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1 + halfBase, y1);
|
||||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
} else if (skipTopRight) {
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.lineTo(x2 - halfBase, y1);
|
||||||
|
cr.lineTo(x2, y1 - rise);
|
||||||
|
cr.lineTo(x2, y1 + borderRadius);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||||
@ -291,19 +324,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x2 - borderRadius, y1);
|
if (!skipTopRight) {
|
||||||
|
cr.lineTo(x2 - borderRadius, y1);
|
||||||
// top-right corner
|
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
3*Math.PI/2, Math.PI*2);
|
||||||
3*Math.PI/2, Math.PI*2);
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.RIGHT) {
|
if (this._arrowSide == St.Side.RIGHT) {
|
||||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
if (skipTopRight) {
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, y1);
|
||||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
} else if (skipBottomRight) {
|
||||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
cr.lineTo(x2, y2 - halfBase);
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, y2);
|
||||||
|
cr.lineTo(x2 - borderRadius, y2);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||||
@ -311,19 +345,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x2, y2 - borderRadius);
|
if (!skipBottomRight) {
|
||||||
|
cr.lineTo(x2, y2 - borderRadius);
|
||||||
// bottom-right corner
|
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
0, Math.PI/2);
|
||||||
0, Math.PI/2);
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.BOTTOM) {
|
if (this._arrowSide == St.Side.BOTTOM) {
|
||||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
if (skipBottomLeft) {
|
||||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
cr.lineTo(x1 + halfBase, y2);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
cr.lineTo(x1, y2 + rise);
|
||||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
} else if (skipBottomRight) {
|
||||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
cr.lineTo(x2, y2 + rise);
|
||||||
|
cr.lineTo(x2 - halfBase, y2);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||||
@ -331,19 +366,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x1 + borderRadius, y2);
|
if (!skipBottomLeft) {
|
||||||
|
cr.lineTo(x1 + borderRadius, y2);
|
||||||
// bottom-left corner
|
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
Math.PI/2, Math.PI);
|
||||||
Math.PI/2, Math.PI);
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.LEFT) {
|
if (this._arrowSide == St.Side.LEFT) {
|
||||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
cr.lineTo(x1, y1 + halfBase);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
cr.lineTo(x1 - rise, y1);
|
||||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1 + borderRadius, y1);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
} else if (skipBottomLeft) {
|
||||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
cr.lineTo(x1 - rise, y2)
|
||||||
|
cr.lineTo(x1 - rise, y2 - halfBase);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||||
@ -351,22 +387,26 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x1, y1 + borderRadius);
|
if (!skipTopLeft) {
|
||||||
|
cr.lineTo(x1, y1 + borderRadius);
|
||||||
// top-left corner
|
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
Math.PI, 3*Math.PI/2);
|
||||||
Math.PI, 3*Math.PI/2);
|
}
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||||
cr.fillPreserve();
|
cr.fillPreserve();
|
||||||
Clutter.cairo_set_source_color(cr, borderColor);
|
|
||||||
cr.setLineWidth(borderWidth);
|
if (borderWidth > 0) {
|
||||||
cr.stroke();
|
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||||
|
Clutter.cairo_set_source_color(cr, borderColor);
|
||||||
|
cr.setLineWidth(borderWidth);
|
||||||
|
cr.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.$dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
setPosition: function(sourceActor, alignment) {
|
setPosition: function(sourceActor, alignment) {
|
||||||
this._arrowSide = this._userArrowSide;
|
|
||||||
|
|
||||||
// We need to show it now to force an allocation,
|
// We need to show it now to force an allocation,
|
||||||
// so that we can query the correct size.
|
// so that we can query the correct size.
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
@ -374,7 +414,8 @@ const BoxPointer = new Lang.Class({
|
|||||||
this._sourceActor = sourceActor;
|
this._sourceActor = sourceActor;
|
||||||
this._arrowAlignment = alignment;
|
this._arrowAlignment = alignment;
|
||||||
|
|
||||||
this._reposition(sourceActor, alignment);
|
this._reposition();
|
||||||
|
this._updateFlip();
|
||||||
},
|
},
|
||||||
|
|
||||||
setSourceAlignment: function(alignment) {
|
setSourceAlignment: function(alignment) {
|
||||||
@ -386,7 +427,10 @@ const BoxPointer = new Lang.Class({
|
|||||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||||
},
|
},
|
||||||
|
|
||||||
_reposition: function(sourceActor, alignment) {
|
_reposition: function() {
|
||||||
|
let sourceActor = this._sourceActor;
|
||||||
|
let alignment = this._arrowAlignment;
|
||||||
|
|
||||||
// Position correctly relative to the sourceActor
|
// Position correctly relative to the sourceActor
|
||||||
let sourceNode = sourceActor.get_theme_node();
|
let sourceNode = sourceActor.get_theme_node();
|
||||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||||
@ -404,10 +448,9 @@ const BoxPointer = new Lang.Class({
|
|||||||
let arrowBase = themeNode.get_length('-arrow-base');
|
let arrowBase = themeNode.get_length('-arrow-base');
|
||||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||||
let halfMargin = margin / 2;
|
|
||||||
|
|
||||||
let themeNode = this.actor.get_theme_node();
|
|
||||||
let gap = themeNode.get_length('-boxpointer-gap');
|
let gap = themeNode.get_length('-boxpointer-gap');
|
||||||
|
let padding = themeNode.get_length('-arrow-rise');
|
||||||
|
|
||||||
let resX, resY;
|
let resX, resY;
|
||||||
|
|
||||||
@ -426,29 +469,66 @@ const BoxPointer = new Lang.Class({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now align and position the pointing axis, making sure
|
// Now align and position the pointing axis, making sure it fits on
|
||||||
// it fits on screen
|
// screen. If the arrowOrigin is so close to the edge that the arrow
|
||||||
|
// will not be isosceles, we try to compensate as follows:
|
||||||
|
// - We skip the rounded corner and settle for a right angled arrow
|
||||||
|
// as shown below. See _drawBorder for further details.
|
||||||
|
// |\_____
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// - If the arrow was going to be acute angled, we move the position
|
||||||
|
// of the box to maintain the arrow's accuracy.
|
||||||
|
|
||||||
|
let arrowOrigin;
|
||||||
|
let halfBase = Math.floor(arrowBase/2);
|
||||||
|
let halfBorder = borderWidth / 2;
|
||||||
|
let halfMargin = margin / 2;
|
||||||
|
let [x1, y1] = [halfBorder, halfBorder];
|
||||||
|
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
|
||||||
|
|
||||||
switch (this._arrowSide) {
|
switch (this._arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
case St.Side.BOTTOM:
|
case St.Side.BOTTOM:
|
||||||
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
||||||
|
|
||||||
resX = Math.max(resX, monitor.x + 10);
|
resX = Math.max(resX, monitor.x + padding);
|
||||||
resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth));
|
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
|
||||||
this.setArrowOrigin(sourceCenterX - resX);
|
|
||||||
|
arrowOrigin = sourceCenterX - resX;
|
||||||
|
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin > x1)
|
||||||
|
resX += (arrowOrigin - x1);
|
||||||
|
arrowOrigin = x1;
|
||||||
|
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin < x2)
|
||||||
|
resX -= (x2 - arrowOrigin);
|
||||||
|
arrowOrigin = x2;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case St.Side.LEFT:
|
case St.Side.LEFT:
|
||||||
case St.Side.RIGHT:
|
case St.Side.RIGHT:
|
||||||
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
||||||
|
|
||||||
resY = Math.max(resY, monitor.y + 10);
|
resY = Math.max(resY, monitor.y + padding);
|
||||||
resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight));
|
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
|
||||||
|
|
||||||
this.setArrowOrigin(sourceCenterY - resY);
|
arrowOrigin = sourceCenterY - resY;
|
||||||
|
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin > y1)
|
||||||
|
resY += (arrowOrigin - y1);
|
||||||
|
arrowOrigin = y1;
|
||||||
|
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin < y2)
|
||||||
|
resX -= (y2 - arrowOrigin);
|
||||||
|
arrowOrigin = y2;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setArrowOrigin(arrowOrigin);
|
||||||
|
|
||||||
let parent = this.actor.get_parent();
|
let parent = this.actor.get_parent();
|
||||||
let success, x, y;
|
let success, x, y;
|
||||||
while (!success) {
|
while (!success) {
|
||||||
@ -471,10 +551,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// @actor: an actor relative to which the arrow is positioned.
|
||||||
|
// Differently from setPosition, this will not move the boxpointer itself,
|
||||||
|
// on the arrow
|
||||||
|
setArrowActor: function(actor) {
|
||||||
|
if (this._arrowActor != actor) {
|
||||||
|
this._arrowActor = actor;
|
||||||
|
this._border.queue_repaint();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_shiftActor : function() {
|
_shiftActor : function() {
|
||||||
// Since the position of the BoxPointer depends on the allocated size
|
// Since the position of the BoxPointer depends on the allocated size
|
||||||
// of the BoxPointer and the position of the source actor, trying
|
// of the BoxPointer and the position of the source actor, trying
|
||||||
// to position the BoxPoiner via the x/y properties will result in
|
// to position the BoxPointer via the x/y properties will result in
|
||||||
// allocation loops and warnings. Instead we do the positioning via
|
// allocation loops and warnings. Instead we do the positioning via
|
||||||
// the anchor point, which is independent of allocation, and leave
|
// the anchor point, which is independent of allocation, and leave
|
||||||
// x == y == 0.
|
// x == y == 0.
|
||||||
@ -482,37 +572,47 @@ const BoxPointer = new Lang.Class({
|
|||||||
-(this._yPosition + this._yOffset));
|
-(this._yPosition + this._yOffset));
|
||||||
},
|
},
|
||||||
|
|
||||||
_shouldFlip: function() {
|
_calculateArrowSide: function(arrowSide) {
|
||||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
|
||||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
|
||||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
|
||||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||||
|
|
||||||
switch (this._arrowSide) {
|
switch (arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
|
||||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||||
return true;
|
return St.Side.BOTTOM;
|
||||||
break;
|
break;
|
||||||
case St.Side.BOTTOM:
|
case St.Side.BOTTOM:
|
||||||
if (boxAllocation.y1 < monitor.y &&
|
if (sourceAllocation.y1 - boxHeight < monitor.y &&
|
||||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||||
return true;
|
return St.Side.TOP;
|
||||||
break;
|
break;
|
||||||
case St.Side.LEFT:
|
case St.Side.LEFT:
|
||||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
|
||||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||||
return true;
|
return St.Side.RIGHT;
|
||||||
break;
|
break;
|
||||||
case St.Side.RIGHT:
|
case St.Side.RIGHT:
|
||||||
if (boxAllocation.x1 < monitor.x &&
|
if (sourceAllocation.y1 - boxWidth < monitor.x &&
|
||||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||||
return true;
|
return St.Side.LEFT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return arrowSide;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateFlip: function() {
|
||||||
|
let arrowSide = this._calculateArrowSide(this._userArrowSide);
|
||||||
|
if (this._arrowSide != arrowSide) {
|
||||||
|
this._arrowSide = arrowSide;
|
||||||
|
this._reposition();
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||||
|
this._container.queue_relayout();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
set xOffset(offset) {
|
set xOffset(offset) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
@ -11,7 +12,6 @@ const Mainloop = imports.mainloop;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
|
|
||||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||||
|
|
||||||
// in org.gnome.desktop.interface
|
// in org.gnome.desktop.interface
|
||||||
@ -63,15 +63,18 @@ function _formatEventTime(event, clockFormat) {
|
|||||||
} else {
|
} else {
|
||||||
switch (clockFormat) {
|
switch (clockFormat) {
|
||||||
case '24h':
|
case '24h':
|
||||||
/* Translators: Shown in calendar event list, if 24h format */
|
/* Translators: Shown in calendar event list, if 24h format,
|
||||||
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
|
\u2236 is a ratio character, similar to : */
|
||||||
|
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* explicit fall-through */
|
/* explicit fall-through */
|
||||||
case '12h':
|
case '12h':
|
||||||
/* Transators: Shown in calendar event list, if 12h format */
|
/* Translators: Shown in calendar event list, if 12h format,
|
||||||
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
|
\u2236 is a ratio character, similar to : and \u2009 is
|
||||||
|
a thin space */
|
||||||
|
ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,15 +98,6 @@ function _getCalendarWeekForDate(date) {
|
|||||||
return weekNumber;
|
return weekNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getDigitWidth(actor){
|
|
||||||
let context = actor.get_pango_context();
|
|
||||||
let themeNode = actor.get_theme_node();
|
|
||||||
let font = themeNode.get_font();
|
|
||||||
let metrics = context.get_metrics(font, context.get_language());
|
|
||||||
let width = metrics.get_approximate_digit_width();
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getCalendarDayAbbreviation(dayNumber) {
|
function _getCalendarDayAbbreviation(dayNumber) {
|
||||||
let abbreviations = [
|
let abbreviations = [
|
||||||
/* Translators: Calendar grid abbreviation for Sunday.
|
/* Translators: Calendar grid abbreviation for Sunday.
|
||||||
@ -174,6 +168,12 @@ const EmptyEventSource = new Lang.Class({
|
|||||||
Name: 'EmptyEventSource',
|
Name: 'EmptyEventSource',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isDummy = true;
|
||||||
|
this.hasCalendars = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
requestRange: function(begin, end) {
|
requestRange: function(begin, end) {
|
||||||
@ -197,22 +197,18 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
|
|||||||
<arg type="b" direction="in" />
|
<arg type="b" direction="in" />
|
||||||
<arg type="a(sssbxxa{sv})" direction="out" />
|
<arg type="a(sssbxxa{sv})" direction="out" />
|
||||||
</method>
|
</method>
|
||||||
|
<property name="HasCalendars" type="b" access="read" />
|
||||||
<signal name="Changed" />
|
<signal name="Changed" />
|
||||||
</interface>;
|
</interface>;
|
||||||
|
|
||||||
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
|
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
|
||||||
|
|
||||||
function CalendarServer() {
|
function CalendarServer() {
|
||||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
return new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||||
g_interface_name: CalendarServerInfo.name,
|
g_interface_name: CalendarServerInfo.name,
|
||||||
g_interface_info: CalendarServerInfo,
|
g_interface_info: CalendarServerInfo,
|
||||||
g_name: 'org.gnome.Shell.CalendarServer',
|
g_name: 'org.gnome.Shell.CalendarServer',
|
||||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
g_object_path: '/org/gnome/Shell/CalendarServer' });
|
||||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
|
||||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
|
||||||
|
|
||||||
self.init(null);
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _datesEqual(a, b) {
|
function _datesEqual(a, b) {
|
||||||
@ -239,18 +235,49 @@ const DBusEventSource = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._resetCache();
|
this._resetCache();
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isDummy = false;
|
||||||
|
|
||||||
|
this._initialized = false;
|
||||||
this._dbusProxy = new CalendarServer();
|
this._dbusProxy = new CalendarServer();
|
||||||
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
|
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
|
||||||
|
try {
|
||||||
|
this._dbusProxy.init_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
log('Error loading calendars: ' + e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
|
||||||
if (this._dbusProxy.g_name_owner)
|
|
||||||
this._onNameAppeared();
|
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
||||||
else
|
if (this._dbusProxy.g_name_owner)
|
||||||
this._onNameVanished();
|
this._onNameAppeared();
|
||||||
|
else
|
||||||
|
this._onNameVanished();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
|
||||||
|
this.emit('notify::has-calendars');
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._initialized = true;
|
||||||
|
this.emit('notify::has-calendars');
|
||||||
|
this._onNameAppeared();
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._dbusProxy.run_dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
get hasCalendars() {
|
||||||
|
if (this._initialized)
|
||||||
|
return this._dbusProxy.HasCalendars;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
_resetCache: function() {
|
_resetCache: function() {
|
||||||
this._events = [];
|
this._events = [];
|
||||||
this._lastRequestBegin = null;
|
this._lastRequestBegin = null;
|
||||||
@ -271,8 +298,9 @@ const DBusEventSource = new Lang.Class({
|
|||||||
this._loadEvents(false);
|
this._loadEvents(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEventsReceived: function([appointments]) {
|
_onEventsReceived: function(results, error) {
|
||||||
let newEvents = [];
|
let newEvents = [];
|
||||||
|
let appointments = results ? results[0] : null;
|
||||||
if (appointments != null) {
|
if (appointments != null) {
|
||||||
for (let n = 0; n < appointments.length; n++) {
|
for (let n = 0; n < appointments.length; n++) {
|
||||||
let a = appointments[n];
|
let a = appointments[n];
|
||||||
@ -289,10 +317,15 @@ const DBusEventSource = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._events = newEvents;
|
this._events = newEvents;
|
||||||
|
this.isLoading = false;
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadEvents: function(forceReload) {
|
_loadEvents: function(forceReload) {
|
||||||
|
// Ignore while loading
|
||||||
|
if (!this._initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
if (this._curRequestBegin && this._curRequestEnd){
|
if (this._curRequestBegin && this._curRequestEnd){
|
||||||
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
|
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
|
||||||
if (forceReload)
|
if (forceReload)
|
||||||
@ -307,6 +340,7 @@ const DBusEventSource = new Lang.Class({
|
|||||||
|
|
||||||
requestRange: function(begin, end, forceReload) {
|
requestRange: function(begin, end, forceReload) {
|
||||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||||
|
this.isLoading = true;
|
||||||
this._lastRequestBegin = begin;
|
this._lastRequestBegin = begin;
|
||||||
this._lastRequestEnd = end;
|
this._lastRequestEnd = end;
|
||||||
this._curRequestBegin = begin;
|
this._curRequestBegin = begin;
|
||||||
@ -340,25 +374,11 @@ const DBusEventSource = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(DBusEventSource.prototype);
|
Signals.addSignalMethods(DBusEventSource.prototype);
|
||||||
|
|
||||||
// Calendar:
|
|
||||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
|
||||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
|
||||||
const Calendar = new Lang.Class({
|
const Calendar = new Lang.Class({
|
||||||
Name: 'Calendar',
|
Name: 'Calendar',
|
||||||
|
|
||||||
_init: function(eventSource) {
|
_init: function() {
|
||||||
if (eventSource) {
|
|
||||||
this._eventSource = eventSource;
|
|
||||||
|
|
||||||
this._eventSource.connect('changed', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._update(false);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._weekStart = Shell.util_get_week_start();
|
this._weekStart = Shell.util_get_week_start();
|
||||||
this._weekdate = NaN;
|
|
||||||
this._digitWidth = NaN;
|
|
||||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
||||||
|
|
||||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||||
@ -392,6 +412,16 @@ const Calendar = new Lang.Class({
|
|||||||
this._buildHeader ();
|
this._buildHeader ();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||||
|
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||||
|
setEventSource: function(eventSource) {
|
||||||
|
this._eventSource = eventSource;
|
||||||
|
this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||||
|
this._update(false);
|
||||||
|
}));
|
||||||
|
this._update(true);
|
||||||
|
},
|
||||||
|
|
||||||
// Sets the calendar to show a specific date
|
// Sets the calendar to show a specific date
|
||||||
setDate: function(date, forceReload) {
|
setDate: function(date, forceReload) {
|
||||||
if (!_sameDay(date, this._selectedDate)) {
|
if (!_sameDay(date, this._selectedDate)) {
|
||||||
@ -413,18 +443,18 @@ const Calendar = new Lang.Class({
|
|||||||
this.actor.add(this._topBox,
|
this.actor.add(this._topBox,
|
||||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||||
|
|
||||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
|
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
|
||||||
|
can_focus: true });
|
||||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
this._topBox.add(this._backButton);
|
||||||
this._topBox.add(back);
|
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
|
||||||
|
|
||||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
||||||
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
|
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
|
||||||
this._topBox.add(forward);
|
can_focus: true });
|
||||||
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
this._topBox.add(this._forwardButton);
|
||||||
|
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
||||||
|
|
||||||
// Add weekday labels...
|
// Add weekday labels...
|
||||||
//
|
//
|
||||||
@ -451,18 +481,6 @@ const Calendar = new Lang.Class({
|
|||||||
this._firstDayIndex = this.actor.get_n_children();
|
this._firstDayIndex = this.actor.get_n_children();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onStyleChange: function(actor, event) {
|
|
||||||
// width of a digit in pango units
|
|
||||||
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
|
|
||||||
this._setWeekdateHeaderWidth();
|
|
||||||
},
|
|
||||||
|
|
||||||
_setWeekdateHeaderWidth: function() {
|
|
||||||
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
|
|
||||||
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onScroll : function(actor, event) {
|
_onScroll : function(actor, event) {
|
||||||
switch (event.get_scroll_direction()) {
|
switch (event.get_scroll_direction()) {
|
||||||
case Clutter.ScrollDirection.UP:
|
case Clutter.ScrollDirection.UP:
|
||||||
@ -495,10 +513,12 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDate(newDate, false);
|
this._backButton.grab_key_focus();
|
||||||
},
|
|
||||||
|
|
||||||
_onNextMonthButtonClicked: function() {
|
this.setDate(newDate, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNextMonthButtonClicked: function() {
|
||||||
let newDate = new Date(this._selectedDate);
|
let newDate = new Date(this._selectedDate);
|
||||||
let oldMonth = newDate.getMonth();
|
let oldMonth = newDate.getMonth();
|
||||||
if (oldMonth == 11) {
|
if (oldMonth == 11) {
|
||||||
@ -517,7 +537,9 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDate(newDate, false);
|
this._forwardButton.grab_key_focus();
|
||||||
|
|
||||||
|
this.setDate(newDate, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSettingsChange: function() {
|
_onSettingsChange: function() {
|
||||||
@ -540,30 +562,60 @@ const Calendar = new Lang.Class({
|
|||||||
children[i].destroy();
|
children[i].destroy();
|
||||||
|
|
||||||
// Start at the beginning of the week before the start of the month
|
// Start at the beginning of the week before the start of the month
|
||||||
|
//
|
||||||
|
// We want to show always 6 weeks (to keep the calendar menu at the same
|
||||||
|
// height if there are no events), so we pad it according to the following
|
||||||
|
// policy:
|
||||||
|
//
|
||||||
|
// 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
|
||||||
|
// 2 - If a month has 5 weeks and it starts on week start, we pad one week
|
||||||
|
// before it (example: Apr 2012)
|
||||||
|
// 3 - If a month has 5 weeks and it starts on any other day, we pad one week
|
||||||
|
// after it (example: Nov 2012)
|
||||||
|
// 4 - If a month has 4 weeks, we pad one week before and one after it
|
||||||
|
// (example: Feb 2010)
|
||||||
|
//
|
||||||
|
// Actually computing the number of weeks is complex, but we know that the
|
||||||
|
// problematic categories (2 and 4) always start on week start, and that
|
||||||
|
// all months at the end have 6 weeks.
|
||||||
|
|
||||||
let beginDate = new Date(this._selectedDate);
|
let beginDate = new Date(this._selectedDate);
|
||||||
beginDate.setDate(1);
|
beginDate.setDate(1);
|
||||||
beginDate.setSeconds(0);
|
beginDate.setSeconds(0);
|
||||||
beginDate.setHours(12);
|
beginDate.setHours(12);
|
||||||
|
let year = beginDate.getYear();
|
||||||
|
|
||||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||||
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
let startsOnWeekStart = daysToWeekStart == 0;
|
||||||
|
let weekPadding = startsOnWeekStart ? 7 : 0;
|
||||||
|
|
||||||
|
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
|
||||||
|
|
||||||
let iter = new Date(beginDate);
|
let iter = new Date(beginDate);
|
||||||
let row = 2;
|
let row = 2;
|
||||||
while (true) {
|
// nRows here means 6 weeks + one header + one navbar
|
||||||
let button = new St.Button({ label: iter.getDate().toString() });
|
let nRows = 8;
|
||||||
|
while (row < 8) {
|
||||||
|
let button = new St.Button({ label: iter.getDate().toString(),
|
||||||
|
can_focus: true });
|
||||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||||
|
|
||||||
if (!this._eventSource)
|
if (this._eventSource.isDummy)
|
||||||
button.reactive = false;
|
button.reactive = false;
|
||||||
|
|
||||||
let iterStr = iter.toUTCString();
|
let iterStr = iter.toUTCString();
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this._shouldDateGrabFocus = true;
|
||||||
|
|
||||||
let newlySelectedDate = new Date(iterStr);
|
let newlySelectedDate = new Date(iterStr);
|
||||||
this.setDate(newlySelectedDate, false);
|
this.setDate(newlySelectedDate, false);
|
||||||
|
|
||||||
|
this._shouldDateGrabFocus = false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
|
let hasEvents = this._eventSource.hasEvents(iter);
|
||||||
let styleClass = 'calendar-day-base calendar-day';
|
let styleClass = 'calendar-day-base calendar-day';
|
||||||
|
|
||||||
if (_isWorkDay(iter))
|
if (_isWorkDay(iter))
|
||||||
styleClass += ' calendar-work-day'
|
styleClass += ' calendar-work-day'
|
||||||
else
|
else
|
||||||
@ -583,9 +635,6 @@ const Calendar = new Lang.Class({
|
|||||||
else if (iter.getMonth() != this._selectedDate.getMonth())
|
else if (iter.getMonth() != this._selectedDate.getMonth())
|
||||||
styleClass += ' calendar-other-month-day';
|
styleClass += ' calendar-other-month-day';
|
||||||
|
|
||||||
if (_sameDay(this._selectedDate, iter))
|
|
||||||
button.add_style_pseudo_class('active');
|
|
||||||
|
|
||||||
if (hasEvents)
|
if (hasEvents)
|
||||||
styleClass += ' calendar-day-with-events'
|
styleClass += ' calendar-day-with-events'
|
||||||
|
|
||||||
@ -595,6 +644,13 @@ const Calendar = new Lang.Class({
|
|||||||
this.actor.add(button,
|
this.actor.add(button,
|
||||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
||||||
|
|
||||||
|
if (_sameDay(this._selectedDate, iter)) {
|
||||||
|
button.add_style_pseudo_class('active');
|
||||||
|
|
||||||
|
if (this._shouldDateGrabFocus)
|
||||||
|
button.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
if (this._useWeekdate && iter.getDay() == 4) {
|
if (this._useWeekdate && iter.getDay() == 4) {
|
||||||
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
||||||
style_class: 'calendar-day-base calendar-week-number'});
|
style_class: 'calendar-day-base calendar-week-number'});
|
||||||
@ -603,17 +659,13 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||||
if (iter.getDay() == this._weekStart) {
|
|
||||||
// We stop on the first "first day of the week" after the month we are displaying
|
if (iter.getDay() == this._weekStart)
|
||||||
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
|
|
||||||
break;
|
|
||||||
row++;
|
row++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Signal to the event source that we are interested in events
|
// Signal to the event source that we are interested in events
|
||||||
// only from this date range
|
// only from this date range
|
||||||
if (this._eventSource)
|
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -622,70 +674,85 @@ Signals.addSignalMethods(Calendar.prototype);
|
|||||||
const EventsList = new Lang.Class({
|
const EventsList = new Lang.Class({
|
||||||
Name: 'EventsList',
|
Name: 'EventsList',
|
||||||
|
|
||||||
_init: function(eventSource) {
|
_init: function() {
|
||||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
this.actor = new St.Table({ style_class: 'events-table' });
|
||||||
this._date = new Date();
|
this._date = new Date();
|
||||||
this._eventSource = eventSource;
|
|
||||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
|
||||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||||
this._weekStart = Shell.util_get_week_start();
|
this._weekStart = Shell.util_get_week_start();
|
||||||
|
|
||||||
this._update();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
setEventSource: function(eventSource) {
|
||||||
if (includeDayName) {
|
this._eventSource = eventSource;
|
||||||
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
|
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||||
text: day } ),
|
|
||||||
{ x_fill: true } );
|
|
||||||
}
|
|
||||||
timeBox.add(new St.Label( { style_class: 'events-day-time',
|
|
||||||
text: time} ),
|
|
||||||
{ x_fill: true } );
|
|
||||||
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
|
|
||||||
text: desc} ));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
_addEvent: function(event, index, includeDayName) {
|
||||||
if (!this._eventSource)
|
let dayString;
|
||||||
return;
|
if (includeDayName)
|
||||||
|
dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||||
|
else
|
||||||
|
dayString = '';
|
||||||
|
|
||||||
|
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
|
||||||
|
text: dayString });
|
||||||
|
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 clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||||
|
let timeString = _formatEventTime(event, clockFormat);
|
||||||
|
let timeLabel = new St.Label({ style_class: 'events-day-time',
|
||||||
|
text: timeString });
|
||||||
|
timeLabel.clutter_text.line_wrap = false;
|
||||||
|
timeLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(timeLabel, { row: index, col: 1,
|
||||||
|
x_expand: false, x_align: St.Align.MIDDLE,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
|
|
||||||
|
let titleLabel = new St.Label({ style_class: 'events-day-task',
|
||||||
|
text: event.summary });
|
||||||
|
titleLabel.clutter_text.line_wrap = true;
|
||||||
|
titleLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(titleLabel, { row: index, col: 2,
|
||||||
|
x_expand: true, x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
|
},
|
||||||
|
|
||||||
|
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
|
||||||
let events = this._eventSource.getEvents(begin, end);
|
let events = this._eventSource.getEvents(begin, end);
|
||||||
|
|
||||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
|
||||||
|
|
||||||
if (events.length == 0 && !showNothingScheduled)
|
if (events.length == 0 && !showNothingScheduled)
|
||||||
return;
|
return index;
|
||||||
|
|
||||||
let vbox = new St.BoxLayout( {vertical: true} );
|
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
|
||||||
this.actor.add(vbox);
|
{ row: index, col: 0, col_span: 3,
|
||||||
|
// In theory, x_expand should be true here, but x_expand
|
||||||
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
|
// is a property of the column for StTable, ie all day cells
|
||||||
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
|
// get it too
|
||||||
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
|
x_expand: false, x_align: St.Align.START,
|
||||||
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
|
y_fill: false, y_align: St.Align.START });
|
||||||
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
|
index++;
|
||||||
box.add(dayNameBox, {x_fill: false});
|
|
||||||
box.add(timeBox, {x_fill: false});
|
|
||||||
box.add(eventTitleBox, {expand: true});
|
|
||||||
vbox.add(box);
|
|
||||||
|
|
||||||
for (let n = 0; n < events.length; n++) {
|
for (let n = 0; n < events.length; n++) {
|
||||||
let event = events[n];
|
this._addEvent(events[n], index, includeDayName);
|
||||||
let dayString = _getEventDayAbbreviation(event.date.getDay());
|
index++;
|
||||||
let timeString = _formatEventTime(event, clockFormat);
|
|
||||||
let summaryString = event.summary;
|
|
||||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events.length == 0 && showNothingScheduled) {
|
if (events.length == 0 && showNothingScheduled) {
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
/* Translators: Text to show if there are no events */
|
/* Translators: Text to show if there are no events */
|
||||||
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
||||||
let timeString = _formatEventTime(nothingEvent, clockFormat);
|
this._addEvent(nothingEvent, index, false);
|
||||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
},
|
},
|
||||||
|
|
||||||
_showOtherDay: function(day) {
|
_showOtherDay: function(day) {
|
||||||
@ -702,37 +769,40 @@ const EventsList = new Lang.Class({
|
|||||||
else
|
else
|
||||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
||||||
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
|
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_showToday: function() {
|
_showToday: function() {
|
||||||
this.actor.destroy_all_children();
|
this.actor.destroy_all_children();
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
let dayBegin = _getBeginningOfDay(now);
|
let dayBegin = _getBeginningOfDay(now);
|
||||||
let dayEnd = _getEndOfDay(now);
|
let dayEnd = _getEndOfDay(now);
|
||||||
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
|
index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
|
||||||
|
|
||||||
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
||||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
|
||||||
|
|
||||||
if (dayEnd.getDay() <= 4 + this._weekStart) {
|
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
||||||
|
|
||||||
|
if (dayInWeek < 5) {
|
||||||
/* If now is within the first 5 days we show "This week" and
|
/* If now is within the first 5 days we show "This week" and
|
||||||
* include events up until and including Saturday/Sunday
|
* include events up until and including Saturday/Sunday
|
||||||
* (depending on whether a week starts on Sunday/Monday).
|
* (depending on whether a week starts on Sunday/Monday).
|
||||||
*/
|
*/
|
||||||
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
|
||||||
} else {
|
} else {
|
||||||
/* otherwise it's one of the two last days of the week ... show
|
/* otherwise it's one of the two last days of the week ... show
|
||||||
* "Next week" and include events up until and including *next*
|
* "Next week" and include events up until and including *next*
|
||||||
* Saturday/Sunday
|
* Saturday/Sunday
|
||||||
*/
|
*/
|
||||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -745,6 +815,9 @@ const EventsList = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_update: function() {
|
_update: function() {
|
||||||
|
if (this._eventSource.isLoading)
|
||||||
|
return;
|
||||||
|
|
||||||
let today = new Date();
|
let today = new Date();
|
||||||
if (_sameDay (this._date, today)) {
|
if (_sameDay (this._date, today)) {
|
||||||
this._showToday();
|
this._showToday();
|
||||||
|
65
js/ui/components/__init__.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
const ComponentManager = new Lang.Class({
|
||||||
|
Name: 'ComponentManager',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._allComponents = {};
|
||||||
|
this._enabledComponents = [];
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
this._sessionUpdated();
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
let newEnabledComponents = Main.sessionMode.components;
|
||||||
|
|
||||||
|
newEnabledComponents.filter(Lang.bind(this, function(name) {
|
||||||
|
return this._enabledComponents.indexOf(name) == -1;
|
||||||
|
})).forEach(Lang.bind(this, function(name) {
|
||||||
|
this._enableComponent(name);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._enabledComponents.filter(Lang.bind(this, function(name) {
|
||||||
|
return newEnabledComponents.indexOf(name) == -1;
|
||||||
|
})).forEach(Lang.bind(this, function(name) {
|
||||||
|
this._disableComponent(name);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._enabledComponents = newEnabledComponents;
|
||||||
|
},
|
||||||
|
|
||||||
|
_importComponent: function(name) {
|
||||||
|
let module = imports.ui.components[name];
|
||||||
|
return module.Component;
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureComponent: function(name) {
|
||||||
|
let component = this._allComponents[name];
|
||||||
|
if (component)
|
||||||
|
return component;
|
||||||
|
|
||||||
|
if (Main.sessionMode.isLocked)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let constructor = this._importComponent(name);
|
||||||
|
component = new constructor();
|
||||||
|
this._allComponents[name] = component;
|
||||||
|
return component;
|
||||||
|
},
|
||||||
|
|
||||||
|
_enableComponent: function(name) {
|
||||||
|
let component = this._ensureComponent(name);
|
||||||
|
if (component)
|
||||||
|
component.enable();
|
||||||
|
},
|
||||||
|
|
||||||
|
_disableComponent: function(name) {
|
||||||
|
let component = this._allComponents[name];
|
||||||
|
if (component == null)
|
||||||
|
return;
|
||||||
|
component.disable();
|
||||||
|
}
|
||||||
|
});
|
@ -5,12 +5,11 @@ const Mainloop = imports.mainloop;
|
|||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
const ScreenSaver = imports.misc.screenSaver;
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
|
||||||
|
|
||||||
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
||||||
|
|
||||||
@ -20,62 +19,6 @@ const SETTING_ENABLE_AUTOMOUNT = 'automount';
|
|||||||
|
|
||||||
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
|
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
|
||||||
|
|
||||||
const ConsoleKitSessionIface = <interface name="org.freedesktop.ConsoleKit.Session">
|
|
||||||
<method name="IsActive">
|
|
||||||
<arg type="b" direction="out" />
|
|
||||||
</method>
|
|
||||||
<signal name="ActiveChanged">
|
|
||||||
<arg type="b" direction="out" />
|
|
||||||
</signal>
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const ConsoleKitSessionProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
|
||||||
|
|
||||||
const ConsoleKitManagerIface = <interface name="org.freedesktop.ConsoleKit.Manager">
|
|
||||||
<method name="GetCurrentSession">
|
|
||||||
<arg type="o" direction="out" />
|
|
||||||
</method>
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const ConsoleKitManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ConsoleKitManagerIface);
|
|
||||||
|
|
||||||
function ConsoleKitManager() {
|
|
||||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
|
||||||
g_interface_name: ConsoleKitManagerInfo.name,
|
|
||||||
g_interface_info: ConsoleKitManagerInfo,
|
|
||||||
g_name: 'org.freedesktop.ConsoleKit',
|
|
||||||
g_object_path: '/org/freedesktop/ConsoleKit/Manager',
|
|
||||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
|
||||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
|
||||||
|
|
||||||
self._updateSessionActive = function() {
|
|
||||||
if (self.g_name_owner) {
|
|
||||||
self.GetCurrentSessionRemote(function([session]) {
|
|
||||||
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
|
|
||||||
|
|
||||||
self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) {
|
|
||||||
self.sessionActive = isActive;
|
|
||||||
});
|
|
||||||
self._ckSession.IsActiveRemote(function([isActive]) {
|
|
||||||
self.sessionActive = isActive;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.sessionActive = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.connect('notify::g-name-owner',
|
|
||||||
Lang.bind(self, self._updateSessionActive));
|
|
||||||
|
|
||||||
self._updateSessionActive();
|
|
||||||
self.init(null);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
function haveSystemd() {
|
|
||||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AutomountManager = new Lang.Class({
|
const AutomountManager = new Lang.Class({
|
||||||
Name: 'AutomountManager',
|
Name: 'AutomountManager',
|
||||||
|
|
||||||
@ -89,32 +32,30 @@ const AutomountManager = new Lang.Class({
|
|||||||
Lang.bind(this, this._InhibitorsChanged));
|
Lang.bind(this, this._InhibitorsChanged));
|
||||||
this._inhibited = false;
|
this._inhibited = false;
|
||||||
|
|
||||||
if (!haveSystemd())
|
|
||||||
this.ckListener = new ConsoleKitManager();
|
|
||||||
|
|
||||||
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
|
|
||||||
this._ssProxy.connectSignal('ActiveChanged',
|
|
||||||
Lang.bind(this, this._screenSaverActiveChanged));
|
|
||||||
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
},
|
||||||
|
|
||||||
this._volumeMonitor.connect('volume-added',
|
enable: function() {
|
||||||
Lang.bind(this,
|
this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded));
|
||||||
this._onVolumeAdded));
|
this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved));
|
||||||
this._volumeMonitor.connect('volume-removed',
|
this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected));
|
||||||
Lang.bind(this,
|
this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected));
|
||||||
this._onVolumeRemoved));
|
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
|
||||||
this._volumeMonitor.connect('drive-connected',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._onDriveConnected));
|
|
||||||
this._volumeMonitor.connect('drive-disconnected',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._onDriveDisconnected));
|
|
||||||
this._volumeMonitor.connect('drive-eject-button',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._onDriveEjectButton));
|
|
||||||
|
|
||||||
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._volumeMonitor.disconnect(this._volumeAddedId);
|
||||||
|
this._volumeMonitor.disconnect(this._volumeRemovedId);
|
||||||
|
this._volumeMonitor.disconnect(this._driveConnectedId);
|
||||||
|
this._volumeMonitor.disconnect(this._driveDisconnectedId);
|
||||||
|
this._volumeMonitor.disconnect(this._driveEjectButtonId);
|
||||||
|
|
||||||
|
if (this._mountAllId > 0) {
|
||||||
|
Mainloop.source_remove(this._mountAllId);
|
||||||
|
this._mountAllId = 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
|
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
|
||||||
@ -127,17 +68,6 @@ const AutomountManager = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
|
|
||||||
if (!isActive) {
|
|
||||||
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
|
|
||||||
this._checkAndMountVolume(volume);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the queue anyway
|
|
||||||
this._volumeQueue = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
_startupMountAll: function() {
|
_startupMountAll: function() {
|
||||||
let volumes = this._volumeMonitor.get_volumes();
|
let volumes = this._volumeMonitor.get_volumes();
|
||||||
volumes.forEach(Lang.bind(this, function(volume) {
|
volumes.forEach(Lang.bind(this, function(volume) {
|
||||||
@ -146,48 +76,36 @@ const AutomountManager = new Lang.Class({
|
|||||||
allowAutorun: false });
|
allowAutorun: false });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._mountAllId = 0;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
isSessionActive: function() {
|
|
||||||
// Return whether the current session is active, using the
|
|
||||||
// right mechanism: either systemd if available or ConsoleKit
|
|
||||||
// as fallback.
|
|
||||||
|
|
||||||
if (haveSystemd())
|
|
||||||
return Shell.session_is_active_for_systemd();
|
|
||||||
|
|
||||||
return this.ckListener.sessionActive;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDriveConnected: function() {
|
_onDriveConnected: function() {
|
||||||
// if we're not in the current ConsoleKit session,
|
// if we're not in the current ConsoleKit session,
|
||||||
// or screensaver is active, don't play sounds
|
// or screensaver is active, don't play sounds
|
||||||
if (!this.isSessionActive())
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._ssProxy.screenSaverActive)
|
global.play_theme_sound(0, 'device-added-media',
|
||||||
return;
|
_("External drive connected"),
|
||||||
|
null);
|
||||||
global.play_theme_sound(0, 'device-added-media');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDriveDisconnected: function() {
|
_onDriveDisconnected: function() {
|
||||||
// if we're not in the current ConsoleKit session,
|
// if we're not in the current ConsoleKit session,
|
||||||
// or screensaver is active, don't play sounds
|
// or screensaver is active, don't play sounds
|
||||||
if (!this.isSessionActive())
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._ssProxy.screenSaverActive)
|
global.play_theme_sound(0, 'device-removed-media',
|
||||||
return;
|
_("External drive disconnected"),
|
||||||
|
null);
|
||||||
global.play_theme_sound(0, 'device-removed-media');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDriveEjectButton: function(monitor, drive) {
|
_onDriveEjectButton: function(monitor, drive) {
|
||||||
// TODO: this code path is not tested, as the GVfs volume monitor
|
// TODO: this code path is not tested, as the GVfs volume monitor
|
||||||
// doesn't emit this signal just yet.
|
// doesn't emit this signal just yet.
|
||||||
if (!this.isSessionActive())
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we force stop/eject in this case, so we don't have to pass a
|
// we force stop/eject in this case, so we don't have to pass a
|
||||||
@ -227,15 +145,8 @@ const AutomountManager = new Lang.Class({
|
|||||||
if (params.checkSession) {
|
if (params.checkSession) {
|
||||||
// if we're not in the current ConsoleKit session,
|
// if we're not in the current ConsoleKit session,
|
||||||
// don't attempt automount
|
// don't attempt automount
|
||||||
if (!this.isSessionActive())
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._ssProxy.screenSaverActive) {
|
|
||||||
if (this._volumeQueue.indexOf(volume) == -1)
|
|
||||||
this._volumeQueue.push(volume);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._inhibited)
|
if (this._inhibited)
|
||||||
@ -329,3 +240,4 @@ const AutomountManager = new Lang.Class({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const Component = AutomountManager;
|
@ -4,6 +4,7 @@ const Lang = imports.lang;
|
|||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
@ -43,6 +44,17 @@ function isMountRootHidden(root) {
|
|||||||
return (path.indexOf('/.') != -1);
|
return (path.indexOf('/.') != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMountNonLocal(mount) {
|
||||||
|
// If the mount doesn't have an associated volume, that means it's
|
||||||
|
// an uninteresting filesystem. Most devices that we care about will
|
||||||
|
// have a mount, like media players and USB sticks.
|
||||||
|
let volume = mount.get_volume();
|
||||||
|
if (volume == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return (volume.get_identifier("class") == "network");
|
||||||
|
}
|
||||||
|
|
||||||
function startAppForMount(app, mount) {
|
function startAppForMount(app, mount) {
|
||||||
let files = [];
|
let files = [];
|
||||||
let root = mount.get_root();
|
let root = mount.get_root();
|
||||||
@ -82,13 +94,21 @@ const ContentTypeDiscoverer = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(callback) {
|
_init: function(callback) {
|
||||||
this._callback = callback;
|
this._callback = callback;
|
||||||
|
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||||
},
|
},
|
||||||
|
|
||||||
guessContentTypes: function(mount) {
|
guessContentTypes: function(mount) {
|
||||||
// guess mount's content types using GIO
|
let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
|
||||||
mount.guess_content_type(false, null,
|
let shouldScan = autorunEnabled && !isMountNonLocal(mount);
|
||||||
Lang.bind(this,
|
|
||||||
this._onContentTypeGuessed));
|
if (shouldScan) {
|
||||||
|
// guess mount's content types using GIO
|
||||||
|
mount.guess_content_type(false, null,
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onContentTypeGuessed));
|
||||||
|
} else {
|
||||||
|
this._emitCallback(mount, []);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onContentTypeGuessed: function(mount, res) {
|
_onContentTypeGuessed: function(mount, res) {
|
||||||
@ -142,55 +162,68 @@ const AutorunManager = new Lang.Class({
|
|||||||
Name: 'AutorunManager',
|
Name: 'AutorunManager',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this._session = new GnomeSession.SessionManager();
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
this._volumeMonitor.connect('mount-added',
|
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||||
Lang.bind(this,
|
},
|
||||||
this._onMountAdded));
|
|
||||||
this._volumeMonitor.connect('mount-removed',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._onMountRemoved));
|
|
||||||
|
|
||||||
this._transDispatcher = new AutorunTransientDispatcher();
|
_ensureResidentSource: function() {
|
||||||
this._createResidentSource();
|
if (this._residentSource)
|
||||||
|
return;
|
||||||
|
|
||||||
let mounts = this._volumeMonitor.get_mounts();
|
this._residentSource = new AutorunResidentSource(this);
|
||||||
|
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
|
||||||
mounts.forEach(Lang.bind(this, function (mount) {
|
this._residentSource.disconnect(destroyId);
|
||||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
this._residentSource = null;
|
||||||
function (mount, apps) {
|
|
||||||
this._residentSource.addMount(mount, apps);
|
|
||||||
}));
|
|
||||||
|
|
||||||
discoverer.guessContentTypes(mount);
|
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_createResidentSource: function() {
|
enable: function() {
|
||||||
this._residentSource = new AutorunResidentSource();
|
this._scanMounts();
|
||||||
this._residentSource.connect('destroy',
|
|
||||||
Lang.bind(this,
|
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
|
||||||
this._createResidentSource));
|
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
if (this._residentSource)
|
||||||
|
this._residentSource.destroy();
|
||||||
|
this._volumeMonitor.disconnect(this._mountAddedId);
|
||||||
|
this._volumeMonitor.disconnect(this._mountRemovedId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_processMount: function(mount, hotplug) {
|
||||||
|
let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
|
||||||
|
this._ensureResidentSource();
|
||||||
|
this._residentSource.addMount(mount, apps);
|
||||||
|
|
||||||
|
if (hotplug)
|
||||||
|
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||||
|
}));
|
||||||
|
discoverer.guessContentTypes(mount);
|
||||||
|
},
|
||||||
|
|
||||||
|
_scanMounts: function() {
|
||||||
|
let mounts = this._volumeMonitor.get_mounts();
|
||||||
|
mounts.forEach(Lang.bind(this, function(mount) {
|
||||||
|
this._processMount(mount, false);
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMountAdded: function(monitor, mount) {
|
_onMountAdded: function(monitor, mount) {
|
||||||
// don't do anything if our session is not the currently
|
// don't do anything if our session is not the currently
|
||||||
// active one
|
// active one
|
||||||
if (!Main.automountManager.isSessionActive())
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
this._processMount(mount, true);
|
||||||
function (mount, apps, contentTypes) {
|
|
||||||
this._transDispatcher.addMount(mount, apps, contentTypes);
|
|
||||||
this._residentSource.addMount(mount, apps);
|
|
||||||
}));
|
|
||||||
|
|
||||||
discoverer.guessContentTypes(mount);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMountRemoved: function(monitor, mount) {
|
_onMountRemoved: function(monitor, mount) {
|
||||||
this._transDispatcher.removeMount(mount);
|
this._transDispatcher.removeMount(mount);
|
||||||
this._residentSource.removeMount(mount);
|
if (this._residentSource)
|
||||||
|
this._residentSource.removeMount(mount);
|
||||||
},
|
},
|
||||||
|
|
||||||
ejectMount: function(mount) {
|
ejectMount: function(mount) {
|
||||||
@ -257,12 +290,22 @@ const AutorunResidentSource = new Lang.Class({
|
|||||||
Name: 'AutorunResidentSource',
|
Name: 'AutorunResidentSource',
|
||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function() {
|
_init: function(manager) {
|
||||||
this.parent(_("Removable Devices"), 'media-removable', St.IconType.FULLCOLOR);
|
this.parent(_("Removable Devices"), 'media-removable');
|
||||||
|
this.resident = true;
|
||||||
|
|
||||||
this._mounts = [];
|
this._mounts = [];
|
||||||
|
|
||||||
this._notification = new AutorunResidentNotification(this);
|
this._manager = manager;
|
||||||
|
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
buildRightClickMenu: function() {
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
addMount: function(mount, apps) {
|
addMount: function(mount, apps) {
|
||||||
@ -312,7 +355,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
Name: 'AutorunResidentNotification',
|
Name: 'AutorunResidentNotification',
|
||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(manager, source) {
|
||||||
this.parent(source, source.title, null, { customContent: true });
|
this.parent(source, source.title, null, { customContent: true });
|
||||||
|
|
||||||
// set the notification as resident
|
// set the notification as resident
|
||||||
@ -320,6 +363,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
|
|
||||||
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
|
this._manager = manager;
|
||||||
|
|
||||||
this.addActor(this._layout,
|
this.addActor(this._layout,
|
||||||
{ x_expand: true,
|
{ x_expand: true,
|
||||||
@ -367,7 +411,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
expand: true });
|
expand: true });
|
||||||
|
|
||||||
let ejectIcon =
|
let ejectIcon =
|
||||||
new St.Icon({ icon_name: 'media-eject',
|
new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||||
style_class: 'hotplug-resident-eject-icon' });
|
style_class: 'hotplug-resident-eject-icon' });
|
||||||
|
|
||||||
let ejectButton =
|
let ejectButton =
|
||||||
@ -382,7 +426,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
ejectButton.connect('clicked', Lang.bind(this, function() {
|
ejectButton.connect('clicked', Lang.bind(this, function() {
|
||||||
Main.autorunManager.ejectMount(mount);
|
this._manager.ejectMount(mount);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@ -392,7 +436,8 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
const AutorunTransientDispatcher = new Lang.Class({
|
const AutorunTransientDispatcher = new Lang.Class({
|
||||||
Name: 'AutorunTransientDispatcher',
|
Name: 'AutorunTransientDispatcher',
|
||||||
|
|
||||||
_init: function() {
|
_init: function(manager) {
|
||||||
|
this._manager = manager;
|
||||||
this._sources = [];
|
this._sources = [];
|
||||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||||
},
|
},
|
||||||
@ -435,7 +480,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// add a new source
|
// add a new source
|
||||||
this._sources.push(new AutorunTransientSource(mount, apps));
|
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
|
||||||
},
|
},
|
||||||
|
|
||||||
addMount: function(mount, apps, contentTypes) {
|
addMount: function(mount, apps, contentTypes) {
|
||||||
@ -488,23 +533,22 @@ const AutorunTransientSource = new Lang.Class({
|
|||||||
Name: 'AutorunTransientSource',
|
Name: 'AutorunTransientSource',
|
||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(mount, apps) {
|
_init: function(manager, mount, apps) {
|
||||||
|
this._manager = manager;
|
||||||
this.mount = mount;
|
this.mount = mount;
|
||||||
this.apps = apps;
|
this.apps = apps;
|
||||||
|
|
||||||
this.parent(mount.get_name());
|
this.parent(mount.get_name());
|
||||||
|
|
||||||
this._notification = new AutorunTransientNotification(this);
|
this._notification = new AutorunTransientNotification(this._manager, this);
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
|
||||||
|
|
||||||
// add ourselves as a source, and popup the notification
|
// add ourselves as a source, and popup the notification
|
||||||
Main.messageTray.add(this);
|
Main.messageTray.add(this);
|
||||||
this.notify(this._notification);
|
this.notify(this._notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
getIcon: function() {
|
||||||
return new St.Icon({ gicon: this.mount.get_icon(),
|
return this.mount.get_icon();
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -512,9 +556,10 @@ const AutorunTransientNotification = new Lang.Class({
|
|||||||
Name: 'AutorunTransientNotification',
|
Name: 'AutorunTransientNotification',
|
||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(manager, source) {
|
||||||
this.parent(source, source.title, null, { customContent: true });
|
this.parent(source, source.title, null, { customContent: true });
|
||||||
|
|
||||||
|
this._manager = manager;
|
||||||
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this.addActor(this._box);
|
this.addActor(this._box);
|
||||||
@ -566,7 +611,7 @@ const AutorunTransientNotification = new Lang.Class({
|
|||||||
|
|
||||||
_buttonForEject: function() {
|
_buttonForEject: function() {
|
||||||
let box = new St.BoxLayout();
|
let box = new St.BoxLayout();
|
||||||
let icon = new St.Icon({ icon_name: 'media-eject',
|
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||||
style_class: 'hotplug-notification-item-icon' });
|
style_class: 'hotplug-notification-item-icon' });
|
||||||
box.add(icon);
|
box.add(icon);
|
||||||
|
|
||||||
@ -583,10 +628,11 @@ const AutorunTransientNotification = new Lang.Class({
|
|||||||
style_class: 'hotplug-notification-item' });
|
style_class: 'hotplug-notification-item' });
|
||||||
|
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
Main.autorunManager.ejectMount(this._mount);
|
this._manager.ejectMount(this._mount);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Component = AutorunManager;
|
@ -25,7 +25,7 @@ const KeyringDialog = new Lang.Class({
|
|||||||
this.prompt = new Shell.KeyringPrompt();
|
this.prompt = new Shell.KeyringPrompt();
|
||||||
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
||||||
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
||||||
this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
|
this.prompt.connect('prompt-close', Lang.bind(this, this._onHidePrompt));
|
||||||
|
|
||||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
@ -60,17 +60,20 @@ const KeyringDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._controlTable = null;
|
this._controlTable = null;
|
||||||
|
|
||||||
let buttons = [{ label: '',
|
|
||||||
action: Lang.bind(this, this._onCancelButton),
|
|
||||||
key: Clutter.Escape
|
|
||||||
},
|
|
||||||
{ label: '',
|
|
||||||
action: Lang.bind(this, this._onContinueButton)
|
|
||||||
}]
|
|
||||||
|
|
||||||
this.setButtons(buttons);
|
this._cancelButton = this.addButton({ label: '',
|
||||||
this._cancelButton = buttons[0].button;
|
action: Lang.bind(this, this._onCancelButton),
|
||||||
this._continueButton = buttons[1].button;
|
key: Clutter.Escape },
|
||||||
|
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
this.placeSpinner({ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this._continueButton = this.addButton({ label: '',
|
||||||
|
action: Lang.bind(this, this._onContinueButton),
|
||||||
|
default: true },
|
||||||
|
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||||
|
|
||||||
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||||
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||||
@ -83,7 +86,10 @@ const KeyringDialog = new Lang.Class({
|
|||||||
if (this.prompt.password_visible) {
|
if (this.prompt.password_visible) {
|
||||||
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
label.set_text(_("Password:"));
|
label.set_text(_("Password:"));
|
||||||
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
|
table.add(label, { row: row, col: 0,
|
||||||
|
x_expand: false, x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: '',
|
text: '',
|
||||||
can_focus: true});
|
can_focus: true});
|
||||||
@ -99,7 +105,10 @@ const KeyringDialog = new Lang.Class({
|
|||||||
if (this.prompt.confirm_visible) {
|
if (this.prompt.confirm_visible) {
|
||||||
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
label.set_text(_("Type again:"));
|
label.set_text(_("Type again:"));
|
||||||
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
|
table.add(label, { row: row, col: 0,
|
||||||
|
x_expand: false, x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: '',
|
text: '',
|
||||||
can_focus: true});
|
can_focus: true});
|
||||||
@ -139,6 +148,22 @@ const KeyringDialog = new Lang.Class({
|
|||||||
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function(sensitive) {
|
||||||
|
if (this._passwordEntry) {
|
||||||
|
this._passwordEntry.reactive = sensitive;
|
||||||
|
this._passwordEntry.clutter_text.editable = sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._confirmEntry) {
|
||||||
|
this._confirmEntry.reactive = sensitive;
|
||||||
|
this._confirmEntry.clutter_text.editable = sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._continueButton.can_focus = sensitive;
|
||||||
|
this._continueButton.reactive = sensitive;
|
||||||
|
this.setWorking(!sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
_ensureOpen: function() {
|
_ensureOpen: function() {
|
||||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||||
// already open - it just returns true without side-effects
|
// already open - it just returns true without side-effects
|
||||||
@ -160,12 +185,14 @@ const KeyringDialog = new Lang.Class({
|
|||||||
_onShowPassword: function(prompt) {
|
_onShowPassword: function(prompt) {
|
||||||
this._buildControlTable();
|
this._buildControlTable();
|
||||||
this._ensureOpen();
|
this._ensureOpen();
|
||||||
|
this._updateSensitivity(true);
|
||||||
this._passwordEntry.grab_key_focus();
|
this._passwordEntry.grab_key_focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onShowConfirm: function(prompt) {
|
_onShowConfirm: function(prompt) {
|
||||||
this._buildControlTable();
|
this._buildControlTable();
|
||||||
this._ensureOpen();
|
this._ensureOpen();
|
||||||
|
this._updateSensitivity(true);
|
||||||
this._continueButton.grab_key_focus();
|
this._continueButton.grab_key_focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -185,23 +212,37 @@ const KeyringDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onContinueButton: function() {
|
_onContinueButton: function() {
|
||||||
this.prompt.complete()
|
this._updateSensitivity(false);
|
||||||
|
this.prompt.complete();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancelButton: function() {
|
_onCancelButton: function() {
|
||||||
this.prompt.cancel()
|
this.prompt.cancel();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function init() {
|
const KeyringPrompter = new Lang.Class({
|
||||||
prompter = new Gcr.SystemPrompter();
|
Name: 'KeyringPrompter',
|
||||||
prompter.connect('new-prompt', function(prompter) {
|
|
||||||
let dialog = new KeyringDialog();
|
|
||||||
return dialog.prompt;
|
|
||||||
});
|
|
||||||
|
|
||||||
let connection = Gio.DBus.session;
|
_init: function() {
|
||||||
prompter.register(connection);
|
this._prompter = new Gcr.SystemPrompter();
|
||||||
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
|
this._prompter.connect('new-prompt', function(prompter) {
|
||||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
let dialog = new KeyringDialog();
|
||||||
}
|
return dialog.prompt;
|
||||||
|
});
|
||||||
|
this._dbusId = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
this._prompter.register(Gio.DBus.session);
|
||||||
|
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||||
|
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._prompter.unregister(false);
|
||||||
|
Gio.DBus.session.unown_name(this._dbusId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Component = KeyringPrompter;
|
@ -1,23 +1,4 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
/*
|
|
||||||
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
@ -135,7 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
} else
|
} else
|
||||||
secret.valid = true;
|
secret.valid = true;
|
||||||
|
|
||||||
secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START });
|
secretTable.add(label, { row: pos, col: 0,
|
||||||
|
x_expand: false, x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
||||||
pos++;
|
pos++;
|
||||||
|
|
||||||
@ -147,7 +131,7 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._okButton = { label: _("Connect"),
|
this._okButton = { label: _("Connect"),
|
||||||
action: Lang.bind(this, this._onOk),
|
action: Lang.bind(this, this._onOk),
|
||||||
key: Clutter.KEY_Return,
|
default: true
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setButtons([{ label: _("Cancel"),
|
this.setButtons([{ label: _("Cancel"),
|
||||||
@ -166,10 +150,6 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._okButton.button.reactive = valid;
|
this._okButton.button.reactive = valid;
|
||||||
this._okButton.button.can_focus = valid;
|
this._okButton.button.can_focus = valid;
|
||||||
if (valid)
|
|
||||||
this._okButton.button.remove_style_pseudo_class('disabled');
|
|
||||||
else
|
|
||||||
this._okButton.button.add_style_pseudo_class('disabled');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOk: function() {
|
_onOk: function() {
|
||||||
@ -428,7 +408,10 @@ const VPNRequestHandler = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
cancel: function() {
|
cancel: function(respond) {
|
||||||
|
if (respond)
|
||||||
|
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||||
|
|
||||||
if (this._newStylePlugin && this._shellDialog) {
|
if (this._newStylePlugin && this._shellDialog) {
|
||||||
this._shellDialog.close(global.get_current_time());
|
this._shellDialog.close(global.get_current_time());
|
||||||
this._shellDialog.destroy();
|
this._shellDialog.destroy();
|
||||||
@ -604,17 +587,41 @@ const NetworkAgent = new Lang.Class({
|
|||||||
Name: 'NetworkAgent',
|
Name: 'NetworkAgent',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._native = new Shell.NetworkAgent({ auto_register: true,
|
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
|
||||||
|
|
||||||
this._dialogs = { };
|
this._dialogs = { };
|
||||||
this._vpnRequests = { };
|
this._vpnRequests = { };
|
||||||
|
|
||||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||||
|
|
||||||
|
this._enabled = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
this._enabled = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
let requestId;
|
||||||
|
|
||||||
|
for (requestId in this._dialogs)
|
||||||
|
this._dialogs[requestId].cancel();
|
||||||
|
this._dialogs = { };
|
||||||
|
|
||||||
|
for (requestId in this._vpnRequests)
|
||||||
|
this._vpnRequests[requestId].cancel(true);
|
||||||
|
this._vpnRequests = { };
|
||||||
|
|
||||||
|
this._enabled = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||||
|
if (!this._enabled) {
|
||||||
|
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (settingName == 'vpn') {
|
if (settingName == 'vpn') {
|
||||||
this._vpnRequest(requestId, connection, hints, flags);
|
this._vpnRequest(requestId, connection, hints, flags);
|
||||||
return;
|
return;
|
||||||
@ -634,7 +641,7 @@ const NetworkAgent = new Lang.Class({
|
|||||||
this._dialogs[requestId].destroy();
|
this._dialogs[requestId].destroy();
|
||||||
delete this._dialogs[requestId];
|
delete this._dialogs[requestId];
|
||||||
} else if (this._vpnRequests[requestId]) {
|
} else if (this._vpnRequests[requestId]) {
|
||||||
this._vpnRequests[requestId].cancel();
|
this._vpnRequests[requestId].cancel(false);
|
||||||
delete this._vpnRequests[requestId];
|
delete this._vpnRequests[requestId];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -703,3 +710,4 @@ const NetworkAgent = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const Component = NetworkAgent;
|
@ -1,24 +1,4 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
/*
|
|
||||||
* Copyright 2010 Red Hat, Inc
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* Author: David Zeuthen <davidz@redhat.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
@ -33,8 +13,12 @@ const Mainloop = imports.mainloop;
|
|||||||
const Polkit = imports.gi.Polkit;
|
const Polkit = imports.gi.Polkit;
|
||||||
const PolkitAgent = imports.gi.PolkitAgent;
|
const PolkitAgent = imports.gi.PolkitAgent;
|
||||||
|
|
||||||
|
const Components = imports.ui.components;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const ShellEntry = imports.ui.shellEntry;
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
|
const DIALOG_ICON_SIZE = 48;
|
||||||
|
|
||||||
const AuthenticationDialog = new Lang.Class({
|
const AuthenticationDialog = new Lang.Class({
|
||||||
Name: 'AuthenticationDialog',
|
Name: 'AuthenticationDialog',
|
||||||
@ -47,7 +31,6 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
this.userNames = userNames;
|
this.userNames = userNames;
|
||||||
this._wasDismissed = false;
|
this._wasDismissed = false;
|
||||||
this._completed = false;
|
|
||||||
|
|
||||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
@ -65,7 +48,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
mainContentBox.add(messageBox,
|
mainContentBox.add(messageBox,
|
||||||
{ y_align: St.Align.START });
|
{ expand: true, y_align: St.Align.START });
|
||||||
|
|
||||||
this._subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
|
this._subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
|
||||||
text: _("Authentication Required") });
|
text: _("Authentication Required") });
|
||||||
@ -117,9 +100,11 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
messageBox.add(userBox);
|
messageBox.add(userBox);
|
||||||
this._userIcon = new St.Icon();
|
this._userAvatar = new UserWidget.Avatar(this._user,
|
||||||
this._userIcon.hide();
|
{ iconSize: DIALOG_ICON_SIZE,
|
||||||
userBox.add(this._userIcon,
|
styleClass: 'polkit-dialog-user-icon' });
|
||||||
|
this._userAvatar.actor.hide();
|
||||||
|
userBox.add(this._userAvatar.actor,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
x_align: St.Align.END,
|
x_align: St.Align.END,
|
||||||
@ -135,17 +120,17 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._onUserChanged();
|
this._onUserChanged();
|
||||||
|
|
||||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
|
||||||
messageBox.add(this._passwordBox);
|
messageBox.add(this._passwordBox);
|
||||||
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
this._passwordBox.add(this._passwordLabel);
|
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: "",
|
text: "",
|
||||||
can_focus: true});
|
can_focus: true});
|
||||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||||
this._passwordBox.add(this._passwordEntry,
|
this._passwordBox.add(this._passwordEntry,
|
||||||
{expand: true });
|
{ expand: true });
|
||||||
this.setInitialKeyFocus(this._passwordEntry);
|
this.setInitialKeyFocus(this._passwordEntry);
|
||||||
this._passwordBox.hide();
|
this._passwordBox.hide();
|
||||||
|
|
||||||
@ -173,28 +158,34 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
messageBox.add(this._nullMessageLabel);
|
messageBox.add(this._nullMessageLabel);
|
||||||
this._nullMessageLabel.show();
|
this._nullMessageLabel.show();
|
||||||
|
|
||||||
this.setButtons([{ label: _("Cancel"),
|
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||||
action: Lang.bind(this, this.cancel),
|
action: Lang.bind(this, this.cancel),
|
||||||
key: Clutter.Escape
|
key: Clutter.Escape },
|
||||||
},
|
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
{ label: _("Authenticate"),
|
this.placeSpinner({ expand: false,
|
||||||
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
x_fill: false,
|
||||||
}]);
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||||
|
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||||
|
default: true },
|
||||||
|
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||||
|
|
||||||
this._doneEmitted = false;
|
this._doneEmitted = false;
|
||||||
|
|
||||||
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
||||||
this._cookie = cookie;
|
this._cookie = cookie;
|
||||||
|
},
|
||||||
|
|
||||||
|
performAuthentication: function() {
|
||||||
|
this.destroySession();
|
||||||
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||||
cookie: this._cookie });
|
cookie: this._cookie });
|
||||||
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
||||||
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
||||||
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
||||||
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
||||||
},
|
|
||||||
|
|
||||||
startAuthentication: function() {
|
|
||||||
this._session.initiate();
|
this._session.initiate();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -216,19 +207,29 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
||||||
' Dismissing authentication request for action-id ' + this.actionId +
|
' Dismissing authentication request for action-id ' + this.actionId +
|
||||||
' cookie ' + this._cookie);
|
' cookie ' + this._cookie);
|
||||||
this._emitDone(false, true);
|
this._emitDone(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_emitDone: function(keepVisible, dismissed) {
|
_emitDone: function(dismissed) {
|
||||||
if (!this._doneEmitted) {
|
if (!this._doneEmitted) {
|
||||||
this._doneEmitted = true;
|
this._doneEmitted = true;
|
||||||
this.emit('done', keepVisible, dismissed);
|
this.emit('done', dismissed);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function(sensitive) {
|
||||||
|
this._passwordEntry.reactive = sensitive;
|
||||||
|
this._passwordEntry.clutter_text.editable = sensitive;
|
||||||
|
|
||||||
|
this._okButton.can_focus = sensitive;
|
||||||
|
this._okButton.reactive = sensitive;
|
||||||
|
this.setWorking(!sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
_onEntryActivate: function() {
|
_onEntryActivate: function() {
|
||||||
let response = this._passwordEntry.get_text();
|
let response = this._passwordEntry.get_text();
|
||||||
|
this._updateSensitivity(false);
|
||||||
this._session.response(response);
|
this._session.response(response);
|
||||||
// When the user responds, dismiss already shown info and
|
// When the user responds, dismiss already shown info and
|
||||||
// error texts (if any)
|
// error texts (if any)
|
||||||
@ -242,12 +243,16 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onSessionCompleted: function(session, gainedAuthorization) {
|
_onSessionCompleted: function(session, gainedAuthorization) {
|
||||||
if (this._completed)
|
if (this._completed || this._doneEmitted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._completed = true;
|
this._completed = true;
|
||||||
|
|
||||||
if (!gainedAuthorization) {
|
/* Yay, all done */
|
||||||
|
if (gainedAuthorization) {
|
||||||
|
this._emitDone(false);
|
||||||
|
|
||||||
|
} else {
|
||||||
/* Unless we are showing an existing error message from the PAM
|
/* Unless we are showing an existing error message from the PAM
|
||||||
* module (the PAM module could be reporting the authentication
|
* module (the PAM module could be reporting the authentication
|
||||||
* error providing authentication-method specific information),
|
* error providing authentication-method specific information),
|
||||||
@ -263,8 +268,10 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._infoMessageLabel.hide();
|
this._infoMessageLabel.hide();
|
||||||
this._nullMessageLabel.hide();
|
this._nullMessageLabel.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try and authenticate again */
|
||||||
|
this.performAuthentication();
|
||||||
}
|
}
|
||||||
this._emitDone(!gainedAuthorization, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSessionRequest: function(session, request, echo_on) {
|
_onSessionRequest: function(session, request, echo_on) {
|
||||||
@ -282,6 +289,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._passwordBox.show();
|
this._passwordBox.show();
|
||||||
this._passwordEntry.set_text('');
|
this._passwordEntry.set_text('');
|
||||||
this._passwordEntry.grab_key_focus();
|
this._passwordEntry.grab_key_focus();
|
||||||
|
this._updateSensitivity(true);
|
||||||
this._ensureOpen();
|
this._ensureOpen();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -307,31 +315,22 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
if (this._session) {
|
if (this._session) {
|
||||||
if (!this._completed)
|
if (!this._completed)
|
||||||
this._session.cancel();
|
this._session.cancel();
|
||||||
|
this._completed = false;
|
||||||
this._session = null;
|
this._session = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onUserChanged: function() {
|
_onUserChanged: function() {
|
||||||
if (this._user.is_loaded) {
|
if (this._user.is_loaded && this._userAvatar) {
|
||||||
if (this._userIcon) {
|
this._userAvatar.update();
|
||||||
let iconFileName = this._user.get_icon_file();
|
this._userAvatar.actor.show();
|
||||||
let iconFile = Gio.file_new_for_path(iconFileName);
|
|
||||||
let icon;
|
|
||||||
if (iconFile.query_exists(null)) {
|
|
||||||
icon = new Gio.FileIcon({file: iconFile});
|
|
||||||
} else {
|
|
||||||
icon = new Gio.ThemedIcon({name: 'avatar-default'});
|
|
||||||
}
|
|
||||||
this._userIcon.set_gicon (icon);
|
|
||||||
this._userIcon.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
this._wasDismissed = true;
|
this._wasDismissed = true;
|
||||||
this.close(global.get_current_time());
|
this.close(global.get_current_time());
|
||||||
this._emitDone(false, true);
|
this._emitDone(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
||||||
@ -340,11 +339,27 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
Name: 'AuthenticationAgent',
|
Name: 'AuthenticationAgent',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this._currentDialog = null;
|
||||||
|
this._handle = null;
|
||||||
this._native = new Shell.PolkitAuthenticationAgent();
|
this._native = new Shell.PolkitAuthenticationAgent();
|
||||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||||
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||||
this._currentDialog = null;
|
},
|
||||||
this._isCompleting = false;
|
|
||||||
|
enable: function() {
|
||||||
|
try {
|
||||||
|
this._native.register();
|
||||||
|
} catch(e) {
|
||||||
|
log('Failed to register AuthenticationAgent');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
try {
|
||||||
|
this._native.unregister();
|
||||||
|
} catch(e) {
|
||||||
|
log('Failed to unregister AuthenticationAgent');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||||
@ -361,47 +376,24 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
// discussion.
|
// discussion.
|
||||||
|
|
||||||
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
||||||
this._currentDialog.startAuthentication();
|
this._currentDialog.performAuthentication();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancel: function(nativeAgent) {
|
_onCancel: function(nativeAgent) {
|
||||||
this._completeRequest(false, false);
|
this._completeRequest(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDialogDone: function(dialog, keepVisible, dismissed) {
|
_onDialogDone: function(dialog, dismissed) {
|
||||||
this._completeRequest(keepVisible, dismissed);
|
this._completeRequest(dismissed);
|
||||||
},
|
},
|
||||||
|
|
||||||
_reallyCompleteRequest: function(dismissed) {
|
_completeRequest: function(dismissed) {
|
||||||
this._currentDialog.close();
|
this._currentDialog.close();
|
||||||
this._currentDialog.destroySession();
|
this._currentDialog.destroySession();
|
||||||
this._currentDialog = null;
|
this._currentDialog = null;
|
||||||
this._isCompleting = false;
|
|
||||||
|
|
||||||
this._native.complete(dismissed)
|
this._native.complete(dismissed);
|
||||||
},
|
},
|
||||||
|
|
||||||
_completeRequest: function(keepVisible, wasDismissed) {
|
|
||||||
if (this._isCompleting)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._isCompleting = true;
|
|
||||||
|
|
||||||
if (keepVisible) {
|
|
||||||
// Give the user 2 seconds to read 'Authentication Failure' before
|
|
||||||
// dismissing the dialog
|
|
||||||
Mainloop.timeout_add(2000,
|
|
||||||
Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._reallyCompleteRequest(wasDismissed);
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._reallyCompleteRequest(wasDismissed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function init() {
|
const Component = AuthenticationAgent;
|
||||||
let agent = new AuthenticationAgent();
|
|
||||||
}
|
|
61
js/ui/components/recorder.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Recorder = new Lang.Class({
|
||||||
|
Name: 'Recorder',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||||
|
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||||
|
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||||
|
this._recorder = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
Main.wm.addKeybinding('toggle-recording',
|
||||||
|
this._bindingSettings,
|
||||||
|
Meta.KeyBindingFlags.NONE,
|
||||||
|
Shell.KeyBindingMode.ALL,
|
||||||
|
Lang.bind(this, this._toggleRecorder));
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
Main.wm.removeKeybinding('toggle-recording');
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureRecorder: function() {
|
||||||
|
if (this._recorder == null)
|
||||||
|
this._recorder = new Shell.Recorder({ stage: global.stage });
|
||||||
|
return this._recorder;
|
||||||
|
},
|
||||||
|
|
||||||
|
_toggleRecorder: function() {
|
||||||
|
let recorder = this._ensureRecorder();
|
||||||
|
if (recorder.is_recording()) {
|
||||||
|
recorder.close();
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||||
|
// read the parameters from GSettings always in case they have changed
|
||||||
|
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
|
||||||
|
/* Translators: this is a filename used for screencast recording */
|
||||||
|
// xgettext:no-c-format
|
||||||
|
recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
|
||||||
|
let pipeline = this._recorderSettings.get_string('pipeline');
|
||||||
|
|
||||||
|
if (!pipeline.match(/^\s*$/))
|
||||||
|
recorder.set_pipeline(pipeline);
|
||||||
|
else
|
||||||
|
recorder.set_pipeline(null);
|
||||||
|
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
|
recorder.record();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Component = Recorder;
|
@ -11,13 +11,14 @@ const Tpl = imports.gi.TelepathyLogger;
|
|||||||
const Tp = imports.gi.TelepathyGLib;
|
const Tp = imports.gi.TelepathyGLib;
|
||||||
|
|
||||||
const History = imports.misc.history;
|
const History = imports.misc.history;
|
||||||
const Params = imports.misc.params;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
// See Notification.appendMessage
|
// See Notification.appendMessage
|
||||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
|
||||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
||||||
const SCROLLBACK_RECENT_LENGTH = 20;
|
const SCROLLBACK_RECENT_LENGTH = 20;
|
||||||
const SCROLLBACK_IDLE_LENGTH = 5;
|
const SCROLLBACK_IDLE_LENGTH = 5;
|
||||||
@ -63,10 +64,10 @@ function makeMessageFromTplEvent(event) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Client = new Lang.Class({
|
const TelepathyClient = new Lang.Class({
|
||||||
Name: 'Client',
|
Name: 'TelepathyClient',
|
||||||
|
|
||||||
_init : function() {
|
_init: function() {
|
||||||
// channel path -> ChatSource
|
// channel path -> ChatSource
|
||||||
this._chatSources = {};
|
this._chatSources = {};
|
||||||
this._chatState = Tp.ChannelChatState.ACTIVE;
|
this._chatState = Tp.ChannelChatState.ACTIVE;
|
||||||
@ -89,9 +90,9 @@ const Client = new Lang.Class({
|
|||||||
// channel matching its filters is detected.
|
// channel matching its filters is detected.
|
||||||
// The second argument, recover, means _observeChannels will be run
|
// The second argument, recover, means _observeChannels will be run
|
||||||
// for any existing channel as well.
|
// for any existing channel as well.
|
||||||
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
|
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
|
||||||
'name': 'GnomeShell',
|
account_manager: this._accountManager,
|
||||||
'uniquify-name': true })
|
uniquify_name: true });
|
||||||
this._tpClient.set_observe_channels_func(
|
this._tpClient.set_observe_channels_func(
|
||||||
Lang.bind(this, this._observeChannels));
|
Lang.bind(this, this._observeChannels));
|
||||||
this._tpClient.set_approve_channels_func(
|
this._tpClient.set_approve_channels_func(
|
||||||
@ -99,6 +100,10 @@ const Client = new Lang.Class({
|
|||||||
this._tpClient.set_handle_channels_func(
|
this._tpClient.set_handle_channels_func(
|
||||||
Lang.bind(this, this._handleChannels));
|
Lang.bind(this, this._handleChannels));
|
||||||
|
|
||||||
|
// Watch subscription requests and connection errors
|
||||||
|
this._subscriptionSource = null;
|
||||||
|
this._accountSource = null;
|
||||||
|
|
||||||
// Workaround for gjs not supporting GPtrArray in signals.
|
// Workaround for gjs not supporting GPtrArray in signals.
|
||||||
// See BGO bug #653941 for context.
|
// See BGO bug #653941 for context.
|
||||||
this._tpClient.set_contact_list_changed_func(
|
this._tpClient.set_contact_list_changed_func(
|
||||||
@ -108,21 +113,26 @@ const Client = new Lang.Class({
|
|||||||
// needed
|
// needed
|
||||||
this._tpClient.set_delegated_channels_callback(
|
this._tpClient.set_delegated_channels_callback(
|
||||||
Lang.bind(this, this._delegatedChannelsCb));
|
Lang.bind(this, this._delegatedChannelsCb));
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
try {
|
try {
|
||||||
this._tpClient.register();
|
this._tpClient.register();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch subscription requests and connection errors
|
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
|
||||||
this._subscriptionSource = null;
|
Lang.bind(this, this._accountValidityChanged));
|
||||||
this._accountSource = null;
|
|
||||||
|
|
||||||
this._accountManager.connect('account-validity-changed',
|
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
|
||||||
Lang.bind(this, this._accountValidityChanged));
|
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
|
||||||
|
},
|
||||||
|
|
||||||
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
|
disable: function() {
|
||||||
|
this._tpClient.unregister();
|
||||||
|
this._accountManager.disconnect(this._accountManagerValidityChangedId);
|
||||||
|
this._accountManagerValidityChangedId = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_observeChannels: function(observer, account, conn, channels,
|
_observeChannels: function(observer, account, conn, channels,
|
||||||
@ -132,7 +142,7 @@ const Client = new Lang.Class({
|
|||||||
let channel = channels[i];
|
let channel = channels[i];
|
||||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||||
|
|
||||||
if (Shell.is_channel_invalidated(channel))
|
if (channel.get_invalidated())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Only observe contact text channels */
|
/* Only observe contact text channels */
|
||||||
@ -184,7 +194,7 @@ const Client = new Lang.Class({
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Shell.is_channel_invalidated(channel))
|
if (channel.get_invalidated())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 'notify' will be true when coming from an actual HandleChannels
|
// 'notify' will be true when coming from an actual HandleChannels
|
||||||
@ -211,13 +221,15 @@ const Client = new Lang.Class({
|
|||||||
// We can only approve the rooms if we have been invited to it
|
// We can only approve the rooms if we have been invited to it
|
||||||
let selfContact = channel.group_get_self_contact();
|
let selfContact = channel.group_get_self_contact();
|
||||||
if (selfContact == null) {
|
if (selfContact == null) {
|
||||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Not invited to the room' }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
|
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
|
||||||
if (!invited) {
|
if (!invited) {
|
||||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Not invited to the room' }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,8 +249,9 @@ const Client = new Lang.Class({
|
|||||||
let channel = channels[0];
|
let channel = channels[0];
|
||||||
let chanType = channel.get_channel_type();
|
let chanType = channel.get_channel_type();
|
||||||
|
|
||||||
if (Shell.is_channel_invalidated(channel)) {
|
if (channel.get_invalidated()) {
|
||||||
Shell.decline_dispatch_op(context, 'Channel is invalidated');
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Channel is invalidated' }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +262,8 @@ const Client = new Lang.Class({
|
|||||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
||||||
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
||||||
else
|
else
|
||||||
Shell.decline_dispatch_op(context, 'Unsupported channel type');
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Unsupported channel type' }));
|
||||||
},
|
},
|
||||||
|
|
||||||
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
||||||
@ -370,26 +384,12 @@ const Client = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Display notification to ask user to accept/reject request */
|
/* Display notification to ask user to accept/reject request */
|
||||||
let source = this._ensureSubscriptionSource();
|
let source = this._ensureAppSource();
|
||||||
|
|
||||||
let notif = new SubscriptionRequestNotification(source, contact);
|
let notif = new SubscriptionRequestNotification(source, contact);
|
||||||
source.notify(notif);
|
source.notify(notif);
|
||||||
},
|
},
|
||||||
|
|
||||||
_ensureSubscriptionSource: function() {
|
|
||||||
if (this._subscriptionSource == null) {
|
|
||||||
this._subscriptionSource = new MessageTray.Source(_("Subscription request"),
|
|
||||||
'gtk-dialog-question',
|
|
||||||
St.IconType.FULLCOLOR);
|
|
||||||
Main.messageTray.add(this._subscriptionSource);
|
|
||||||
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
|
||||||
this._subscriptionSource = null;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._subscriptionSource;
|
|
||||||
},
|
|
||||||
|
|
||||||
_accountConnectionStatusNotifyCb: function(account) {
|
_accountConnectionStatusNotifyCb: function(account) {
|
||||||
let connectionError = account.connection_error;
|
let connectionError = account.connection_error;
|
||||||
|
|
||||||
@ -403,7 +403,7 @@ const Client = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Display notification that account failed to connect */
|
/* Display notification that account failed to connect */
|
||||||
let source = this._ensureAccountSource();
|
let source = this._ensureAppSource();
|
||||||
|
|
||||||
notif = new AccountNotification(source, account, connectionError);
|
notif = new AccountNotification(source, account, connectionError);
|
||||||
this._accountNotifications[account.get_object_path()] = notif;
|
this._accountNotifications[account.get_object_path()] = notif;
|
||||||
@ -413,18 +413,18 @@ const Client = new Lang.Class({
|
|||||||
source.notify(notif);
|
source.notify(notif);
|
||||||
},
|
},
|
||||||
|
|
||||||
_ensureAccountSource: function() {
|
_ensureAppSource: function() {
|
||||||
if (this._accountSource == null) {
|
if (this._appSource == null) {
|
||||||
this._accountSource = new MessageTray.Source(_("Connection error"),
|
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
||||||
'gtk-dialog-error',
|
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||||
St.IconType.FULLCOLOR);
|
|
||||||
Main.messageTray.add(this._accountSource);
|
Main.messageTray.add(this._appSource);
|
||||||
this._accountSource.connect('destroy', Lang.bind(this, function () {
|
this._appSource.connect('destroy', Lang.bind(this, function () {
|
||||||
this._accountSource = null;
|
this._appSource = null;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._accountSource;
|
return this._appSource;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -471,6 +471,26 @@ const ChatSource = new Lang.Class({
|
|||||||
this._getLogMessages();
|
this._getLogMessages();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildRightClickMenu: function() {
|
||||||
|
let item;
|
||||||
|
|
||||||
|
let rightClickMenu = this.parent();
|
||||||
|
item = new PopupMenu.PopupMenuItem('');
|
||||||
|
item.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||||
|
item.label.set_text(this.isMuted ? _("Unmute") : _("Mute"));
|
||||||
|
}));
|
||||||
|
item.connect('activate', Lang.bind(this, function() {
|
||||||
|
this.setMuted(!this.isMuted);
|
||||||
|
this.emit('done-displaying-content', false);
|
||||||
|
}));
|
||||||
|
rightClickMenu.add(item.actor);
|
||||||
|
return rightClickMenu;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||||
|
},
|
||||||
|
|
||||||
_updateAlias: function() {
|
_updateAlias: function() {
|
||||||
let oldAlias = this.title;
|
let oldAlias = this.title;
|
||||||
let newAlias = this._contact.get_alias();
|
let newAlias = this._contact.get_alias();
|
||||||
@ -482,64 +502,55 @@ const ChatSource = new Lang.Class({
|
|||||||
this._notification.appendAliasChange(oldAlias, newAlias);
|
this._notification.appendAliasChange(oldAlias, newAlias);
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
getIcon: function() {
|
||||||
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
|
||||||
this._iconBox._size = this.ICON_SIZE;
|
|
||||||
let textureCache = St.TextureCache.get_default();
|
|
||||||
let file = this._contact.get_avatar_file();
|
let file = this._contact.get_avatar_file();
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
let uri = file.get_uri();
|
return new Gio.FileIcon({ file: file });
|
||||||
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
|
|
||||||
} else {
|
} else {
|
||||||
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
return new Gio.ThemedIcon({ name: 'avatar-default' });
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: this._iconBox._size });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._iconBox;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
createSecondaryIcon: function() {
|
getSecondaryIcon: function() {
|
||||||
let iconBox = new St.Bin();
|
let iconName;
|
||||||
iconBox.child = new St.Icon({ style_class: 'secondary-icon',
|
|
||||||
icon_type: St.IconType.FULLCOLOR });
|
|
||||||
let presenceType = this._contact.get_presence_type();
|
let presenceType = this._contact.get_presence_type();
|
||||||
|
|
||||||
switch (presenceType) {
|
switch (presenceType) {
|
||||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||||
iconBox.child.icon_name = 'user-available';
|
iconName = 'user-available';
|
||||||
break;
|
break;
|
||||||
case Tp.ConnectionPresenceType.BUSY:
|
case Tp.ConnectionPresenceType.BUSY:
|
||||||
iconBox.child.icon_name = 'user-busy';
|
iconName = 'user-busy';
|
||||||
break;
|
break;
|
||||||
case Tp.ConnectionPresenceType.OFFLINE:
|
case Tp.ConnectionPresenceType.OFFLINE:
|
||||||
iconBox.child.icon_name = 'user-offline';
|
iconName = 'user-offline';
|
||||||
break;
|
break;
|
||||||
case Tp.ConnectionPresenceType.HIDDEN:
|
case Tp.ConnectionPresenceType.HIDDEN:
|
||||||
iconBox.child.icon_name = 'user-invisible';
|
iconName = 'user-invisible';
|
||||||
break;
|
break;
|
||||||
case Tp.ConnectionPresenceType.AWAY:
|
case Tp.ConnectionPresenceType.AWAY:
|
||||||
iconBox.child.icon_name = 'user-away';
|
iconName = 'user-away';
|
||||||
break;
|
break;
|
||||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||||
iconBox.child.icon_name = 'user-idle';
|
iconName = 'user-idle';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
iconBox.child.icon_name = 'user-offline';
|
iconName = 'user-offline';
|
||||||
}
|
}
|
||||||
return iconBox;
|
return new Gio.ThemedIcon({ name: iconName });
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateAvatarIcon: function() {
|
_updateAvatarIcon: function() {
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
this.iconUpdated();
|
||||||
this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
|
this._notification.update(this._notification.title, null, { customContent: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function(notification) {
|
||||||
if (this._client.is_handling_channel(this._channel)) {
|
if (this._client.is_handling_channel(this._channel)) {
|
||||||
// We are handling the channel, try to pass it to Empathy
|
// We are handling the channel, try to pass it to Empathy
|
||||||
this._client.delegate_channels_async([this._channel], global.get_current_time(), '', null);
|
this._client.delegate_channels_async([this._channel],
|
||||||
|
global.get_current_time(),
|
||||||
|
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We are not the handler, just ask to present the channel
|
// We are not the handler, just ask to present the channel
|
||||||
@ -578,7 +589,7 @@ const ChatSource = new Lang.Class({
|
|||||||
this._pendingMessages.push(message);
|
this._pendingMessages.push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateCount();
|
this.countUpdated();
|
||||||
|
|
||||||
let showTimestamp = false;
|
let showTimestamp = false;
|
||||||
|
|
||||||
@ -624,8 +635,21 @@ const ChatSource = new Lang.Class({
|
|||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateCount: function() {
|
/* All messages are new messages for Telepathy sources */
|
||||||
this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0);
|
get count() {
|
||||||
|
return this._pendingMessages.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
get indicatorCount() {
|
||||||
|
return this.count;
|
||||||
|
},
|
||||||
|
|
||||||
|
get unseenCount() {
|
||||||
|
return this.count;
|
||||||
|
},
|
||||||
|
|
||||||
|
get countVisible() {
|
||||||
|
return this.count > 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_messageReceived: function(channel, message) {
|
_messageReceived: function(channel, message) {
|
||||||
@ -633,7 +657,7 @@ const ChatSource = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._pendingMessages.push(message);
|
this._pendingMessages.push(message);
|
||||||
this._updateCount();
|
this.countUpdated();
|
||||||
|
|
||||||
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
||||||
this._notification.appendMessage(message);
|
this._notification.appendMessage(message);
|
||||||
@ -699,7 +723,7 @@ const ChatSource = new Lang.Class({
|
|||||||
|
|
||||||
title = GLib.markup_escape_text(this.title, -1);
|
title = GLib.markup_escape_text(this.title, -1);
|
||||||
|
|
||||||
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() });
|
this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
|
||||||
|
|
||||||
if (message)
|
if (message)
|
||||||
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
||||||
@ -710,17 +734,14 @@ const ChatSource = new Lang.Class({
|
|||||||
|
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
this._pendingMessages.splice(idx, 1);
|
this._pendingMessages.splice(idx, 1);
|
||||||
this._updateCount();
|
this.countUpdated();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw new Error('Message not in our pending list: ' + message);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ackMessages: function() {
|
_ackMessages: function() {
|
||||||
// Don't clear our messages here, tp-glib will send a
|
// Don't clear our messages here, tp-glib will send a
|
||||||
// 'pending-message-removed' for each one.
|
// 'pending-message-removed' for each one.
|
||||||
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
|
this._channel.ack_all_pending_messages_async(null);
|
||||||
this._channel.ack_all_pending_messages_finish(result);}));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -729,7 +750,7 @@ const ChatNotification = new Lang.Class({
|
|||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(source) {
|
||||||
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() });
|
this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() });
|
||||||
this.setResident(true);
|
this.setResident(true);
|
||||||
|
|
||||||
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
||||||
@ -738,16 +759,27 @@ const ChatNotification = new Lang.Class({
|
|||||||
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
|
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
|
||||||
this.setActionArea(this._responseEntry);
|
this.setActionArea(this._responseEntry);
|
||||||
|
|
||||||
this._oldMaxScrollAdjustment = 0;
|
this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
|
||||||
|
this.focused = true;
|
||||||
|
}));
|
||||||
|
this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
|
||||||
|
this.focused = false;
|
||||||
|
this.emit('unfocused');
|
||||||
|
}));
|
||||||
|
|
||||||
this._createScrollArea();
|
this._createScrollArea();
|
||||||
this._lastGroup = null;
|
this._lastGroup = null;
|
||||||
this._lastGroupActor = 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
|
||||||
|
// bottom
|
||||||
|
this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
|
||||||
|
this._scrollArea.add_style_class_name('chat-notification-scrollview');
|
||||||
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
|
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
|
||||||
let currentValue = adjustment.value + adjustment.page_size;
|
if (adjustment.value == this._oldMaxScrollValue)
|
||||||
if (currentValue == this._oldMaxScrollAdjustment)
|
|
||||||
this.scrollTo(St.Side.BOTTOM);
|
this.scrollTo(St.Side.BOTTOM);
|
||||||
this._oldMaxScrollAdjustment = adjustment.upper;
|
this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
|
this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
|
||||||
@ -899,24 +931,30 @@ const ChatNotification = new Lang.Class({
|
|||||||
|
|
||||||
let format;
|
let format;
|
||||||
|
|
||||||
// Show a week day and time if date is in the last week
|
// Show only the hour if date is on today
|
||||||
if (daysAgo < 1 || (daysAgo < 7 && now.getDay() != date.getDay())) {
|
if(daysAgo < 1){
|
||||||
/* Translators: this is a time format string followed by a date.
|
format = "<b>%H:%M</b>";
|
||||||
If applicable, replace %X with a strftime format valid for your
|
}
|
||||||
locale, without seconds. */
|
// 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
|
// xgettext:no-c-format
|
||||||
format = _("Sent at <b>%X</b> on <b>%A</b>");
|
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>");
|
||||||
|
|
||||||
} else if (date.getYear() == now.getYear()) {
|
} else if (date.getYear() == now.getYear()) {
|
||||||
/* Translators: this is a time format in the style of "Wednesday, May 25",
|
/* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/
|
||||||
shown when you get a chat message in the same year. */
|
|
||||||
// xgettext:no-c-format
|
// xgettext:no-c-format
|
||||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
|
format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>");
|
||||||
} else {
|
} else {
|
||||||
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
/* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/
|
||||||
shown when you get a chat message in a different year. */
|
|
||||||
// xgettext:no-c-format
|
// xgettext:no-c-format
|
||||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
|
format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
|
||||||
}
|
}
|
||||||
|
|
||||||
return date.toLocaleFormat(format);
|
return date.toLocaleFormat(format);
|
||||||
@ -1022,6 +1060,10 @@ const ApproverSource = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||||
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
if (this._invalidId != 0) {
|
if (this._invalidId != 0) {
|
||||||
this._dispatchOp.disconnect(this._invalidId);
|
this._dispatchOp.disconnect(this._invalidId);
|
||||||
@ -1031,9 +1073,8 @@ const ApproverSource = new Lang.Class({
|
|||||||
this.parent();
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
getIcon: function() {
|
||||||
return new St.Icon({ gicon: this._gicon,
|
return this._gicon;
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1094,7 +1135,7 @@ const AudioVideoNotification = new Lang.Class({
|
|||||||
this.parent(source, title, null, { customContent: true });
|
this.parent(source, title, null, { customContent: true });
|
||||||
this.setResident(true);
|
this.setResident(true);
|
||||||
|
|
||||||
this.addButton('reject', _("Reject"));
|
this.addButton('reject', _("Decline"));
|
||||||
/* translators: this is a button label (verb), not a noun */
|
/* translators: this is a button label (verb), not a noun */
|
||||||
this.addButton('answer', _("Answer"));
|
this.addButton('answer', _("Answer"));
|
||||||
|
|
||||||
@ -1184,7 +1225,6 @@ const SubscriptionRequestNotification = new Lang.Class({
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: iconBox._size });
|
icon_size: iconBox._size });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1312,32 +1352,20 @@ const AccountNotification = new Lang.Class({
|
|||||||
this.parent(source,
|
this.parent(source,
|
||||||
/* translators: argument is the account name, like
|
/* translators: argument is the account name, like
|
||||||
* name@jabber.org for example. */
|
* name@jabber.org for example. */
|
||||||
_("Connection to %s failed").format(account.get_display_name()),
|
_("Unable to connect to %s").format(account.get_display_name()),
|
||||||
null, { customContent: true });
|
this._getMessage(connectionError));
|
||||||
|
|
||||||
this._label = new St.Label();
|
|
||||||
this.addActor(this._label);
|
|
||||||
this._updateMessage(connectionError);
|
|
||||||
|
|
||||||
this._account = account;
|
this._account = account;
|
||||||
|
|
||||||
this.addButton('reconnect', _("Reconnect"));
|
this.addButton('view', _("View account"));
|
||||||
this.addButton('edit', _("Edit account"));
|
|
||||||
|
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'reconnect':
|
case 'view':
|
||||||
// If it fails again, a new notification should pop up with the
|
let cmd = 'empathy-accounts --select-account=' +
|
||||||
// new error.
|
account.get_path_suffix();
|
||||||
account.reconnect_async(null, null);
|
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||||
break;
|
app_info.launch([], global.create_app_launch_context());
|
||||||
case 'edit':
|
|
||||||
let cmd = '/usr/bin/empathy-accounts'
|
|
||||||
+ ' --select-account=%s'
|
|
||||||
.format(account.get_path_suffix());
|
|
||||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
|
|
||||||
null);
|
|
||||||
app_info.launch([], null, null);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.destroy();
|
this.destroy();
|
||||||
@ -1358,19 +1386,19 @@ const AccountNotification = new Lang.Class({
|
|||||||
if (status == Tp.ConnectionStatus.CONNECTED) {
|
if (status == Tp.ConnectionStatus.CONNECTED) {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
|
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
|
||||||
this._updateMessage(account.connection_error);
|
this.update(this.title, this._getMessage(account.connection_error));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateMessage: function(connectionError) {
|
_getMessage: function(connectionError) {
|
||||||
let message;
|
let message;
|
||||||
if (connectionError in _connectionErrorMessages) {
|
if (connectionError in _connectionErrorMessages) {
|
||||||
message = _connectionErrorMessages[connectionError];
|
message = _connectionErrorMessages[connectionError];
|
||||||
} else {
|
} else {
|
||||||
message = _("Unknown reason");
|
message = _("Unknown reason");
|
||||||
}
|
}
|
||||||
this._label.set_text(message);
|
return message;
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@ -1392,3 +1420,4 @@ const AccountNotification = new Lang.Class({
|
|||||||
this.parent();
|
this.parent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const Component = TelepathyClient;
|
@ -1,15 +1,14 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gdk = imports.gi.Gdk;
|
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const AltTab = imports.ui.altTab;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const SwitcherPopup = imports.ui.switcherPopup;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
@ -27,7 +26,9 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._items = [];
|
this._items = [];
|
||||||
this._focusManager = St.FocusManager.get_for_stage(global.stage);
|
this.addGroup(global.window_group, _("Windows"),
|
||||||
|
'emblem-documents-symbolic', { sortGroup: SortGroup.TOP,
|
||||||
|
focusCallback: Lang.bind(this, this._focusWindows) });
|
||||||
},
|
},
|
||||||
|
|
||||||
addGroup: function(root, name, icon, params) {
|
addGroup: function(root, name, icon, params) {
|
||||||
@ -41,11 +42,13 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
|
|
||||||
this._items.push(item);
|
this._items.push(item);
|
||||||
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
||||||
this._focusManager.add_group(root);
|
if (root instanceof St.Widget)
|
||||||
|
global.focus_manager.add_group(root);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeGroup: function(root) {
|
removeGroup: function(root) {
|
||||||
this._focusManager.remove_group(root);
|
if (root instanceof St.Widget)
|
||||||
|
global.focus_manager.remove_group(root);
|
||||||
for (let i = 0; i < this._items.length; i++) {
|
for (let i = 0; i < this._items.length; i++) {
|
||||||
if (this._items[i].root == root) {
|
if (this._items[i].root == root) {
|
||||||
this._items.splice(i, 1);
|
this._items.splice(i, 1);
|
||||||
@ -54,17 +57,15 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
focusGroup: function(item) {
|
focusGroup: function(item, timestamp) {
|
||||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
if (item.focusCallback) {
|
||||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
item.focusCallback(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
} else {
|
||||||
|
if (global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||||
|
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||||
|
|
||||||
if (item.window)
|
|
||||||
Main.activateWindow(item.window);
|
|
||||||
else if (item.focusCallback)
|
|
||||||
item.focusCallback();
|
|
||||||
else
|
|
||||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sort the items into a consistent order; panel first, tray last,
|
// Sort the items into a consistent order; panel first, tray last,
|
||||||
@ -75,44 +76,45 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
if (a.sortGroup != b.sortGroup)
|
if (a.sortGroup != b.sortGroup)
|
||||||
return a.sortGroup - b.sortGroup;
|
return a.sortGroup - b.sortGroup;
|
||||||
|
|
||||||
let y;
|
let ax, bx, y;
|
||||||
if (a.x == undefined) {
|
[ax, y] = a.proxy.get_transformed_position();
|
||||||
if (a.window)
|
[bx, y] = b.proxy.get_transformed_position();
|
||||||
a.x = a.window.get_compositor_private().x;
|
|
||||||
else
|
|
||||||
[a.x, y] = a.proxy.get_transformed_position();
|
|
||||||
}
|
|
||||||
if (b.x == undefined) {
|
|
||||||
if (b.window)
|
|
||||||
b.x = b.window.get_compositor_private().x;
|
|
||||||
else
|
|
||||||
[b.x, y] = b.proxy.get_transformed_position();
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.x - b.x;
|
return ax - bx;
|
||||||
},
|
},
|
||||||
|
|
||||||
popup: function(backwards, mask) {
|
popup: function(backward, binding, mask) {
|
||||||
// Start with the set of focus groups that are currently mapped
|
// Start with the set of focus groups that are currently mapped
|
||||||
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
||||||
|
|
||||||
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
||||||
if (!Main.overview.visible) {
|
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
|
||||||
let screen = global.screen;
|
let screen = global.screen;
|
||||||
let display = screen.get_display();
|
let display = screen.get_display();
|
||||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||||
let windowTracker = Shell.WindowTracker.get_default();
|
let windowTracker = Shell.WindowTracker.get_default();
|
||||||
let textureCache = St.TextureCache.get_default();
|
let textureCache = St.TextureCache.get_default();
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
let icon;
|
let icon = null;
|
||||||
let app = windowTracker.get_window_app(windows[i]);
|
let iconName = null;
|
||||||
if (app)
|
if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
|
||||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
iconName = 'video-display-symbolic';
|
||||||
else
|
} else {
|
||||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
let app = windowTracker.get_window_app(windows[i]);
|
||||||
items.push({ window: windows[i],
|
if (app)
|
||||||
name: windows[i].title,
|
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||||
|
else
|
||||||
|
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({ name: windows[i].title,
|
||||||
|
proxy: windows[i].get_compositor_private(),
|
||||||
|
focusCallback: Lang.bind(windows[i],
|
||||||
|
function(timestamp) {
|
||||||
|
Main.activateWindow(this, timestamp);
|
||||||
|
}),
|
||||||
iconActor: icon,
|
iconActor: icon,
|
||||||
|
iconName: iconName,
|
||||||
sortGroup: SortGroup.MIDDLE });
|
sortGroup: SortGroup.MIDDLE });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,187 +125,63 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
items.sort(Lang.bind(this, this._sortItems));
|
items.sort(Lang.bind(this, this._sortItems));
|
||||||
|
|
||||||
if (!this._popup) {
|
if (!this._popup) {
|
||||||
this._popup = new CtrlAltTabPopup();
|
this._popup = new CtrlAltTabPopup(items);
|
||||||
this._popup.show(items, backwards, mask);
|
this._popup.show(backward, binding, mask);
|
||||||
|
|
||||||
this._popup.actor.connect('destroy',
|
this._popup.actor.connect('destroy',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this._popup = null;
|
this._popup = null;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_focusWindows: function(timestamp) {
|
||||||
|
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||||
|
global.stage.key_focus = null;
|
||||||
|
global.screen.focus_default_window(timestamp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function mod(a, b) {
|
|
||||||
return (a + b) % b;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CtrlAltTabPopup = new Lang.Class({
|
const CtrlAltTabPopup = new Lang.Class({
|
||||||
Name: 'CtrlAltTabPopup',
|
Name: 'CtrlAltTabPopup',
|
||||||
|
Extends: SwitcherPopup.SwitcherPopup,
|
||||||
|
|
||||||
_init : function() {
|
_createSwitcher: function() {
|
||||||
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
|
this._switcherList = new CtrlAltTabSwitcher(this._items);
|
||||||
reactive: true });
|
return true;
|
||||||
|
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
|
||||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
|
||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
||||||
|
|
||||||
this._haveModal = false;
|
|
||||||
this._modifierMask = 0;
|
|
||||||
this._selection = 0;
|
|
||||||
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
_initialSelection: function(backward, binding) {
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
if (binding == 'switch-panels') {
|
||||||
|
if (backward)
|
||||||
alloc.min_size = primary.width;
|
this._selectedIndex = this._items.length - 1;
|
||||||
alloc.natural_size = primary.width;
|
} else if (binding == 'switch-panels-backward') {
|
||||||
},
|
if (!backward)
|
||||||
|
this._selectedIndex = this._items.length - 1;
|
||||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
|
||||||
|
|
||||||
alloc.min_size = primary.height;
|
|
||||||
alloc.natural_size = primary.height;
|
|
||||||
},
|
|
||||||
|
|
||||||
_allocate: function (actor, box, flags) {
|
|
||||||
let childBox = new Clutter.ActorBox();
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
|
||||||
|
|
||||||
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
|
||||||
let vPadding = this.actor.get_theme_node().get_vertical_padding();
|
|
||||||
let hPadding = this.actor.get_theme_node().get_horizontal_padding();
|
|
||||||
|
|
||||||
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
|
|
||||||
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
|
|
||||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
|
||||||
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
|
||||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
|
||||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
|
||||||
this._switcher.actor.allocate(childBox, flags);
|
|
||||||
},
|
|
||||||
|
|
||||||
show : function(items, startBackwards, mask) {
|
|
||||||
if (!Main.pushModal(this.actor))
|
|
||||||
return false;
|
|
||||||
this._haveModal = true;
|
|
||||||
this._modifierMask = AltTab.primaryModifier(mask);
|
|
||||||
|
|
||||||
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
|
||||||
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
|
||||||
|
|
||||||
this._items = items;
|
|
||||||
this._switcher = new CtrlAltTabSwitcher(items);
|
|
||||||
this.actor.add_actor(this._switcher.actor);
|
|
||||||
|
|
||||||
if (startBackwards)
|
|
||||||
this._selection = this._items.length - 1;
|
|
||||||
this._select(this._selection);
|
|
||||||
|
|
||||||
let [x, y, mods] = global.get_pointer();
|
|
||||||
if (!(mods & this._modifierMask)) {
|
|
||||||
this._finish();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
this._select(this._selectedIndex);
|
||||||
this.actor.opacity = 0;
|
|
||||||
this.actor.show();
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: POPUP_FADE_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_next : function() {
|
_keyPressHandler: function(keysym, backwards, action) {
|
||||||
return mod(this._selection + 1, this._items.length);
|
if (action == Meta.KeyBindingAction.SWITCH_PANELS)
|
||||||
},
|
this._select(backwards ? this._previous() : this._next());
|
||||||
|
else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
|
||||||
_previous : function() {
|
this._select(backwards ? this._next() : this._previous());
|
||||||
return mod(this._selection - 1, this._items.length);
|
else if (keysym == Clutter.Left)
|
||||||
},
|
|
||||||
|
|
||||||
_keyPressEvent : function(actor, event) {
|
|
||||||
let keysym = event.get_key_symbol();
|
|
||||||
let shift = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
|
|
||||||
if (shift && keysym == Clutter.KEY_Tab)
|
|
||||||
keysym = Clutter.ISO_Left_Tab;
|
|
||||||
|
|
||||||
if (keysym == Clutter.KEY_Escape)
|
|
||||||
this.destroy();
|
|
||||||
else if (keysym == Clutter.KEY_Tab)
|
|
||||||
this._select(this._next());
|
|
||||||
else if (keysym == Clutter.KEY_ISO_Left_Tab)
|
|
||||||
this._select(this._previous());
|
this._select(this._previous());
|
||||||
else if (keysym == Clutter.KEY_Left)
|
else if (keysym == Clutter.Right)
|
||||||
this._select(this._previous());
|
|
||||||
else if (keysym == Clutter.KEY_Right)
|
|
||||||
this._select(this._next());
|
this._select(this._next());
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_keyReleaseEvent : function(actor, event) {
|
_finish : function(time) {
|
||||||
let [x, y, mods] = global.get_pointer();
|
this.parent(time);
|
||||||
let state = mods & this._modifierMask;
|
Main.ctrlAltTabManager.focusGroup(this._items[this._selectedIndex], time);
|
||||||
|
|
||||||
if (state == 0)
|
|
||||||
this._finish();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_finish : function() {
|
|
||||||
this.destroy();
|
|
||||||
|
|
||||||
Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
|
|
||||||
},
|
|
||||||
|
|
||||||
_popModal: function() {
|
|
||||||
if (this._haveModal) {
|
|
||||||
Main.popModal(this.actor);
|
|
||||||
this._haveModal = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy : function() {
|
|
||||||
this._popModal();
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ opacity: 0,
|
|
||||||
time: POPUP_FADE_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
onComplete: Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this.actor.destroy();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy : function() {
|
|
||||||
this._popModal();
|
|
||||||
if (this._keyPressEventId)
|
|
||||||
this.actor.disconnect(this._keyPressEventId);
|
|
||||||
if (this._keyReleaseEventId)
|
|
||||||
this.actor.disconnect(this._keyReleaseEventId);
|
|
||||||
},
|
|
||||||
|
|
||||||
_select : function(num) {
|
|
||||||
this._selection = num;
|
|
||||||
this._switcher.highlight(num);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const CtrlAltTabSwitcher = new Lang.Class({
|
const CtrlAltTabSwitcher = new Lang.Class({
|
||||||
Name: 'CtrlAltTabSwitcher',
|
Name: 'CtrlAltTabSwitcher',
|
||||||
Extends: AltTab.SwitcherList,
|
Extends: SwitcherPopup.SwitcherList,
|
||||||
|
|
||||||
_init : function(items) {
|
_init : function(items) {
|
||||||
this.parent(true);
|
this.parent(true);
|
||||||
@ -319,7 +197,6 @@ const CtrlAltTabSwitcher = new Lang.Class({
|
|||||||
let icon = item.iconActor;
|
let icon = item.iconActor;
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
icon = new St.Icon({ icon_name: item.iconName,
|
icon = new St.Icon({ icon_name: item.iconName,
|
||||||
icon_type: St.IconType.SYMBOLIC,
|
|
||||||
icon_size: POPUP_APPICON_SIZE });
|
icon_size: POPUP_APPICON_SIZE });
|
||||||
}
|
}
|
||||||
box.add(icon, { x_fill: false, y_fill: false } );
|
box.add(icon, { x_fill: false, y_fill: false } );
|
||||||
|
524
js/ui/dash.js
@ -21,30 +21,38 @@ const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
|
|||||||
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
||||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
||||||
|
|
||||||
|
function getAppFromSource(source) {
|
||||||
|
if (source instanceof AppDisplay.AppIcon) {
|
||||||
|
return source.app;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A container like StBin, but taking the child's scale into account
|
// A container like StBin, but taking the child's scale into account
|
||||||
// when requesting a size
|
// when requesting a size
|
||||||
const DashItemContainer = new Lang.Class({
|
const DashItemContainer = new Lang.Class({
|
||||||
Name: 'DashItemContainer',
|
Name: 'DashItemContainer',
|
||||||
|
Extends: St.Widget,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
|
this.parent({ style_class: 'dash-item-container' });
|
||||||
this.actor.connect('get-preferred-width',
|
|
||||||
Lang.bind(this, this._getPreferredWidth));
|
|
||||||
this.actor.connect('get-preferred-height',
|
|
||||||
Lang.bind(this, this._getPreferredHeight));
|
|
||||||
this.actor.connect('allocate',
|
|
||||||
Lang.bind(this, this._allocate));
|
|
||||||
this.actor._delegate = this;
|
|
||||||
|
|
||||||
this.label = null;
|
this._labelText = "";
|
||||||
|
this.label = new St.Label({ style_class: 'dash-label'});
|
||||||
|
this.label.hide();
|
||||||
|
Main.layoutManager.addChrome(this.label);
|
||||||
|
this.label_actor = this.label;
|
||||||
|
|
||||||
this.child = null;
|
this.child = null;
|
||||||
this._childScale = 1;
|
this._childScale = 0;
|
||||||
this._childOpacity = 255;
|
this._childOpacity = 0;
|
||||||
this.animatingOut = false;
|
this.animatingOut = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
vfunc_allocate: function(box, flags) {
|
||||||
|
this.set_allocation(box, flags);
|
||||||
|
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -66,40 +74,41 @@ const DashItemContainer = new Lang.Class({
|
|||||||
this.child.allocate(childBox, flags);
|
this.child.allocate(childBox, flags);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
vfunc_get_preferred_height: function(forWidth) {
|
||||||
alloc.min_size = 0;
|
let themeNode = this.get_theme_node();
|
||||||
alloc.natural_size = 0;
|
|
||||||
|
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return [0, 0];
|
||||||
|
|
||||||
|
forWidth = themeNode.adjust_for_width(forWidth);
|
||||||
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
||||||
alloc.min_size += minHeight * this.child.scale_y;
|
return themeNode.adjust_preferred_height(minHeight * this.child.scale_y,
|
||||||
alloc.natural_size += natHeight * this.child.scale_y;
|
natHeight * this.child.scale_y);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
vfunc_get_preferred_width: function(forHeight) {
|
||||||
alloc.min_size = 0;
|
let themeNode = this.get_theme_node();
|
||||||
alloc.natural_size = 0;
|
|
||||||
|
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return [0, 0];
|
||||||
|
|
||||||
|
forHeight = themeNode.adjust_for_height(forHeight);
|
||||||
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
||||||
alloc.min_size = minWidth * this.child.scale_y;
|
return themeNode.adjust_preferred_width(minWidth * this.child.scale_y,
|
||||||
alloc.natural_size = natWidth * this.child.scale_y;
|
natWidth * this.child.scale_y);
|
||||||
},
|
},
|
||||||
|
|
||||||
showLabel: function() {
|
showLabel: function() {
|
||||||
if (this.label == null)
|
if (!this._labelText)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this.label.set_text(this._labelText);
|
||||||
this.label.opacity = 0;
|
this.label.opacity = 0;
|
||||||
this.label.show();
|
this.label.show();
|
||||||
|
|
||||||
let [stageX, stageY] = this.actor.get_transformed_position();
|
let [stageX, stageY] = this.get_transformed_position();
|
||||||
|
|
||||||
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
let itemHeight = this.allocation.y2 - this.allocation.y1;
|
||||||
|
|
||||||
let labelHeight = this.label.get_height();
|
let labelHeight = this.label.get_height();
|
||||||
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
||||||
@ -113,7 +122,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||||
x = stageX - this.label.get_width() - xOffset;
|
x = stageX - this.label.get_width() - xOffset;
|
||||||
else
|
else
|
||||||
x = stageX + this.actor.get_width() + xOffset;
|
x = stageX + this.get_width() + xOffset;
|
||||||
|
|
||||||
this.label.set_position(x, y);
|
this.label.set_position(x, y);
|
||||||
Tweener.addTween(this.label,
|
Tweener.addTween(this.label,
|
||||||
@ -124,12 +133,8 @@ const DashItemContainer = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setLabelText: function(text) {
|
setLabelText: function(text) {
|
||||||
if (this.label == null)
|
this._labelText = text;
|
||||||
this.label = new St.Label({ style_class: 'dash-label'});
|
this.child.accessible_name = text;
|
||||||
|
|
||||||
this.label.set_text(text);
|
|
||||||
Main.layoutManager.addChrome(this.label);
|
|
||||||
this.label.hide();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hideLabel: function () {
|
hideLabel: function () {
|
||||||
@ -147,22 +152,25 @@ const DashItemContainer = new Lang.Class({
|
|||||||
if (this.child == actor)
|
if (this.child == actor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.actor.destroy_all_children();
|
this.destroy_all_children();
|
||||||
|
|
||||||
this.child = actor;
|
this.child = actor;
|
||||||
this.actor.add_actor(this.child);
|
this.add_actor(this.child);
|
||||||
|
|
||||||
|
this.child.set_scale_with_gravity(this._childScale, this._childScale,
|
||||||
|
Clutter.Gravity.CENTER);
|
||||||
|
this.child.set_opacity(this._childOpacity);
|
||||||
},
|
},
|
||||||
|
|
||||||
animateIn: function() {
|
show: function(animate) {
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.childScale = 0;
|
let time = animate ? DASH_ANIMATION_TIME : 0;
|
||||||
this.childOpacity = 0;
|
|
||||||
Tweener.addTween(this,
|
Tweener.addTween(this,
|
||||||
{ childScale: 1.0,
|
{ childScale: 1.0,
|
||||||
childOpacity: 255,
|
childOpacity: 255,
|
||||||
time: DASH_ANIMATION_TIME,
|
time: time,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -171,7 +179,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
if (this.label)
|
if (this.label)
|
||||||
this.label.destroy();
|
this.label.destroy();
|
||||||
|
|
||||||
this.actor.destroy();
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
animateOutAndDestroy: function() {
|
animateOutAndDestroy: function() {
|
||||||
@ -179,19 +187,18 @@ const DashItemContainer = new Lang.Class({
|
|||||||
this.label.destroy();
|
this.label.destroy();
|
||||||
|
|
||||||
if (this.child == null) {
|
if (this.child == null) {
|
||||||
this.actor.destroy();
|
this.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.animatingOut = true;
|
this.animatingOut = true;
|
||||||
this.childScale = 1.0;
|
|
||||||
Tweener.addTween(this,
|
Tweener.addTween(this,
|
||||||
{ childScale: 0.0,
|
{ childScale: 0.0,
|
||||||
childOpacity: 0,
|
childOpacity: 0,
|
||||||
time: DASH_ANIMATION_TIME,
|
time: DASH_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.actor.destroy();
|
this.destroy();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -204,7 +211,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
|
|
||||||
this.child.set_scale_with_gravity(scale, scale,
|
this.child.set_scale_with_gravity(scale, scale,
|
||||||
Clutter.Gravity.CENTER);
|
Clutter.Gravity.CENTER);
|
||||||
this.actor.queue_relayout();
|
this.queue_relayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
get childScale() {
|
get childScale() {
|
||||||
@ -218,7 +225,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.child.set_opacity(opacity);
|
this.child.set_opacity(opacity);
|
||||||
this.actor.queue_redraw();
|
this.queue_redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
get childOpacity() {
|
get childOpacity() {
|
||||||
@ -226,52 +233,70 @@ const DashItemContainer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const RemoveFavoriteIcon = new Lang.Class({
|
const ShowAppsIcon = new Lang.Class({
|
||||||
Name: 'RemoveFavoriteIcon',
|
Name: 'ShowAppsIcon',
|
||||||
Extends: DashItemContainer,
|
Extends: DashItemContainer,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent();
|
this.parent();
|
||||||
|
|
||||||
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
|
this.toggleButton = new St.Button({ style_class: 'show-apps',
|
||||||
|
track_hover: true,
|
||||||
|
can_focus: true,
|
||||||
|
toggle_mode: true });
|
||||||
this._iconActor = null;
|
this._iconActor = null;
|
||||||
this.icon = new IconGrid.BaseIcon(_("Remove"),
|
this.icon = new IconGrid.BaseIcon(_("Show Applications"),
|
||||||
{ setSizeManually: true,
|
{ setSizeManually: true,
|
||||||
showLabel: false,
|
showLabel: false,
|
||||||
createIcon: Lang.bind(this, this._createIcon) });
|
createIcon: Lang.bind(this, this._createIcon) });
|
||||||
this._iconBin.set_child(this.icon.actor);
|
this.toggleButton.add_actor(this.icon.actor);
|
||||||
this._iconBin._delegate = this;
|
this.toggleButton._delegate = this;
|
||||||
|
|
||||||
this.setChild(this._iconBin);
|
this.setChild(this.toggleButton);
|
||||||
|
this.setDragApp(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_createIcon: function(size) {
|
_createIcon: function(size) {
|
||||||
this._iconActor = new St.Icon({ icon_name: 'user-trash',
|
this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
|
||||||
style_class: 'remove-favorite-icon',
|
icon_size: size,
|
||||||
icon_size: size });
|
style_class: 'show-apps-icon',
|
||||||
|
track_hover: true });
|
||||||
return this._iconActor;
|
return this._iconActor;
|
||||||
},
|
},
|
||||||
|
|
||||||
setHover: function(hovered) {
|
_canRemoveApp: function(app) {
|
||||||
this._iconBin.set_hover(hovered);
|
if (app == null)
|
||||||
if (this._iconActor)
|
return false;
|
||||||
this._iconActor.set_hover(hovered);
|
|
||||||
|
let id = app.get_id();
|
||||||
|
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
||||||
|
return isFavorite;
|
||||||
|
},
|
||||||
|
|
||||||
|
setDragApp: function(app) {
|
||||||
|
let canRemove = this._canRemoveApp(app);
|
||||||
|
|
||||||
|
this.toggleButton.set_hover(canRemove);
|
||||||
|
if (this._iconActor)
|
||||||
|
this._iconActor.set_hover(canRemove);
|
||||||
|
|
||||||
|
if (canRemove)
|
||||||
|
this.setLabelText(_("Remove from Favorites"));
|
||||||
|
else
|
||||||
|
this.setLabelText(_("Show Applications"));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Rely on the dragged item being a favorite
|
|
||||||
handleDragOver: function(source, actor, x, y, time) {
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
|
if (!this._canRemoveApp(getAppFromSource(source)))
|
||||||
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
return DND.DragMotionResult.MOVE_DROP;
|
return DND.DragMotionResult.MOVE_DROP;
|
||||||
},
|
},
|
||||||
|
|
||||||
acceptDrop: function(source, actor, x, y, time) {
|
acceptDrop: function(source, actor, x, y, time) {
|
||||||
let app = null;
|
let app = getAppFromSource(source);
|
||||||
if (source instanceof AppDisplay.AppWellIcon) {
|
if (!this._canRemoveApp(app))
|
||||||
let appSystem = Shell.AppSystem.get_default();
|
return false;
|
||||||
app = appSystem.lookup_app(source.getId());
|
|
||||||
} else if (source.metaWindow) {
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
|
||||||
app = tracker.get_window_app(source.metaWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = app.get_id();
|
let id = app.get_id();
|
||||||
|
|
||||||
@ -295,6 +320,66 @@ const DragPlaceholderItem = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const EmptyDropTargetItem = new Lang.Class({
|
||||||
|
Name: 'EmptyDropTargetItem',
|
||||||
|
Extends: DashItemContainer,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent();
|
||||||
|
this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DashActor = new Lang.Class({
|
||||||
|
Name: 'DashActor',
|
||||||
|
Extends: St.Widget,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||||
|
this.parent({ name: 'dash',
|
||||||
|
layout_manager: layout,
|
||||||
|
clip_to_allocation: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_allocate: function(box, flags) {
|
||||||
|
let contentBox = this.get_theme_node().get_content_box(box);
|
||||||
|
let availWidth = contentBox.x2 - contentBox.x1;
|
||||||
|
|
||||||
|
this.set_allocation(box, flags);
|
||||||
|
|
||||||
|
let [appIcons, showAppsButton] = this.get_children();
|
||||||
|
let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
|
||||||
|
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
childBox.x1 = contentBox.x1;
|
||||||
|
childBox.y1 = contentBox.y1;
|
||||||
|
childBox.x2 = contentBox.x2;
|
||||||
|
childBox.y2 = contentBox.y2 - showAppsNatHeight;
|
||||||
|
appIcons.allocate(childBox, flags);
|
||||||
|
|
||||||
|
childBox.y1 = contentBox.y2 - showAppsNatHeight;
|
||||||
|
childBox.y2 = contentBox.y2;
|
||||||
|
showAppsButton.allocate(childBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_height: function(forWidth) {
|
||||||
|
// We want to request the natural height of all our children
|
||||||
|
// as our natural height, so we chain up to StWidget (which
|
||||||
|
// then calls BoxLayout), but we only request the showApps
|
||||||
|
// button as the minimum size
|
||||||
|
|
||||||
|
let [, natHeight] = this.parent(forWidth);
|
||||||
|
|
||||||
|
let themeNode = this.get_theme_node();
|
||||||
|
let adjustedForWidth = themeNode.adjust_for_width(forWidth);
|
||||||
|
let [, showAppsButton] = this.get_children();
|
||||||
|
let [minHeight, ] = showAppsButton.get_preferred_height(adjustedForWidth);
|
||||||
|
[minHeight, ] = themeNode.adjust_preferred_height(minHeight, natHeight);
|
||||||
|
|
||||||
|
return [minHeight, natHeight];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const Dash = new Lang.Class({
|
const Dash = new Lang.Class({
|
||||||
Name: 'Dash',
|
Name: 'Dash',
|
||||||
|
|
||||||
@ -306,17 +391,27 @@ const Dash = new Lang.Class({
|
|||||||
this._dragPlaceholder = null;
|
this._dragPlaceholder = null;
|
||||||
this._dragPlaceholderPos = -1;
|
this._dragPlaceholderPos = -1;
|
||||||
this._animatingPlaceholdersCount = 0;
|
this._animatingPlaceholdersCount = 0;
|
||||||
this._favRemoveTarget = null;
|
|
||||||
this._showLabelTimeoutId = 0;
|
this._showLabelTimeoutId = 0;
|
||||||
this._resetHoverTimeoutId = 0;
|
this._resetHoverTimeoutId = 0;
|
||||||
this._labelShowing = false;
|
this._labelShowing = false;
|
||||||
|
|
||||||
this._box = new St.BoxLayout({ name: 'dash',
|
this._container = new DashActor();
|
||||||
vertical: true,
|
this._box = new St.BoxLayout({ vertical: true,
|
||||||
clip_to_allocation: true });
|
clip_to_allocation: true });
|
||||||
this._box._delegate = this;
|
this._box._delegate = this;
|
||||||
|
this._container.add_actor(this._box);
|
||||||
|
|
||||||
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
|
this._showAppsIcon = new ShowAppsIcon();
|
||||||
|
this._showAppsIcon.childScale = 1;
|
||||||
|
this._showAppsIcon.childOpacity = 255;
|
||||||
|
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
||||||
|
this._hookUpLabel(this._showAppsIcon);
|
||||||
|
|
||||||
|
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||||||
|
|
||||||
|
this._container.add_actor(this._showAppsIcon);
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ child: this._container });
|
||||||
this.actor.connect('notify::height', Lang.bind(this,
|
this.actor.connect('notify::height', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
if (this._maxHeight != this.actor.height)
|
if (this._maxHeight != this.actor.height)
|
||||||
@ -326,7 +421,6 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
||||||
|
|
||||||
this._tracker = Shell.WindowTracker.get_default();
|
|
||||||
this._appSystem = Shell.AppSystem.get_default();
|
this._appSystem = Shell.AppSystem.get_default();
|
||||||
|
|
||||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||||
@ -339,12 +433,10 @@ const Dash = new Lang.Class({
|
|||||||
Lang.bind(this, this._onDragEnd));
|
Lang.bind(this, this._onDragEnd));
|
||||||
Main.overview.connect('item-drag-cancelled',
|
Main.overview.connect('item-drag-cancelled',
|
||||||
Lang.bind(this, this._onDragCancelled));
|
Lang.bind(this, this._onDragCancelled));
|
||||||
Main.overview.connect('window-drag-begin',
|
|
||||||
Lang.bind(this, this._onDragBegin));
|
// Translators: this is the name of the dock/favorites area on
|
||||||
Main.overview.connect('window-drag-cancelled',
|
// the left of the overview
|
||||||
Lang.bind(this, this._onDragCancelled));
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||||
Main.overview.connect('window-drag-end',
|
|
||||||
Lang.bind(this, this._onDragEnd));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragBegin: function() {
|
_onDragBegin: function() {
|
||||||
@ -353,6 +445,12 @@ const Dash = new Lang.Class({
|
|||||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||||
};
|
};
|
||||||
DND.addDragMonitor(this._dragMonitor);
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
|
|
||||||
|
if (this._box.get_n_children() == 0) {
|
||||||
|
this._emptyDropTarget = new EmptyDropTargetItem();
|
||||||
|
this._box.insert_child_at_index(this._emptyDropTarget, 0);
|
||||||
|
this._emptyDropTarget.show(true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragCancelled: function() {
|
_onDragCancelled: function() {
|
||||||
@ -369,54 +467,26 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
_endDrag: function() {
|
_endDrag: function() {
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
if (this._favRemoveTarget) {
|
this._clearEmptyDropTarget();
|
||||||
this._favRemoveTarget.animateOutAndDestroy();
|
this._showAppsIcon.setDragApp(null);
|
||||||
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._favRemoveTarget = null;
|
|
||||||
}));
|
|
||||||
this._adjustIconSize();
|
|
||||||
}
|
|
||||||
DND.removeDragMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragMotion: function(dragEvent) {
|
_onDragMotion: function(dragEvent) {
|
||||||
let app = null;
|
let app = getAppFromSource(dragEvent.source);
|
||||||
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
if (app == null)
|
||||||
app = this._appSystem.lookup_app(dragEvent.source.getId());
|
|
||||||
else if (dragEvent.source.metaWindow)
|
|
||||||
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
|
||||||
else
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
|
|
||||||
let id = app.get_id();
|
let showAppsHovered =
|
||||||
|
this._showAppsIcon.contains(dragEvent.targetActor);
|
||||||
|
|
||||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
|
||||||
|
|
||||||
let srcIsFavorite = (id in favorites);
|
|
||||||
|
|
||||||
if (srcIsFavorite &&
|
|
||||||
app.get_state() != Shell.AppState.RUNNING &&
|
|
||||||
dragEvent.source.actor &&
|
|
||||||
this.actor.contains (dragEvent.source.actor) &&
|
|
||||||
this._favRemoveTarget == null) {
|
|
||||||
this._favRemoveTarget = new RemoveFavoriteIcon();
|
|
||||||
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
|
||||||
this._box.add(this._favRemoveTarget.actor);
|
|
||||||
this._adjustIconSize();
|
|
||||||
this._favRemoveTarget.animateIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
let favRemoveHovered = false;
|
|
||||||
if (this._favRemoveTarget)
|
|
||||||
favRemoveHovered =
|
|
||||||
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
|
|
||||||
|
|
||||||
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
|
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
|
|
||||||
if (this._favRemoveTarget)
|
if (showAppsHovered)
|
||||||
this._favRemoveTarget.setHover(favRemoveHovered);
|
this._showAppsIcon.setDragApp(app);
|
||||||
|
else
|
||||||
|
this._showAppsIcon.setDragApp(null);
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
@ -432,44 +502,63 @@ const Dash = new Lang.Class({
|
|||||||
Main.queueDeferredWork(this._workId);
|
Main.queueDeferredWork(this._workId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_hookUpLabel: function(item) {
|
||||||
|
item.child.connect('notify::hover', Lang.bind(this, function() {
|
||||||
|
this._onHover(item);
|
||||||
|
}));
|
||||||
|
|
||||||
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||||
|
this._labelShowing = false;
|
||||||
|
item.hideLabel();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
_createAppItem: function(app) {
|
_createAppItem: function(app) {
|
||||||
let display = new AppDisplay.AppWellIcon(app,
|
let appIcon = new AppDisplay.AppIcon(app,
|
||||||
{ setSizeManually: true,
|
{ setSizeManually: true,
|
||||||
showLabel: false });
|
showLabel: false });
|
||||||
display._draggable.connect('drag-begin',
|
appIcon._draggable.connect('drag-begin',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
display.actor.opacity = 50;
|
appIcon.actor.opacity = 50;
|
||||||
}));
|
}));
|
||||||
display._draggable.connect('drag-end',
|
appIcon._draggable.connect('drag-end',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
display.actor.opacity = 255;
|
appIcon.actor.opacity = 255;
|
||||||
}));
|
}));
|
||||||
|
appIcon.connect('menu-state-changed',
|
||||||
|
Lang.bind(this, function(appIcon, opened) {
|
||||||
|
this._itemMenuStateChanged(item, opened);
|
||||||
|
}));
|
||||||
|
|
||||||
let item = new DashItemContainer();
|
let item = new DashItemContainer();
|
||||||
item.setChild(display.actor);
|
item.setChild(appIcon.actor);
|
||||||
|
|
||||||
|
// Override default AppIcon label_actor, now the
|
||||||
|
// accessible_name is set at DashItemContainer.setLabelText
|
||||||
|
appIcon.actor.label_actor = null;
|
||||||
item.setLabelText(app.get_name());
|
item.setLabelText(app.get_name());
|
||||||
// Override default AppWellIcon label_actor
|
|
||||||
display.actor.label_actor = item.label;
|
|
||||||
|
|
||||||
|
appIcon.icon.setIconSize(this.iconSize);
|
||||||
display.icon.setIconSize(this.iconSize);
|
this._hookUpLabel(item);
|
||||||
display.actor.connect('notify::hover',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._onHover(item, display)
|
|
||||||
}));
|
|
||||||
|
|
||||||
Main.overview.connect('hiding',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._labelShowing = false;
|
|
||||||
item.hideLabel();
|
|
||||||
}));
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onHover: function (item, display) {
|
_itemMenuStateChanged: function(item, opened) {
|
||||||
if (display.actor.get_hover() && !display.isMenuUp) {
|
// When the menu closes, it calls sync_hover, which means
|
||||||
|
// that the notify::hover handler does everything we need to.
|
||||||
|
if (opened) {
|
||||||
|
if (this._showLabelTimeoutId > 0) {
|
||||||
|
Mainloop.source_remove(this._showLabelTimeoutId);
|
||||||
|
this._showLabelTimeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.hideLabel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onHover: function (item) {
|
||||||
|
if (item.child.get_hover()) {
|
||||||
if (this._showLabelTimeoutId == 0) {
|
if (this._showLabelTimeoutId == 0) {
|
||||||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||||||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||||
@ -504,24 +593,18 @@ const Dash = new Lang.Class({
|
|||||||
// animating out (which means they will be destroyed at the end of
|
// animating out (which means they will be destroyed at the end of
|
||||||
// the animation)
|
// the animation)
|
||||||
let iconChildren = this._box.get_children().filter(function(actor) {
|
let iconChildren = this._box.get_children().filter(function(actor) {
|
||||||
return actor._delegate.child &&
|
return actor.child &&
|
||||||
actor._delegate.child._delegate &&
|
actor.child._delegate &&
|
||||||
actor._delegate.child._delegate.icon &&
|
actor.child._delegate.icon &&
|
||||||
!actor._delegate.animatingOut;
|
!actor.animatingOut;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (iconChildren.length == 0) {
|
iconChildren.push(this._showAppsIcon);
|
||||||
this._box.add_style_pseudo_class('empty');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._box.remove_style_pseudo_class('empty');
|
|
||||||
|
|
||||||
if (this._maxHeight == -1)
|
if (this._maxHeight == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
let themeNode = this._container.get_theme_node();
|
||||||
let themeNode = this._box.get_theme_node();
|
|
||||||
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
|
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
|
||||||
x2: 42 /* whatever */,
|
x2: 42 /* whatever */,
|
||||||
y2: this._maxHeight });
|
y2: this._maxHeight });
|
||||||
@ -529,24 +612,18 @@ const Dash = new Lang.Class({
|
|||||||
let availHeight = maxContent.y2 - maxContent.y1;
|
let availHeight = maxContent.y2 - maxContent.y1;
|
||||||
let spacing = themeNode.get_length('spacing');
|
let spacing = themeNode.get_length('spacing');
|
||||||
|
|
||||||
|
let firstButton = iconChildren[0].child;
|
||||||
let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
|
let firstIcon = firstButton._delegate.icon;
|
||||||
|
|
||||||
let minHeight, natHeight;
|
let minHeight, natHeight;
|
||||||
|
|
||||||
// Enforce the current icon size during the size request if
|
// Enforce the current icon size during the size request
|
||||||
// the icon is animating
|
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
||||||
if (firstIcon._animating) {
|
|
||||||
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
|
||||||
|
|
||||||
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
||||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
|
||||||
|
|
||||||
firstIcon.icon.set_size(currentWidth, currentHeight);
|
|
||||||
} else {
|
|
||||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
firstIcon.icon.set_size(currentWidth, currentHeight);
|
||||||
|
|
||||||
// Subtract icon padding and box spacing from the available height
|
// Subtract icon padding and box spacing from the available height
|
||||||
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
||||||
@ -571,15 +648,17 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
let scale = oldIconSize / newIconSize;
|
let scale = oldIconSize / newIconSize;
|
||||||
for (let i = 0; i < iconChildren.length; i++) {
|
for (let i = 0; i < iconChildren.length; i++) {
|
||||||
let icon = iconChildren[i]._delegate.child._delegate.icon;
|
let icon = iconChildren[i].child._delegate.icon;
|
||||||
|
|
||||||
// Set the new size immediately, to keep the icons' sizes
|
// Set the new size immediately, to keep the icons' sizes
|
||||||
// in sync with this.iconSize
|
// in sync with this.iconSize
|
||||||
icon.setIconSize(this.iconSize);
|
icon.setIconSize(this.iconSize);
|
||||||
|
|
||||||
// Don't animate the icon size change when the overview
|
// Don't animate the icon size change when the overview
|
||||||
// is not visible or when initially filling the dash
|
// is transitioning, not visible or when initially filling
|
||||||
if (!Main.overview.visible || !this._shownInitially)
|
// the dash
|
||||||
|
if (!Main.overview.visible || Main.overview.animationInProgress ||
|
||||||
|
!this._shownInitially)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let [targetWidth, targetHeight] = icon.icon.get_size();
|
let [targetWidth, targetHeight] = icon.icon.get_size();
|
||||||
@ -589,15 +668,11 @@ const Dash = new Lang.Class({
|
|||||||
icon.icon.set_size(icon.icon.width * scale,
|
icon.icon.set_size(icon.icon.width * scale,
|
||||||
icon.icon.height * scale);
|
icon.icon.height * scale);
|
||||||
|
|
||||||
icon._animating = true;
|
|
||||||
Tweener.addTween(icon.icon,
|
Tweener.addTween(icon.icon,
|
||||||
{ width: targetWidth,
|
{ width: targetWidth,
|
||||||
height: targetHeight,
|
height: targetHeight,
|
||||||
time: DASH_ANIMATION_TIME,
|
time: DASH_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: function() {
|
|
||||||
icon._animating = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -608,13 +683,13 @@ const Dash = new Lang.Class({
|
|||||||
let running = this._appSystem.get_running();
|
let running = this._appSystem.get_running();
|
||||||
|
|
||||||
let children = this._box.get_children().filter(function(actor) {
|
let children = this._box.get_children().filter(function(actor) {
|
||||||
return actor._delegate.child &&
|
return actor.child &&
|
||||||
actor._delegate.child._delegate &&
|
actor.child._delegate &&
|
||||||
actor._delegate.child._delegate.app;
|
actor.child._delegate.app;
|
||||||
});
|
});
|
||||||
// Apps currently in the dash
|
// Apps currently in the dash
|
||||||
let oldApps = children.map(function(actor) {
|
let oldApps = children.map(function(actor) {
|
||||||
return actor._delegate.child._delegate.app;
|
return actor.child._delegate.app;
|
||||||
});
|
});
|
||||||
// Apps supposed to be in the dash
|
// Apps supposed to be in the dash
|
||||||
let newApps = [];
|
let newApps = [];
|
||||||
@ -680,7 +755,7 @@ const Dash = new Lang.Class({
|
|||||||
let insertHere = newApps[newIndex + 1] &&
|
let insertHere = newApps[newIndex + 1] &&
|
||||||
newApps[newIndex + 1] == oldApps[oldIndex];
|
newApps[newIndex + 1] == oldApps[oldIndex];
|
||||||
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
||||||
let removedApp = actor._delegate.child._delegate.app;
|
let removedApp = actor.child._delegate.app;
|
||||||
return result || removedApp == newApps[newIndex];
|
return result || removedApp == newApps[newIndex];
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
@ -697,14 +772,15 @@ const Dash = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < addedItems.length; i++)
|
for (let i = 0; i < addedItems.length; i++)
|
||||||
this._box.insert_child_at_index(addedItems[i].item.actor,
|
this._box.insert_child_at_index(addedItems[i].item,
|
||||||
addedItems[i].pos);
|
addedItems[i].pos);
|
||||||
|
|
||||||
for (let i = 0; i < removedActors.length; i++) {
|
for (let i = 0; i < removedActors.length; i++) {
|
||||||
let item = removedActors[i]._delegate;
|
let item = removedActors[i];
|
||||||
|
|
||||||
// Don't animate item removal when the overview is hidden
|
// Don't animate item removal when the overview is transitioning
|
||||||
if (Main.overview.visible)
|
// or hidden
|
||||||
|
if (Main.overview.visible && !Main.overview.animationInProgress)
|
||||||
item.animateOutAndDestroy();
|
item.animateOutAndDestroy();
|
||||||
else
|
else
|
||||||
item.destroy();
|
item.destroy();
|
||||||
@ -714,33 +790,44 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
// Skip animations on first run when adding the initial set
|
// Skip animations on first run when adding the initial set
|
||||||
// of items, to avoid all items zooming in at once
|
// of items, to avoid all items zooming in at once
|
||||||
if (!this._shownInitially) {
|
|
||||||
|
let animate = this._shownInitially && Main.overview.visible &&
|
||||||
|
!Main.overview.animationInProgress;
|
||||||
|
|
||||||
|
if (!this._shownInitially)
|
||||||
this._shownInitially = true;
|
this._shownInitially = true;
|
||||||
return;
|
|
||||||
|
for (let i = 0; i < addedItems.length; i++) {
|
||||||
|
addedItems[i].item.show(animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't animate item addition when the overview is hidden
|
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
|
||||||
if (!Main.overview.visible)
|
// Without it, StBoxLayout may use a stale size cache
|
||||||
return;
|
this._box.queue_relayout();
|
||||||
|
|
||||||
for (let i = 0; i < addedItems.length; i++)
|
|
||||||
addedItems[i].item.animateIn();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearDragPlaceholder: function() {
|
_clearDragPlaceholder: function() {
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
|
this._animatingPlaceholdersCount++;
|
||||||
this._dragPlaceholder.animateOutAndDestroy();
|
this._dragPlaceholder.animateOutAndDestroy();
|
||||||
|
this._dragPlaceholder.connect('destroy',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._animatingPlaceholdersCount--;
|
||||||
|
}));
|
||||||
this._dragPlaceholder = null;
|
this._dragPlaceholder = null;
|
||||||
this._dragPlaceholderPos = -1;
|
}
|
||||||
|
this._dragPlaceholderPos = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearEmptyDropTarget: function() {
|
||||||
|
if (this._emptyDropTarget) {
|
||||||
|
this._emptyDropTarget.animateOutAndDestroy();
|
||||||
|
this._emptyDropTarget = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDragOver : function(source, actor, x, y, time) {
|
handleDragOver : function(source, actor, x, y, time) {
|
||||||
let app = null;
|
let app = getAppFromSource(source);
|
||||||
if (source instanceof AppDisplay.AppWellIcon)
|
|
||||||
app = this._appSystem.lookup_app(source.getId());
|
|
||||||
else if (source.metaWindow)
|
|
||||||
app = this._tracker.get_window_app(source.metaWindow);
|
|
||||||
|
|
||||||
// Don't allow favoriting of transient apps
|
// Don't allow favoriting of transient apps
|
||||||
if (app == null || app.is_window_backed())
|
if (app == null || app.is_window_backed())
|
||||||
@ -759,27 +846,22 @@ const Dash = new Lang.Class({
|
|||||||
// the remove target has the same size as "normal" items, we don't
|
// the remove target has the same size as "normal" items, we don't
|
||||||
// need to do the same adjustment there.
|
// need to do the same adjustment there.
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
boxHeight -= this._dragPlaceholder.actor.height;
|
boxHeight -= this._dragPlaceholder.height;
|
||||||
numChildren--;
|
numChildren--;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = Math.floor(y * numChildren / boxHeight);
|
let pos;
|
||||||
|
if (!this._emptyDropTarget)
|
||||||
|
pos = Math.floor(y * numChildren / boxHeight);
|
||||||
|
else
|
||||||
|
pos = 0; // always insert at the top when dash is empty
|
||||||
|
|
||||||
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
||||||
this._dragPlaceholderPos = pos;
|
this._dragPlaceholderPos = pos;
|
||||||
|
|
||||||
// Don't allow positioning before or after self
|
// Don't allow positioning before or after self
|
||||||
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
||||||
if (this._dragPlaceholder) {
|
this._clearDragPlaceholder();
|
||||||
this._dragPlaceholder.animateOutAndDestroy();
|
|
||||||
this._animatingPlaceholdersCount++;
|
|
||||||
this._dragPlaceholder.actor.connect('destroy',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._animatingPlaceholdersCount--;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this._dragPlaceholder = null;
|
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,7 +870,7 @@ const Dash = new Lang.Class({
|
|||||||
// an animation
|
// an animation
|
||||||
let fadeIn;
|
let fadeIn;
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
this._dragPlaceholder.actor.destroy();
|
this._dragPlaceholder.destroy();
|
||||||
fadeIn = false;
|
fadeIn = false;
|
||||||
} else {
|
} else {
|
||||||
fadeIn = true;
|
fadeIn = true;
|
||||||
@ -797,17 +879,16 @@ const Dash = new Lang.Class({
|
|||||||
this._dragPlaceholder = new DragPlaceholderItem();
|
this._dragPlaceholder = new DragPlaceholderItem();
|
||||||
this._dragPlaceholder.child.set_width (this.iconSize);
|
this._dragPlaceholder.child.set_width (this.iconSize);
|
||||||
this._dragPlaceholder.child.set_height (this.iconSize / 2);
|
this._dragPlaceholder.child.set_height (this.iconSize / 2);
|
||||||
this._box.insert_child_at_index(this._dragPlaceholder.actor,
|
this._box.insert_child_at_index(this._dragPlaceholder,
|
||||||
this._dragPlaceholderPos);
|
this._dragPlaceholderPos);
|
||||||
if (fadeIn)
|
this._dragPlaceholder.show(fadeIn);
|
||||||
this._dragPlaceholder.animateIn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the drag placeholder if we are not in the
|
// Remove the drag placeholder if we are not in the
|
||||||
// "favorites zone"
|
// "favorites zone"
|
||||||
if (pos > numFavorites && this._dragPlaceholder) {
|
if (pos > numFavorites)
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
}
|
|
||||||
if (!this._dragPlaceholder)
|
if (!this._dragPlaceholder)
|
||||||
return DND.DragMotionResult.NO_DROP;
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
@ -821,12 +902,7 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
// Draggable target interface
|
// Draggable target interface
|
||||||
acceptDrop : function(source, actor, x, y, time) {
|
acceptDrop : function(source, actor, x, y, time) {
|
||||||
let app = null;
|
let app = getAppFromSource(source);
|
||||||
if (source instanceof AppDisplay.AppWellIcon) {
|
|
||||||
app = this._appSystem.lookup_app(source.getId());
|
|
||||||
} else if (source.metaWindow) {
|
|
||||||
app = this._tracker.get_window_app(source.metaWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow favoriting of transient apps
|
// Don't allow favoriting of transient apps
|
||||||
if (app == null || app.is_window_backed()) {
|
if (app == null || app.is_window_backed()) {
|
||||||
@ -843,10 +919,10 @@ const Dash = new Lang.Class({
|
|||||||
let children = this._box.get_children();
|
let children = this._box.get_children();
|
||||||
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
||||||
if (this._dragPlaceholder &&
|
if (this._dragPlaceholder &&
|
||||||
children[i] == this._dragPlaceholder.actor)
|
children[i] == this._dragPlaceholder)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let childId = children[i]._delegate.child._delegate.app.get_id();
|
let childId = children[i].child._delegate.app.get_id();
|
||||||
if (childId == id)
|
if (childId == id)
|
||||||
continue;
|
continue;
|
||||||
if (childId in favorites)
|
if (childId in favorites)
|
||||||
|
@ -32,6 +32,7 @@ function _onVertSepRepaint (area)
|
|||||||
cr.setDash([1, 3], 1); // Hard-code for now
|
cr.setDash([1, 3], 1); // Hard-code for now
|
||||||
cr.setLineWidth(stippleWidth);
|
cr.setLineWidth(stippleWidth);
|
||||||
cr.stroke();
|
cr.stroke();
|
||||||
|
cr.$dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const DateMenuButton = new Lang.Class({
|
const DateMenuButton = new Lang.Class({
|
||||||
@ -48,11 +49,6 @@ const DateMenuButton = new Lang.Class({
|
|||||||
menuAlignment = 1.0 - menuAlignment;
|
menuAlignment = 1.0 - menuAlignment;
|
||||||
this.parent(menuAlignment);
|
this.parent(menuAlignment);
|
||||||
|
|
||||||
// At this moment calendar menu is not keyboard navigable at
|
|
||||||
// all (so not accessible), so it doesn't make sense to set as
|
|
||||||
// role ATK_ROLE_MENU like other elements of the panel.
|
|
||||||
this.actor.accessible_role = Atk.Role.LABEL;
|
|
||||||
|
|
||||||
this._clockDisplay = new St.Label();
|
this._clockDisplay = new St.Label();
|
||||||
this.actor.add_actor(this._clockDisplay);
|
this.actor.add_actor(this._clockDisplay);
|
||||||
|
|
||||||
@ -70,16 +66,8 @@ const DateMenuButton = new Lang.Class({
|
|||||||
this._date.style_class = 'datemenu-date-label';
|
this._date.style_class = 'datemenu-date-label';
|
||||||
vbox.add(this._date);
|
vbox.add(this._date);
|
||||||
|
|
||||||
if (Main.sessionMode.showCalendarEvents) {
|
this._eventList = new Calendar.EventsList();
|
||||||
this._eventSource = new Calendar.DBusEventSource();
|
this._calendar = new Calendar.Calendar();
|
||||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
|
||||||
} else {
|
|
||||||
this._eventSource = null;
|
|
||||||
this._eventList = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calendar
|
|
||||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
|
||||||
|
|
||||||
this._calendar.connect('selected-date-changed',
|
this._calendar.connect('selected-date-changed',
|
||||||
Lang.bind(this, function(calendar, date) {
|
Lang.bind(this, function(calendar, date) {
|
||||||
@ -91,37 +79,34 @@ const DateMenuButton = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
vbox.add(this._calendar.actor);
|
vbox.add(this._calendar.actor);
|
||||||
|
|
||||||
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
|
||||||
|
|
||||||
|
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||||
|
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||||
|
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
|
||||||
|
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
|
||||||
|
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
|
||||||
|
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
|
||||||
|
Shell.AppSystem.get_default().connect('installed-changed',
|
||||||
|
Lang.bind(this, this._appInstalledChanged));
|
||||||
|
|
||||||
|
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
|
||||||
if (item) {
|
if (item) {
|
||||||
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
item.actor.show_on_set_parent = false;
|
||||||
separator.setColumnWidths(1);
|
|
||||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
|
||||||
|
|
||||||
item.actor.can_focus = false;
|
|
||||||
item.actor.reparent(vbox);
|
item.actor.reparent(vbox);
|
||||||
|
this._dateAndTimeSeparator = separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Main.sessionMode.showCalendarEvents) {
|
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||||
// Add vertical separator
|
pseudo_class: 'highlighted' });
|
||||||
|
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||||
|
hbox.add(this._separator);
|
||||||
|
|
||||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
// Fill up the second column
|
||||||
pseudo_class: 'highlighted' });
|
hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
|
||||||
hbox.add(item);
|
|
||||||
|
|
||||||
// Fill up the second column
|
|
||||||
vbox = new St.BoxLayout({name: 'calendarEventsArea',
|
|
||||||
vertical: true});
|
|
||||||
hbox.add(vbox, { expand: true });
|
|
||||||
|
|
||||||
// Event list
|
|
||||||
vbox.add(this._eventList.actor, { expand: true });
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
|
||||||
item.actor.can_focus = false;
|
|
||||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whenever the menu is opened, select today
|
// Whenever the menu is opened, select today
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||||
@ -151,6 +136,61 @@ const DateMenuButton = new Lang.Class({
|
|||||||
this._clock = new GnomeDesktop.WallClock();
|
this._clock = new GnomeDesktop.WallClock();
|
||||||
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
||||||
this._updateClockAndDate();
|
this._updateClockAndDate();
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
this._sessionUpdated();
|
||||||
|
},
|
||||||
|
|
||||||
|
_appInstalledChanged: function() {
|
||||||
|
this._calendarApp = undefined;
|
||||||
|
this._updateEventsVisibility();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateEventsVisibility: function() {
|
||||||
|
let visible = this._eventSource.hasCalendars;
|
||||||
|
this._openCalendarItem.actor.visible = visible &&
|
||||||
|
(this._getCalendarApp() != null);
|
||||||
|
this._openClocksItem.actor.visible = visible &&
|
||||||
|
(this._getClockApp() != null);
|
||||||
|
this._separator.visible = visible;
|
||||||
|
this._eventList.actor.visible = visible;
|
||||||
|
if (visible) {
|
||||||
|
let alignment = 0.25;
|
||||||
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||||
|
alignment = 1.0 - alignment;
|
||||||
|
this.menu._arrowAlignment = alignment;
|
||||||
|
} else {
|
||||||
|
this.menu._arrowAlignment = 0.5;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_setEventSource: function(eventSource) {
|
||||||
|
if (this._eventSource)
|
||||||
|
this._eventSource.destroy();
|
||||||
|
|
||||||
|
this._calendar.setEventSource(eventSource);
|
||||||
|
this._eventList.setEventSource(eventSource);
|
||||||
|
|
||||||
|
this._eventSource = eventSource;
|
||||||
|
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
|
||||||
|
this._updateEventsVisibility();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
let eventSource;
|
||||||
|
let showEvents = Main.sessionMode.showCalendarEvents;
|
||||||
|
if (showEvents) {
|
||||||
|
eventSource = new Calendar.DBusEventSource();
|
||||||
|
} else {
|
||||||
|
eventSource = new Calendar.EmptyEventSource();
|
||||||
|
}
|
||||||
|
this._setEventSource(eventSource);
|
||||||
|
this._updateEventsVisibility();
|
||||||
|
|
||||||
|
// This needs to be handled manually, as the code to
|
||||||
|
// autohide separators doesn't work across the vbox
|
||||||
|
this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateClockAndDate: function() {
|
_updateClockAndDate: function() {
|
||||||
@ -163,27 +203,34 @@ const DateMenuButton = new Lang.Class({
|
|||||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getCalendarApp: function() {
|
||||||
|
if (this._calendarApp !== undefined)
|
||||||
|
return this._calendarApp;
|
||||||
|
|
||||||
|
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
|
||||||
|
if (apps && (apps.length > 0))
|
||||||
|
this._calendarApp = apps[0];
|
||||||
|
else
|
||||||
|
this._calendarApp = null;
|
||||||
|
return this._calendarApp;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getClockApp: function() {
|
||||||
|
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||||
|
},
|
||||||
|
|
||||||
_onOpenCalendarActivate: function() {
|
_onOpenCalendarActivate: function() {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
|
||||||
let tool = calendarSettings.get_string('exec');
|
let app = this._getCalendarApp();
|
||||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
if (app.get_id() == 'evolution.desktop')
|
||||||
// TODO: pass the selected day
|
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
app.launch([], global.create_app_launch_context());
|
||||||
app.activate();
|
},
|
||||||
} else {
|
|
||||||
let needTerm = calendarSettings.get_boolean('needs-term');
|
_onOpenClocksActivate: function() {
|
||||||
if (needTerm) {
|
this.menu.close();
|
||||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
let app = this._getClockApp();
|
||||||
let term = terminalSettings.get_string('exec');
|
app.activate();
|
||||||
let arg = terminalSettings.get_string('exec-arg');
|
|
||||||
if (arg != '')
|
|
||||||
Util.spawn([term, arg, tool]);
|
|
||||||
else
|
|
||||||
Util.spawn([term, tool]);
|
|
||||||
} else {
|
|
||||||
Util.spawnCommandLine(tool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
65
js/ui/dnd.js
@ -43,9 +43,7 @@ let dragMonitors = [];
|
|||||||
|
|
||||||
function _getEventHandlerActor() {
|
function _getEventHandlerActor() {
|
||||||
if (!eventHandlerActor) {
|
if (!eventHandlerActor) {
|
||||||
eventHandlerActor = new Clutter.Rectangle();
|
eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
|
||||||
eventHandlerActor.width = 0;
|
|
||||||
eventHandlerActor.height = 0;
|
|
||||||
Main.uiGroup.add_actor(eventHandlerActor);
|
Main.uiGroup.add_actor(eventHandlerActor);
|
||||||
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
|
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
|
||||||
// when you've grabbed the pointer.
|
// when you've grabbed the pointer.
|
||||||
@ -85,11 +83,13 @@ const _Draggable = new Lang.Class({
|
|||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||||
this._actorDestroyed = true;
|
this._actorDestroyed = true;
|
||||||
|
|
||||||
// If the drag actor is destroyed and we were going to fix
|
// If the drag actor is destroyed and we were going to fix
|
||||||
// up its hover state, fix up the parent hover state instead
|
// up its hover state, fix up the parent hover state instead
|
||||||
if (this.actor == this._firstLeaveActor)
|
if (this.actor == this._firstLeaveActor)
|
||||||
this._firstLeaveActor = this._dragOrigParent;
|
this._firstLeaveActor = this._dragOrigParent;
|
||||||
if (this._dragInProgress)
|
|
||||||
|
if (this._dragInProgress && this._dragCancellable)
|
||||||
this._cancelDrag(global.get_current_time());
|
this._cancelDrag(global.get_current_time());
|
||||||
this.disconnectAll();
|
this.disconnectAll();
|
||||||
}));
|
}));
|
||||||
@ -102,6 +102,7 @@ const _Draggable = new Lang.Class({
|
|||||||
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
||||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||||
|
this._dragCancellable = true;
|
||||||
|
|
||||||
// During the drag, we eat enter/leave events so that actors don't prelight.
|
// During the drag, we eat enter/leave events so that actors don't prelight.
|
||||||
// But we remember the actors that we first left/last entered so we can
|
// But we remember the actors that we first left/last entered so we can
|
||||||
@ -136,25 +137,26 @@ const _Draggable = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_ungrabActor: function() {
|
_ungrabActor: function() {
|
||||||
Clutter.ungrab_pointer();
|
|
||||||
if (!this._onEventId)
|
if (!this._onEventId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Clutter.ungrab_pointer();
|
||||||
this.actor.disconnect(this._onEventId);
|
this.actor.disconnect(this._onEventId);
|
||||||
this._onEventId = null;
|
this._onEventId = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_grabEvents: function() {
|
_grabEvents: function() {
|
||||||
if (!this._eventsGrabbed) {
|
if (!this._eventsGrabbed) {
|
||||||
Clutter.grab_pointer(_getEventHandlerActor());
|
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
|
||||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
if (this._eventsGrabbed)
|
||||||
this._eventsGrabbed = true;
|
Clutter.grab_pointer(_getEventHandlerActor());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ungrabEvents: function() {
|
_ungrabEvents: function() {
|
||||||
if (this._eventsGrabbed) {
|
if (this._eventsGrabbed) {
|
||||||
Clutter.ungrab_pointer();
|
Clutter.ungrab_pointer();
|
||||||
Clutter.ungrab_keyboard();
|
Main.popModal(_getEventHandlerActor());
|
||||||
this._eventsGrabbed = false;
|
this._eventsGrabbed = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -203,6 +205,19 @@ const _Draggable = new Lang.Class({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fakeRelease:
|
||||||
|
*
|
||||||
|
* Fake a release event.
|
||||||
|
* Must be called if you want to intercept release events on draggable
|
||||||
|
* actors for other purposes (for example if you're using
|
||||||
|
* PopupMenu.ignoreRelease())
|
||||||
|
*/
|
||||||
|
fakeRelease: function() {
|
||||||
|
this._buttonDown = false;
|
||||||
|
this._ungrabActor();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* startDrag:
|
* startDrag:
|
||||||
* @stageX: X coordinate of event
|
* @stageX: X coordinate of event
|
||||||
@ -234,7 +249,11 @@ const _Draggable = new Lang.Class({
|
|||||||
this._dragY = this._dragStartY = stageY;
|
this._dragY = this._dragStartY = stageY;
|
||||||
|
|
||||||
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
||||||
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
|
this._dragActor = this.actor._delegate.getDragActor();
|
||||||
|
this._dragActor.reparent(Main.uiGroup);
|
||||||
|
this._dragActor.raise_top();
|
||||||
|
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||||
|
|
||||||
// Drag actor does not always have to be the same as actor. For example drag actor
|
// Drag actor does not always have to be the same as actor. For example drag actor
|
||||||
// can be an image that's part of the actor. So to perform "snap back" correctly we need
|
// can be an image that's part of the actor. So to perform "snap back" correctly we need
|
||||||
// to know what was the drag actor source.
|
// to know what was the drag actor source.
|
||||||
@ -263,27 +282,28 @@ const _Draggable = new Lang.Class({
|
|||||||
this._dragOffsetY = this._dragActor.y - this._dragStartY;
|
this._dragOffsetY = this._dragActor.y - this._dragStartY;
|
||||||
} else {
|
} else {
|
||||||
this._dragActor = this.actor;
|
this._dragActor = this.actor;
|
||||||
|
|
||||||
this._dragActorSource = undefined;
|
this._dragActorSource = undefined;
|
||||||
this._dragOrigParent = this.actor.get_parent();
|
this._dragOrigParent = this.actor.get_parent();
|
||||||
this._dragOrigX = this._dragActor.x;
|
this._dragOrigX = this._dragActor.x;
|
||||||
this._dragOrigY = this._dragActor.y;
|
this._dragOrigY = this._dragActor.y;
|
||||||
this._dragOrigScale = this._dragActor.scale_x;
|
this._dragOrigScale = this._dragActor.scale_x;
|
||||||
|
|
||||||
|
// Set the actor's scale such that it will keep the same
|
||||||
|
// transformed size when it's reparented to the uiGroup
|
||||||
|
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
|
||||||
|
this._dragActor.set_scale(scaledWidth / this.actor.width,
|
||||||
|
scaledHeight / this.actor.height);
|
||||||
|
|
||||||
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
|
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
|
||||||
this._dragOffsetX = actorStageX - this._dragStartX;
|
this._dragOffsetX = actorStageX - this._dragStartX;
|
||||||
this._dragOffsetY = actorStageY - this._dragStartY;
|
this._dragOffsetY = actorStageY - this._dragStartY;
|
||||||
|
|
||||||
// Set the actor's scale such that it will keep the same
|
this._dragActor.reparent(Main.uiGroup);
|
||||||
// transformed size when it's reparented to the uiGroup
|
this._dragActor.raise_top();
|
||||||
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
|
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||||
this.actor.set_scale(scaledWidth / this.actor.width,
|
|
||||||
scaledHeight / this.actor.height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dragActor.reparent(Main.uiGroup);
|
|
||||||
this._dragActor.raise_top();
|
|
||||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
|
||||||
|
|
||||||
this._dragOrigOpacity = this._dragActor.opacity;
|
this._dragOrigOpacity = this._dragActor.opacity;
|
||||||
if (this._dragActorOpacity != undefined)
|
if (this._dragActorOpacity != undefined)
|
||||||
this._dragActor.opacity = this._dragActorOpacity;
|
this._dragActor.opacity = this._dragActorOpacity;
|
||||||
@ -420,6 +440,11 @@ const _Draggable = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At this point it is too late to cancel a drag by destroying
|
||||||
|
// the actor, the fate of which is decided by acceptDrop and its
|
||||||
|
// side-effects
|
||||||
|
this._dragCancellable = false;
|
||||||
|
|
||||||
while (target) {
|
while (target) {
|
||||||
if (target._delegate && target._delegate.acceptDrop) {
|
if (target._delegate && target._delegate.acceptDrop) {
|
||||||
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
|
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
|
||||||
@ -428,8 +453,6 @@ const _Draggable = new Lang.Class({
|
|||||||
targX,
|
targX,
|
||||||
targY,
|
targY,
|
||||||
event.get_time())) {
|
event.get_time())) {
|
||||||
if (this._actorDestroyed)
|
|
||||||
return true;
|
|
||||||
// If it accepted the drop without taking the actor,
|
// If it accepted the drop without taking the actor,
|
||||||
// handle it ourselves.
|
// handle it ourselves.
|
||||||
if (this._dragActor.get_parent() == Main.uiGroup) {
|
if (this._dragActor.get_parent() == Main.uiGroup) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const AccountsService = imports.gi.AccountsService;
|
const AccountsService = imports.gi.AccountsService;
|
||||||
@ -34,6 +35,7 @@ const GnomeSession = imports.misc.gnomeSession;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
let _endSessionDialog = null;
|
let _endSessionDialog = null;
|
||||||
|
|
||||||
@ -49,6 +51,7 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
|
|||||||
<arg type="u" direction="in" />
|
<arg type="u" direction="in" />
|
||||||
<arg type="ao" direction="in" />
|
<arg type="ao" direction="in" />
|
||||||
</method>
|
</method>
|
||||||
|
<method name="Close" />
|
||||||
<signal name="ConfirmedLogout" />
|
<signal name="ConfirmedLogout" />
|
||||||
<signal name="ConfirmedReboot" />
|
<signal name="ConfirmedReboot" />
|
||||||
<signal name="ConfirmedShutdown" />
|
<signal name="ConfirmedShutdown" />
|
||||||
@ -89,7 +92,7 @@ const shutdownDialogContent = {
|
|||||||
label: C_("button", "Restart") },
|
label: C_("button", "Restart") },
|
||||||
{ signal: 'ConfirmedShutdown',
|
{ signal: 'ConfirmedShutdown',
|
||||||
label: C_("button", "Power Off") }],
|
label: C_("button", "Power Off") }],
|
||||||
iconName: 'system-shutdown',
|
iconName: 'system-shutdown-symbolic',
|
||||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -104,7 +107,7 @@ const restartDialogContent = {
|
|||||||
endDescription: _("Restarting the system."),
|
endDescription: _("Restarting the system."),
|
||||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||||
label: C_("button", "Restart") }],
|
label: C_("button", "Restart") }],
|
||||||
iconName: 'system-shutdown',
|
iconName: 'view-refresh-symbolic',
|
||||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,6 +163,7 @@ const ListItem = new Lang.Class({
|
|||||||
|
|
||||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
this._descriptionLabel = new St.Label({ text: this._reason,
|
||||||
style_class: 'end-session-dialog-app-list-item-description' });
|
style_class: 'end-session-dialog-app-list-item-description' });
|
||||||
|
this.actor.label_actor = this._nameLabel;
|
||||||
textLayout.add(this._descriptionLabel,
|
textLayout.add(this._descriptionLabel,
|
||||||
{ expand: true,
|
{ expand: true,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
@ -221,7 +225,8 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent({ styleClass: 'end-session-dialog' });
|
this.parent({ styleClass: 'end-session-dialog',
|
||||||
|
destroyOnClose: false });
|
||||||
|
|
||||||
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
||||||
|
|
||||||
@ -279,11 +284,7 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
scrollView.hide();
|
scrollView.hide();
|
||||||
|
|
||||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||||
scrollView.add_actor(this._applicationList,
|
scrollView.add_actor(this._applicationList);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
this._applicationList.connect('actor-added',
|
this._applicationList.connect('actor-added',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
@ -306,41 +307,6 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._user.disconnect(this._userChangedId);
|
this._user.disconnect(this._userChangedId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_setIconFromFile: function(iconFile, styleClass) {
|
|
||||||
if (styleClass)
|
|
||||||
this._iconBin.set_style_class_name(styleClass);
|
|
||||||
this._iconBin.set_style(null);
|
|
||||||
|
|
||||||
this._iconBin.child = null;
|
|
||||||
if (iconFile) {
|
|
||||||
this._iconBin.show();
|
|
||||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
|
||||||
'background-size: contain;');
|
|
||||||
} else {
|
|
||||||
this._iconBin.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_setIconFromName: function(iconName, styleClass) {
|
|
||||||
if (styleClass)
|
|
||||||
this._iconBin.set_style_class_name(styleClass);
|
|
||||||
this._iconBin.set_style(null);
|
|
||||||
|
|
||||||
if (iconName != null) {
|
|
||||||
let textureCache = St.TextureCache.get_default();
|
|
||||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
|
||||||
iconName,
|
|
||||||
St.IconType.SYMBOLIC,
|
|
||||||
_DIALOG_ICON_SIZE);
|
|
||||||
|
|
||||||
this._iconBin.child = icon;
|
|
||||||
this._iconBin.show();
|
|
||||||
} else {
|
|
||||||
this._iconBin.child = null;
|
|
||||||
this._iconBin.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDescription: function() {
|
_updateDescription: function() {
|
||||||
if (this.state != ModalDialog.State.OPENING &&
|
if (this.state != ModalDialog.State.OPENING &&
|
||||||
this.state != ModalDialog.State.OPENED)
|
this.state != ModalDialog.State.OPENED)
|
||||||
@ -389,16 +355,16 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
|
if (dialogContent.iconName) {
|
||||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName,
|
||||||
let iconFile = this._user.get_icon_file();
|
icon_size: _DIALOG_ICON_SIZE,
|
||||||
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
style_class: dialogContent.iconStyleClass });
|
||||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
} else {
|
||||||
else
|
let avatarWidget = new UserWidget.Avatar(this._user,
|
||||||
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
|
{ iconSize: _DIALOG_ICON_SIZE,
|
||||||
} else if (dialogContent.iconName) {
|
styleClass: dialogContent.iconStyleClass });
|
||||||
this._setIconFromName(dialogContent.iconName,
|
this._iconBin.child = avatarWidget.actor;
|
||||||
dialogContent.iconStyleClass);
|
avatarWidget.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateDescription();
|
this._updateDescription();
|
||||||
@ -414,7 +380,12 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
let signal = dialogContent.confirmButtons[i].signal;
|
let signal = dialogContent.confirmButtons[i].signal;
|
||||||
let label = dialogContent.confirmButtons[i].label;
|
let label = dialogContent.confirmButtons[i].label;
|
||||||
buttons.push({ action: Lang.bind(this, function() {
|
buttons.push({ action: Lang.bind(this, function() {
|
||||||
this._confirm(signal);
|
this.close(true);
|
||||||
|
let signalId = this.connect('closed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.disconnect(signalId);
|
||||||
|
this._confirm(signal);
|
||||||
|
}));
|
||||||
}),
|
}),
|
||||||
label: label });
|
label: label });
|
||||||
}
|
}
|
||||||
@ -422,15 +393,17 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this.setButtons(buttons);
|
this.setButtons(buttons);
|
||||||
},
|
},
|
||||||
|
|
||||||
close: function() {
|
close: function(skipSignal) {
|
||||||
this.parent();
|
this.parent();
|
||||||
this._dbusImpl.emit_signal('Closed', null);
|
|
||||||
|
if (!skipSignal)
|
||||||
|
this._dbusImpl.emit_signal('Closed', null);
|
||||||
},
|
},
|
||||||
|
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
this._stopTimer();
|
this._stopTimer();
|
||||||
this._dbusImpl.emit_signal('Canceled', null);
|
this._dbusImpl.emit_signal('Canceled', null);
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_confirm: function(signal) {
|
_confirm: function(signal) {
|
||||||
@ -445,22 +418,35 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_startTimer: function() {
|
_startTimer: function() {
|
||||||
|
let startTime = GLib.get_monotonic_time();
|
||||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||||
Tweener.addTween(this,
|
this._updateDescription();
|
||||||
{ _secondsLeft: 0,
|
|
||||||
time: this._secondsLeft,
|
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
|
||||||
transition: 'linear',
|
function() {
|
||||||
onUpdate: Lang.bind(this, this._updateDescription),
|
let currentTime = GLib.get_monotonic_time();
|
||||||
onComplete: Lang.bind(this, function() {
|
let secondsElapsed = ((currentTime - startTime) / 1000000);
|
||||||
let dialogContent = DialogContent[this._type];
|
|
||||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
|
||||||
this._confirm(button.signal);
|
if (this._secondsLeft > 0) {
|
||||||
}),
|
this._updateDescription();
|
||||||
});
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dialogContent = DialogContent[this._type];
|
||||||
|
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||||
|
this._confirm(button.signal);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_stopTimer: function() {
|
_stopTimer: function() {
|
||||||
Tweener.removeTweens(this);
|
if (this._timerId != 0) {
|
||||||
|
Mainloop.source_remove(this._timerId);
|
||||||
|
this._timerId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
this._secondsLeft = 0;
|
this._secondsLeft = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -477,7 +463,7 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
let item = new ListItem(app, reason);
|
let item = new ListItem(app, reason);
|
||||||
item.connect('activate',
|
item.connect('activate',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
}));
|
}));
|
||||||
this._applicationList.add(item.actor, { x_fill: true });
|
this._applicationList.add(item.actor, { x_fill: true });
|
||||||
this._stopTimer();
|
this._stopTimer();
|
||||||
@ -525,5 +511,9 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
invocation.return_value(null);
|
invocation.return_value(null);
|
||||||
this.disconnect(signalId);
|
this.disconnect(signalId);
|
||||||
}));
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
Close: function(parameters, invocation) {
|
||||||
|
this.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -60,7 +60,7 @@ function uninstallExtension(uuid) {
|
|||||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ExtensionSystem.unloadExtension(uuid))
|
if (!ExtensionSystem.unloadExtension(extension))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FileUtils.recursivelyDeleteDir(extension.dir, true);
|
FileUtils.recursivelyDeleteDir(extension.dir, true);
|
||||||
@ -82,7 +82,7 @@ function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
|
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
|
||||||
let contents = message.response_body.flatten().as_bytes();
|
let contents = message.response_body.flatten().get_as_bytes();
|
||||||
stream.output_stream.write_bytes(contents, null);
|
stream.output_stream.write_bytes(contents, null);
|
||||||
stream.close(null);
|
stream.close(null);
|
||||||
let [success, pid] = GLib.spawn_async(null,
|
let [success, pid] = GLib.spawn_async(null,
|
||||||
@ -124,7 +124,7 @@ function updateExtension(uuid) {
|
|||||||
let oldExtension = ExtensionUtils.extensions[uuid];
|
let oldExtension = ExtensionUtils.extensions[uuid];
|
||||||
let extensionDir = oldExtension.dir;
|
let extensionDir = oldExtension.dir;
|
||||||
|
|
||||||
if (!ExtensionSystem.unloadExtension(uuid))
|
if (!ExtensionSystem.unloadExtension(oldExtension))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
|
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
|
||||||
@ -135,7 +135,7 @@ function updateExtension(uuid) {
|
|||||||
try {
|
try {
|
||||||
ExtensionSystem.loadExtension(extension);
|
ExtensionSystem.loadExtension(extension);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
ExtensionSystem.unloadExtension(uuid);
|
ExtensionSystem.unloadExtension(extension);
|
||||||
|
|
||||||
logError(e, 'Error loading extension %s'.format(uuid));
|
logError(e, 'Error loading extension %s'.format(uuid));
|
||||||
|
|
||||||
@ -197,7 +197,8 @@ const InstallExtensionDialog = new Lang.Class({
|
|||||||
key: Clutter.Escape
|
key: Clutter.Escape
|
||||||
},
|
},
|
||||||
{ label: _("Install"),
|
{ label: _("Install"),
|
||||||
action: Lang.bind(this, this._onInstallButtonPressed)
|
action: Lang.bind(this, this._onInstallButtonPressed),
|
||||||
|
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);
|
||||||
@ -214,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onCancelButtonPressed: function(button, event) {
|
_onCancelButtonPressed: function(button, event) {
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -256,7 +257,7 @@ const InstallExtensionDialog = new Lang.Class({
|
|||||||
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ const Gio = imports.gi.Gio;
|
|||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const ExtensionState = {
|
const ExtensionState = {
|
||||||
ENABLED: 1,
|
ENABLED: 1,
|
||||||
@ -38,6 +39,9 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
|
|||||||
|
|
||||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||||
|
|
||||||
|
var initted = false;
|
||||||
|
var enabled;
|
||||||
|
|
||||||
function disableExtension(uuid) {
|
function disableExtension(uuid) {
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
if (!extension)
|
if (!extension)
|
||||||
@ -102,15 +106,19 @@ function enableExtension(uuid) {
|
|||||||
|
|
||||||
extensionOrder.push(uuid);
|
extensionOrder.push(uuid);
|
||||||
|
|
||||||
extension.stateObj.enable();
|
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
|
||||||
|
for (let i = 0; i < stylesheetNames.length; i++) {
|
||||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
|
||||||
if (stylesheetFile.query_exists(null)) {
|
if (stylesheetFile.query_exists(null)) {
|
||||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||||
theme.load_stylesheet(stylesheetFile.get_path());
|
theme.load_stylesheet(stylesheetFile.get_path());
|
||||||
extension.stylesheet = stylesheetFile;
|
extension.stylesheet = stylesheetFile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension.stateObj.enable();
|
||||||
|
|
||||||
extension.state = ExtensionState.ENABLED;
|
extension.state = ExtensionState.ENABLED;
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
}
|
}
|
||||||
@ -122,13 +130,10 @@ function logExtensionError(uuid, error) {
|
|||||||
|
|
||||||
let message = '' + error;
|
let message = '' + error;
|
||||||
|
|
||||||
if (error.state)
|
extension.state = ExtensionState.ERROR;
|
||||||
extension.state = error.state;
|
|
||||||
else
|
|
||||||
extension.state = ExtensionState.ERROR;
|
|
||||||
|
|
||||||
if (!extension.errors)
|
if (!extension.errors)
|
||||||
extension.errors = [];
|
extension.errors = [];
|
||||||
|
extension.errors.push(message);
|
||||||
|
|
||||||
log('Extension "%s" had error: %s'.format(uuid, message));
|
log('Extension "%s" had error: %s'.format(uuid, message));
|
||||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||||
@ -141,40 +146,47 @@ function loadExtension(extension) {
|
|||||||
extension.state = ExtensionState.ERROR;
|
extension.state = ExtensionState.ERROR;
|
||||||
|
|
||||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||||
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version');
|
extension.state = ExtensionState.OUT_OF_DATE;
|
||||||
error.state = ExtensionState.OUT_OF_DATE;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
|
||||||
if (enabled) {
|
|
||||||
initExtension(extension.uuid);
|
|
||||||
if (extension.state == ExtensionState.DISABLED)
|
|
||||||
enableExtension(extension.uuid);
|
|
||||||
} else {
|
} else {
|
||||||
extension.state = ExtensionState.INITIALIZED;
|
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||||
|
if (enabled) {
|
||||||
|
initExtension(extension.uuid);
|
||||||
|
if (extension.state == ExtensionState.DISABLED)
|
||||||
|
enableExtension(extension.uuid);
|
||||||
|
} else {
|
||||||
|
extension.state = ExtensionState.INITIALIZED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unloadExtension(uuid) {
|
function unloadExtension(extension) {
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
|
||||||
if (!extension)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||||
// but it will be removed on next reboot, and hopefully nothing
|
// but it will be removed on next reboot, and hopefully nothing
|
||||||
// broke too much.
|
// broke too much.
|
||||||
disableExtension(uuid);
|
disableExtension(extension.uuid);
|
||||||
|
|
||||||
extension.state = ExtensionState.UNINSTALLED;
|
extension.state = ExtensionState.UNINSTALLED;
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
|
|
||||||
delete ExtensionUtils.extensions[uuid];
|
delete ExtensionUtils.extensions[extension.uuid];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reloadExtension(oldExtension) {
|
||||||
|
// Grab the things we'll need to pass to createExtensionObject
|
||||||
|
// to reload it.
|
||||||
|
let { uuid: uuid, dir: dir, type: type } = oldExtension;
|
||||||
|
|
||||||
|
// Then unload the old extension.
|
||||||
|
unloadExtension(oldExtension);
|
||||||
|
|
||||||
|
// Now, recreate the extension and load it.
|
||||||
|
let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||||
|
loadExtension(newExtension);
|
||||||
|
}
|
||||||
|
|
||||||
function initExtension(uuid) {
|
function initExtension(uuid) {
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
let dir = extension.dir;
|
let dir = extension.dir;
|
||||||
@ -204,8 +216,19 @@ function initExtension(uuid) {
|
|||||||
_signals.emit('extension-loaded', uuid);
|
_signals.emit('extension-loaded', uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEnabledExtensions() {
|
||||||
|
let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||||
|
if (!Array.isArray(Main.sessionMode.enabledExtensions))
|
||||||
|
return extensions;
|
||||||
|
|
||||||
|
return Main.sessionMode.enabledExtensions.concat(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
function onEnabledExtensionsChanged() {
|
function onEnabledExtensionsChanged() {
|
||||||
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
let newEnabledExtensions = getEnabledExtensions();
|
||||||
|
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
// Find and enable all the newly enabled extensions: UUIDs found in the
|
// Find and enable all the newly enabled extensions: UUIDs found in the
|
||||||
// new setting, but not in the old one.
|
// new setting, but not in the old one.
|
||||||
@ -234,9 +257,9 @@ function onEnabledExtensionsChanged() {
|
|||||||
enabledExtensions = newEnabledExtensions;
|
enabledExtensions = newEnabledExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadExtensions() {
|
function _loadExtensions() {
|
||||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
enabledExtensions = getEnabledExtensions();
|
||||||
|
|
||||||
let finder = new ExtensionUtils.ExtensionFinder();
|
let finder = new ExtensionUtils.ExtensionFinder();
|
||||||
finder.connect('extension-found', function(signals, extension) {
|
finder.connect('extension-found', function(signals, extension) {
|
||||||
@ -248,3 +271,50 @@ function loadExtensions() {
|
|||||||
});
|
});
|
||||||
finder.scanExtensions();
|
finder.scanExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enableAllExtensions() {
|
||||||
|
if (enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!initted) {
|
||||||
|
_loadExtensions();
|
||||||
|
initted = true;
|
||||||
|
} else {
|
||||||
|
enabledExtensions.forEach(function(uuid) {
|
||||||
|
enableExtension(uuid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableAllExtensions() {
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (initted) {
|
||||||
|
extensionOrder.slice().reverse().forEach(function(uuid) {
|
||||||
|
disableExtension(uuid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sessionUpdated() {
|
||||||
|
// For now sessionMode.allowExtensions controls extensions from both the
|
||||||
|
// 'enabled-extensions' preference and the sessionMode.enabledExtensions
|
||||||
|
// property; it might make sense to make enabledExtensions independent
|
||||||
|
// from allowExtensions in the future
|
||||||
|
if (Main.sessionMode.allowExtensions) {
|
||||||
|
if (initted)
|
||||||
|
onEnabledExtensionsChanged();
|
||||||
|
enableAllExtensions();
|
||||||
|
} else {
|
||||||
|
disableAllExtensions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Main.sessionMode.connect('updated', _sessionUpdated);
|
||||||
|
_sessionUpdated();
|
||||||
|
}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Lang = imports.lang;
|
|
||||||
|
|
||||||
const Lightbox = imports.ui.lightbox;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const Tweener = imports.ui.tweener;
|
|
||||||
|
|
||||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
|
||||||
|
|
||||||
const Flashspot = new Lang.Class({
|
|
||||||
Name: 'Flashspot',
|
|
||||||
Extends: Lightbox.Lightbox,
|
|
||||||
|
|
||||||
_init: function(area) {
|
|
||||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
|
||||||
width: area.width,
|
|
||||||
height: area.height });
|
|
||||||
|
|
||||||
this.actor.style_class = 'flashspot';
|
|
||||||
this.actor.set_position(area.x, area.y);
|
|
||||||
},
|
|
||||||
|
|
||||||
fire: function() {
|
|
||||||
this.actor.opacity = 0;
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: FLASHSPOT_ANIMATION_TIME,
|
|
||||||
transition: 'linear',
|
|
||||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
|
||||||
});
|
|
||||||
this.actor.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFireShowComplete: function() {
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ opacity: 0,
|
|
||||||
time: FLASHSPOT_ANIMATION_TIME,
|
|
||||||
transition: 'linear',
|
|
||||||
onComplete: Lang.bind(this, function() {
|
|
||||||
this.destroy();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
376
js/ui/grabHelper.js
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
// GrabHelper:
|
||||||
|
// @owner: the actor that owns the GrabHelper
|
||||||
|
// @params: optional parameters to pass to Main.pushModal()
|
||||||
|
//
|
||||||
|
// Creates a new GrabHelper object, for dealing with keyboard and pointer grabs
|
||||||
|
// associated with a set of actors.
|
||||||
|
//
|
||||||
|
// Note that the grab can be automatically dropped at any time by the user, and
|
||||||
|
// your code just needs to deal with it; you shouldn't adjust behavior directly
|
||||||
|
// after you call ungrab(), but instead pass an 'onUngrab' callback when you
|
||||||
|
// call grab().
|
||||||
|
const GrabHelper = new Lang.Class({
|
||||||
|
Name: 'GrabHelper',
|
||||||
|
|
||||||
|
_init: function(owner, params) {
|
||||||
|
this._owner = owner;
|
||||||
|
this._modalParams = params;
|
||||||
|
|
||||||
|
this._grabStack = [];
|
||||||
|
|
||||||
|
this._actors = [];
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
this._keyFocusNotifyId = 0;
|
||||||
|
this._focusWindowChangedId = 0;
|
||||||
|
this._ignoreRelease = false;
|
||||||
|
this._isUngrabbingCount = 0;
|
||||||
|
|
||||||
|
this._modalCount = 0;
|
||||||
|
this._grabFocusCount = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
// addActor:
|
||||||
|
// @actor: an actor
|
||||||
|
//
|
||||||
|
// Adds @actor to the set of actors that are allowed to process events
|
||||||
|
// during a grab.
|
||||||
|
addActor: function(actor) {
|
||||||
|
actor.__grabHelperDestroyId = actor.connect('destroy', Lang.bind(this, function() { this.removeActor(actor); }));
|
||||||
|
this._actors.push(actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
// removeActor:
|
||||||
|
// @actor: an actor
|
||||||
|
//
|
||||||
|
// Removes @actor from the set of actors that are allowed to
|
||||||
|
// process events during a grab.
|
||||||
|
removeActor: function(actor) {
|
||||||
|
let index = this._actors.indexOf(actor);
|
||||||
|
if (index != -1)
|
||||||
|
this._actors.splice(index, 1);
|
||||||
|
if (actor.__grabHelperDestroyId) {
|
||||||
|
actor.disconnect(actor.__grabHelperDestroyId);
|
||||||
|
delete actor.__grabHelperDestroyId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_isWithinGrabbedActor: function(actor) {
|
||||||
|
let currentActor = this.currentGrab.actor;
|
||||||
|
while (actor) {
|
||||||
|
if (this._actors.indexOf(actor) != -1)
|
||||||
|
return true;
|
||||||
|
if (actor == currentActor)
|
||||||
|
return true;
|
||||||
|
actor = actor.get_parent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get currentGrab() {
|
||||||
|
return this._grabStack[this._grabStack.length - 1] || {};
|
||||||
|
},
|
||||||
|
|
||||||
|
get grabbed() {
|
||||||
|
return this._grabStack.length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
get grabStack() {
|
||||||
|
return this._grabStack;
|
||||||
|
},
|
||||||
|
|
||||||
|
_findStackIndex: function(actor) {
|
||||||
|
if (!actor)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._grabStack.length; i++) {
|
||||||
|
if (this._grabStack[i].actor === actor)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_actorInGrabStack: function(actor) {
|
||||||
|
while (actor) {
|
||||||
|
let idx = this._findStackIndex(actor);
|
||||||
|
if (idx >= 0)
|
||||||
|
return idx;
|
||||||
|
actor = actor.get_parent();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
isActorGrabbed: function(actor) {
|
||||||
|
return this._findStackIndex(actor) >= 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
// grab:
|
||||||
|
// @params: A bunch of parameters, see below
|
||||||
|
//
|
||||||
|
// Grabs the mouse and keyboard, according to the GrabHelper's
|
||||||
|
// parameters. If @newFocus is not %null, then the keyboard focus
|
||||||
|
// is moved to the first #StWidget:can-focus widget inside it.
|
||||||
|
//
|
||||||
|
// The grab will automatically be dropped if:
|
||||||
|
// - The user clicks outside the grabbed actors
|
||||||
|
// - The user types Escape
|
||||||
|
// - The keyboard focus is moved outside the grabbed actors
|
||||||
|
// - A window is focused
|
||||||
|
//
|
||||||
|
// If @params.actor is not null, then it will be focused as the
|
||||||
|
// new actor. If you attempt to grab an already focused actor, the
|
||||||
|
// request to be focused will be ignored. The actor will not be
|
||||||
|
// added to the grab stack, so do not call a paired ungrab().
|
||||||
|
//
|
||||||
|
// If @params contains { modal: true }, then grab() will push a modal
|
||||||
|
// on the owner of the GrabHelper. As long as there is at least one
|
||||||
|
// { modal: true } actor on the grab stack, the grab will be kept.
|
||||||
|
// When the last { modal: true } actor is ungrabbed, then the modal
|
||||||
|
// will be dropped. A modal grab can fail if there is already a grab
|
||||||
|
// in effect from aother application; in this case the function returns
|
||||||
|
// false and nothing happens. Non-modal grabs can never fail.
|
||||||
|
//
|
||||||
|
// If @params contains { grabFocus: true }, then if you call grab()
|
||||||
|
// while the shell is outside the overview, it will set the stage
|
||||||
|
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
|
||||||
|
// revert it back, and re-focus the previously-focused window (if
|
||||||
|
// another window hasn't been explicitly focused before then).
|
||||||
|
grab: function(params) {
|
||||||
|
params = Params.parse(params, { actor: null,
|
||||||
|
modal: false,
|
||||||
|
grabFocus: false,
|
||||||
|
focus: null,
|
||||||
|
onUngrab: null });
|
||||||
|
|
||||||
|
let focus = global.stage.key_focus;
|
||||||
|
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||||
|
let newFocus = params.actor;
|
||||||
|
|
||||||
|
if (this.isActorGrabbed(params.actor))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
params.savedFocus = focus;
|
||||||
|
|
||||||
|
if (params.modal && !this._takeModalGrab())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._grabStack.push(params);
|
||||||
|
|
||||||
|
if (params.focus) {
|
||||||
|
params.focus.grab_key_focus();
|
||||||
|
} else if (newFocus && (hadFocus || params.grabFocus)) {
|
||||||
|
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||||
|
newFocus.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((params.grabFocus || params.modal) && !this._capturedEventId)
|
||||||
|
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_takeModalGrab: function() {
|
||||||
|
let firstGrab = (this._modalCount == 0);
|
||||||
|
if (firstGrab) {
|
||||||
|
if (!Main.pushModal(this._owner, this._modalParams))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._modalCount++;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_releaseModalGrab: function() {
|
||||||
|
this._modalCount--;
|
||||||
|
if (this._modalCount > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Main.popModal(this._owner);
|
||||||
|
global.sync_pointer();
|
||||||
|
},
|
||||||
|
|
||||||
|
_takeFocusGrab: function(hadFocus) {
|
||||||
|
let firstGrab = (this._grabFocusCount == 0);
|
||||||
|
if (firstGrab) {
|
||||||
|
let metaDisplay = global.screen.get_display();
|
||||||
|
|
||||||
|
this._grabbedFromKeynav = hadFocus;
|
||||||
|
this._preGrabInputMode = global.stage_input_mode;
|
||||||
|
|
||||||
|
if (this._preGrabInputMode == Shell.StageInputMode.NORMAL)
|
||||||
|
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||||
|
|
||||||
|
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||||
|
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._grabFocusCount++;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_releaseFocusGrab: function() {
|
||||||
|
this._grabFocusCount--;
|
||||||
|
if (this._grabFocusCount > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._keyFocusNotifyId > 0) {
|
||||||
|
global.stage.disconnect(this._keyFocusNotifyId);
|
||||||
|
this._keyFocusNotifyId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._focusWindowChangedId > 0) {
|
||||||
|
let metaDisplay = global.screen.get_display();
|
||||||
|
metaDisplay.disconnect(this._focusWindowChangedId);
|
||||||
|
this._focusWindowChangedId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let prePopInputMode = global.stage_input_mode;
|
||||||
|
|
||||||
|
if (this._grabbedFromKeynav) {
|
||||||
|
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
||||||
|
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
||||||
|
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
|
||||||
|
},
|
||||||
|
|
||||||
|
// ignoreRelease:
|
||||||
|
//
|
||||||
|
// Make sure that the next button release event evaluated by the
|
||||||
|
// capture event handler returns false. This is designed for things
|
||||||
|
// like the ComboBoxMenu that go away on press, but need to eat
|
||||||
|
// the next release event.
|
||||||
|
ignoreRelease: function() {
|
||||||
|
this._ignoreRelease = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// ungrab:
|
||||||
|
// @params: The parameters for the grab; see below.
|
||||||
|
//
|
||||||
|
// Pops an actor from the grab stack, potentially dropping the grab.
|
||||||
|
//
|
||||||
|
// If the actor that was popped from the grab stack was not the actor
|
||||||
|
// That was passed in, this call is ignored.
|
||||||
|
ungrab: function(params) {
|
||||||
|
params = Params.parse(params, { actor: this.currentGrab.actor,
|
||||||
|
isUser: false });
|
||||||
|
|
||||||
|
let grabStackIndex = this._findStackIndex(params.actor);
|
||||||
|
if (grabStackIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We may get key focus changes when calling onUngrab, which
|
||||||
|
// would cause an extra ungrab() on the next actor in the
|
||||||
|
// stack, which is wrong. Ignore key focus changes during the
|
||||||
|
// ungrab, and restore the saved key focus ourselves afterwards.
|
||||||
|
// We use a count as ungrab() may be re-entrant, as onUngrab()
|
||||||
|
// may ungrab additional actors.
|
||||||
|
this._isUngrabbingCount++;
|
||||||
|
|
||||||
|
let focus = global.stage.key_focus;
|
||||||
|
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||||
|
|
||||||
|
let poppedGrabs = this._grabStack.slice(grabStackIndex);
|
||||||
|
// "Pop" all newly ungrabbed actors off the grab stack
|
||||||
|
// by truncating the array.
|
||||||
|
this._grabStack.length = grabStackIndex;
|
||||||
|
|
||||||
|
for (let i = poppedGrabs.length - 1; i >= 0; i--) {
|
||||||
|
let poppedGrab = poppedGrabs[i];
|
||||||
|
|
||||||
|
if (poppedGrab.onUngrab)
|
||||||
|
poppedGrab.onUngrab(params.isUser);
|
||||||
|
|
||||||
|
if (poppedGrab.modal)
|
||||||
|
this._releaseModalGrab();
|
||||||
|
|
||||||
|
if (poppedGrab.grabFocus)
|
||||||
|
this._releaseFocusGrab();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.grabbed && this._capturedEventId > 0) {
|
||||||
|
global.stage.disconnect(this._capturedEventId);
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
|
||||||
|
this._ignoreRelease = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hadFocus) {
|
||||||
|
let poppedGrab = poppedGrabs[0];
|
||||||
|
if (poppedGrab.savedFocus)
|
||||||
|
poppedGrab.savedFocus.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isUngrabbingCount--;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCapturedEvent: function(actor, event) {
|
||||||
|
let type = event.type();
|
||||||
|
|
||||||
|
if (type == Clutter.EventType.KEY_PRESS &&
|
||||||
|
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||||
|
this.ungrab({ isUser: true });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!button && this._modalCount == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._isWithinGrabbedActor(event.get_source()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Main.keyboard.shouldTakeEvent(event))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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 this._modalCount > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyFocusChanged: function() {
|
||||||
|
if (this._isUngrabbingCount > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let focus = global.stage.key_focus;
|
||||||
|
if (!focus || !this._isWithinGrabbedActor(focus))
|
||||||
|
this.ungrab({ isUser: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
_focusWindowChanged: function() {
|
||||||
|
let metaDisplay = global.screen.get_display();
|
||||||
|
if (metaDisplay.focus_window != null)
|
||||||
|
this.ungrab({ isUser: true });
|
||||||
|
}
|
||||||
|
});
|
@ -3,107 +3,149 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const IBus = imports.gi.IBus;
|
const IBus = imports.gi.IBus;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
|
||||||
|
|
||||||
const MAX_CANDIDATES_PER_PAGE = 16;
|
const MAX_CANDIDATES_PER_PAGE = 16;
|
||||||
|
|
||||||
const CandidateArea = new Lang.Class({
|
const CandidateArea = new Lang.Class({
|
||||||
Name: 'CandidateArea',
|
Name: 'CandidateArea',
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent({ reactive: false });
|
this.actor = new St.BoxLayout({ vertical: true,
|
||||||
|
visible: false });
|
||||||
// St.Table exhibits some sizing problems so let's go with a
|
this._candidateBoxes = [];
|
||||||
// clutter layout manager for now.
|
|
||||||
this._table = new Clutter.Actor();
|
|
||||||
this.addActor(this._table);
|
|
||||||
|
|
||||||
this._tableLayout = new Clutter.TableLayout();
|
|
||||||
this._table.set_layout_manager(this._tableLayout);
|
|
||||||
|
|
||||||
this._indexLabels = [];
|
|
||||||
this._candidateLabels = [];
|
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||||
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
|
let box = new St.BoxLayout({ style_class: 'candidate-box',
|
||||||
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
|
reactive: true,
|
||||||
|
track_hover: true });
|
||||||
|
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
|
||||||
|
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
|
||||||
|
box.add(box._indexLabel, { y_fill: false });
|
||||||
|
box.add(box._candidateLabel, { y_fill: false });
|
||||||
|
this._candidateBoxes.push(box);
|
||||||
|
this.actor.add(box);
|
||||||
|
|
||||||
|
let j = i;
|
||||||
|
box.connect('button-release-event', Lang.bind(this, function(actor, event) {
|
||||||
|
this.emit('candidate-clicked', j, event.get_button(), event.get_state());
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
|
||||||
|
|
||||||
|
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous' });
|
||||||
|
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
||||||
|
this._buttonBox.add(this._previousButton, { expand: true });
|
||||||
|
|
||||||
|
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next' });
|
||||||
|
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
||||||
|
this._buttonBox.add(this._nextButton, { expand: true });
|
||||||
|
|
||||||
|
this.actor.add(this._buttonBox);
|
||||||
|
|
||||||
|
this._previousButton.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this.emit('previous-page');
|
||||||
|
}));
|
||||||
|
this._nextButton.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this.emit('next-page');
|
||||||
|
}));
|
||||||
|
|
||||||
this._orientation = -1;
|
this._orientation = -1;
|
||||||
this._cursorPosition = 0;
|
this._cursorPosition = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_setOrientation: function(orientation) {
|
setOrientation: function(orientation) {
|
||||||
if (this._orientation == orientation)
|
if (this._orientation == orientation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._orientation = orientation;
|
this._orientation = orientation;
|
||||||
|
|
||||||
this._table.remove_all_children();
|
if (this._orientation == IBus.Orientation.HORIZONTAL) {
|
||||||
|
this.actor.vertical = false;
|
||||||
if (this._orientation == IBus.Orientation.HORIZONTAL)
|
this.actor.remove_style_class_name('vertical');
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
this.actor.add_style_class_name('horizontal');
|
||||||
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
|
this._previousButton.child.icon_name = 'go-previous-symbolic';
|
||||||
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
|
this._nextButton.child.icon_name = 'go-next-symbolic';
|
||||||
}
|
} else { // VERTICAL || SYSTEM
|
||||||
else // VERTICAL || SYSTEM
|
this.actor.vertical = true;
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
this.actor.add_style_class_name('vertical');
|
||||||
this._tableLayout.pack(this._indexLabels[i], 0, i);
|
this.actor.remove_style_class_name('horizontal');
|
||||||
this._tableLayout.pack(this._candidateLabels[i], 1, i);
|
this._previousButton.child.icon_name = 'go-up-symbolic';
|
||||||
}
|
this._nextButton.child.icon_name = 'go-down-symbolic';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
|
setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
|
||||||
this._setOrientation(orientation);
|
|
||||||
|
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||||
let visible = i < candidates.length;
|
let visible = i < candidates.length;
|
||||||
this._indexLabels[i].visible = visible;
|
let box = this._candidateBoxes[i];
|
||||||
this._candidateLabels[i].visible = visible;
|
box.visible = visible;
|
||||||
|
|
||||||
if (!visible)
|
if (!visible)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
|
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
|
||||||
this._candidateLabels[i].text = candidates[i];
|
box._candidateLabel.text = candidates[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
|
this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
|
||||||
this._cursorPosition = cursorPosition;
|
this._cursorPosition = cursorPosition;
|
||||||
if (cursorVisible)
|
if (cursorVisible)
|
||||||
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
|
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
|
||||||
|
},
|
||||||
|
|
||||||
|
updateButtons: function(wrapsAround, page, nPages) {
|
||||||
|
if (nPages < 2) {
|
||||||
|
this._buttonBox.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._buttonBox.show();
|
||||||
|
this._previousButton.reactive = wrapsAround || page > 0;
|
||||||
|
this._nextButton.reactive = wrapsAround || page < nPages - 1;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(CandidateArea.prototype);
|
||||||
|
|
||||||
const CandidatePopup = new Lang.Class({
|
const CandidatePopup = new Lang.Class({
|
||||||
Name: 'CandidatePopup',
|
Name: 'CandidatePopup',
|
||||||
Extends: PopupMenu.PopupMenu,
|
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._cursor = new St.Bin({ opacity: 0 });
|
this._cursor = new St.Bin({ opacity: 0 });
|
||||||
Main.uiGroup.add_actor(this._cursor);
|
Main.uiGroup.add_actor(this._cursor);
|
||||||
|
|
||||||
this.parent(this._cursor, 0, St.Side.TOP);
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
|
||||||
this.actor.hide();
|
this._boxPointer.actor.visible = false;
|
||||||
Main.uiGroup.add_actor(this.actor);
|
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
|
||||||
|
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||||
|
|
||||||
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
|
||||||
this._preeditTextItem.actor.hide();
|
vertical: true });
|
||||||
this.addMenuItem(this._preeditTextItem);
|
this._boxPointer.bin.set_child(box);
|
||||||
|
|
||||||
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
|
||||||
this._auxTextItem.actor.hide();
|
visible: false });
|
||||||
this.addMenuItem(this._auxTextItem);
|
box.add(this._preeditText);
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this._auxText = new St.Label({ style_class: 'candidate-popup-text',
|
||||||
|
visible: false });
|
||||||
|
box.add(this._auxText);
|
||||||
|
|
||||||
this._lookupTableItem = new CandidateArea();
|
this._candidateArea = new CandidateArea();
|
||||||
this._lookupTableItem.actor.hide();
|
box.add(this._candidateArea.actor);
|
||||||
this.addMenuItem(this._lookupTableItem);
|
|
||||||
|
this._candidateArea.connect('previous-page', Lang.bind(this, function() {
|
||||||
|
this._panelService.page_up();
|
||||||
|
}));
|
||||||
|
this._candidateArea.connect('next-page', Lang.bind(this, function() {
|
||||||
|
this._panelService.page_down();
|
||||||
|
}));
|
||||||
|
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
|
||||||
|
this._panelService.candidate_clicked(index, button, state);
|
||||||
|
}));
|
||||||
|
|
||||||
this._panelService = null;
|
this._panelService = null;
|
||||||
},
|
},
|
||||||
@ -117,66 +159,61 @@ const CandidatePopup = new Lang.Class({
|
|||||||
Lang.bind(this, function(ps, x, y, w, h) {
|
Lang.bind(this, function(ps, x, y, w, h) {
|
||||||
this._cursor.set_position(x, y);
|
this._cursor.set_position(x, y);
|
||||||
this._cursor.set_size(w, h);
|
this._cursor.set_size(w, h);
|
||||||
|
if (this._boxPointer.actor.visible)
|
||||||
|
this._boxPointer.setPosition(this._cursor, 0);
|
||||||
}));
|
}));
|
||||||
panelService.connect('update-preedit-text',
|
panelService.connect('update-preedit-text',
|
||||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||||
if (visible)
|
this._preeditText.visible = visible;
|
||||||
this._preeditTextItem.actor.show();
|
|
||||||
else
|
|
||||||
this._preeditTextItem.actor.hide();
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
|
|
||||||
this._preeditTextItem.actor.label_actor.text = text.get_text();
|
this._preeditText.text = text.get_text();
|
||||||
|
|
||||||
let attrs = text.get_attributes();
|
let attrs = text.get_attributes();
|
||||||
if (attrs)
|
if (attrs)
|
||||||
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
|
this._setTextAttributes(this._preeditText.clutter_text,
|
||||||
attrs);
|
attrs);
|
||||||
}));
|
}));
|
||||||
panelService.connect('show-preedit-text',
|
panelService.connect('show-preedit-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._preeditTextItem.actor.show();
|
this._preeditText.show();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('hide-preedit-text',
|
panelService.connect('hide-preedit-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._preeditTextItem.actor.hide();
|
this._preeditText.hide();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('update-auxiliary-text',
|
panelService.connect('update-auxiliary-text',
|
||||||
Lang.bind(this, function(ps, text, visible) {
|
Lang.bind(this, function(ps, text, visible) {
|
||||||
if (visible)
|
this._auxText.visible = visible;
|
||||||
this._auxTextItem.actor.show();
|
|
||||||
else
|
|
||||||
this._auxTextItem.actor.hide();
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
|
|
||||||
this._auxTextItem.actor.label_actor.text = text.get_text();
|
this._auxText.text = text.get_text();
|
||||||
}));
|
}));
|
||||||
panelService.connect('show-auxiliary-text',
|
panelService.connect('show-auxiliary-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._auxTextItem.actor.show();
|
this._auxText.show();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('hide-auxiliary-text',
|
panelService.connect('hide-auxiliary-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._auxTextItem.actor.hide();
|
this._auxText.hide();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('update-lookup-table',
|
panelService.connect('update-lookup-table',
|
||||||
Lang.bind(this, function(ps, lookupTable, visible) {
|
Lang.bind(this, function(ps, lookupTable, visible) {
|
||||||
if (visible)
|
this._candidateArea.actor.visible = visible;
|
||||||
this._lookupTableItem.actor.show();
|
|
||||||
else
|
|
||||||
this._lookupTableItem.actor.hide();
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
|
|
||||||
|
let nCandidates = lookupTable.get_number_of_candidates();
|
||||||
let cursorPos = lookupTable.get_cursor_pos();
|
let cursorPos = lookupTable.get_cursor_pos();
|
||||||
let pageSize = lookupTable.get_page_size();
|
let pageSize = lookupTable.get_page_size();
|
||||||
|
let nPages = Math.ceil(nCandidates / pageSize);
|
||||||
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
||||||
let startIndex = page * pageSize;
|
let startIndex = page * pageSize;
|
||||||
let endIndex = Math.min((page + 1) * pageSize,
|
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
|
||||||
lookupTable.get_number_of_candidates());
|
|
||||||
let indexes = [];
|
let indexes = [];
|
||||||
let indexLabel;
|
let indexLabel;
|
||||||
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
||||||
@ -186,37 +223,41 @@ const CandidatePopup = new Lang.Class({
|
|||||||
for (let i = startIndex; i < endIndex; ++i)
|
for (let i = startIndex; i < endIndex; ++i)
|
||||||
candidates.push(lookupTable.get_candidate(i).get_text());
|
candidates.push(lookupTable.get_candidate(i).get_text());
|
||||||
|
|
||||||
this._lookupTableItem.setCandidates(indexes,
|
this._candidateArea.setCandidates(indexes,
|
||||||
candidates,
|
candidates,
|
||||||
lookupTable.get_orientation(),
|
cursorPos % pageSize,
|
||||||
cursorPos % pageSize,
|
lookupTable.is_cursor_visible());
|
||||||
lookupTable.is_cursor_visible());
|
this._candidateArea.setOrientation(lookupTable.get_orientation());
|
||||||
|
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
|
||||||
}));
|
}));
|
||||||
panelService.connect('show-lookup-table',
|
panelService.connect('show-lookup-table',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._lookupTableItem.actor.show();
|
this._candidateArea.actor.show();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('hide-lookup-table',
|
panelService.connect('hide-lookup-table',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._lookupTableItem.actor.hide();
|
this._candidateArea.actor.hide();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('focus-out',
|
panelService.connect('focus-out',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this.close(BoxPointer.PopupAnimation.NONE);
|
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
let isVisible = (this._preeditTextItem.actor.visible ||
|
let isVisible = (this._preeditText.visible ||
|
||||||
this._auxTextItem.actor.visible ||
|
this._auxText.visible ||
|
||||||
this._lookupTableItem.actor.visible);
|
this._candidateArea.actor.visible);
|
||||||
|
|
||||||
if (isVisible)
|
if (isVisible) {
|
||||||
this.open(BoxPointer.PopupAnimation.NONE);
|
this._boxPointer.setPosition(this._cursor, 0);
|
||||||
else
|
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
|
||||||
this.close(BoxPointer.PopupAnimation.NONE);
|
this._boxPointer.actor.raise_top();
|
||||||
|
} else {
|
||||||
|
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_setTextAttributes: function(clutterText, ibusAttrList) {
|
_setTextAttributes: function(clutterText, ibusAttrList) {
|
||||||
|
@ -23,6 +23,8 @@ const BaseIcon = new Lang.Class({
|
|||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
this.actor.connect('style-changed',
|
this.actor.connect('style-changed',
|
||||||
Lang.bind(this, this._onStyleChanged));
|
Lang.bind(this, this._onStyleChanged));
|
||||||
|
this.actor.connect('destroy',
|
||||||
|
Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
|
|
||||||
@ -52,6 +54,9 @@ const BaseIcon = new Lang.Class({
|
|||||||
this._setSizeManually = params.setSizeManually;
|
this._setSizeManually = params.setSizeManually;
|
||||||
|
|
||||||
this.icon = null;
|
this.icon = null;
|
||||||
|
|
||||||
|
let cache = St.TextureCache.get_default();
|
||||||
|
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
_allocate: function(actor, box, flags) {
|
||||||
@ -146,7 +151,22 @@ const BaseIcon = new Lang.Class({
|
|||||||
size = found ? len : ICON_SIZE;
|
size = found ? len : ICON_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.iconSize == size && this._iconBin.child)
|
||||||
|
return;
|
||||||
|
|
||||||
this._createIconTexture(size);
|
this._createIconTexture(size);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
if (this._iconThemeChangedId > 0) {
|
||||||
|
let cache = St.TextureCache.get_default();
|
||||||
|
cache.disconnect(this._iconThemeChangedId);
|
||||||
|
this._iconThemeChangedId = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onIconThemeChanged: function() {
|
||||||
|
this._createIconTexture(this.iconSize);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -156,13 +176,16 @@ const IconGrid = new Lang.Class({
|
|||||||
_init: function(params) {
|
_init: function(params) {
|
||||||
params = Params.parse(params, { rowLimit: null,
|
params = Params.parse(params, { rowLimit: null,
|
||||||
columnLimit: null,
|
columnLimit: null,
|
||||||
|
fillParent: false,
|
||||||
xAlign: St.Align.MIDDLE });
|
xAlign: St.Align.MIDDLE });
|
||||||
this._rowLimit = params.rowLimit;
|
this._rowLimit = params.rowLimit;
|
||||||
this._colLimit = params.columnLimit;
|
this._colLimit = params.columnLimit;
|
||||||
this._xAlign = params.xAlign;
|
this._xAlign = params.xAlign;
|
||||||
|
this._fillParent = params.fillParent;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
|
|
||||||
// Pulled from CSS, but hardcode some defaults here
|
// Pulled from CSS, but hardcode some defaults here
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
this._hItemSize = this._vItemSize = ICON_SIZE;
|
this._hItemSize = this._vItemSize = ICON_SIZE;
|
||||||
@ -176,6 +199,11 @@ const IconGrid = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||||
|
if (this._fillParent)
|
||||||
|
// Ignore all size requests of children and request a size of 0;
|
||||||
|
// later we'll allocate as many children as fit the parent
|
||||||
|
return;
|
||||||
|
|
||||||
let children = this._grid.get_children();
|
let children = this._grid.get_children();
|
||||||
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
||||||
children.length)
|
children.length)
|
||||||
@ -197,8 +225,20 @@ const IconGrid = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||||
|
if (this._fillParent)
|
||||||
|
// Ignore all size requests of children and request a size of 0;
|
||||||
|
// later we'll allocate as many children as fit the parent
|
||||||
|
return;
|
||||||
|
|
||||||
let children = this._getVisibleChildren();
|
let children = this._getVisibleChildren();
|
||||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
let nColumns, spacing;
|
||||||
|
if (forWidth < 0) {
|
||||||
|
nColumns = children.length;
|
||||||
|
spacing = this._spacing;
|
||||||
|
} else {
|
||||||
|
[nColumns, , spacing] = this._computeLayout(forWidth);
|
||||||
|
}
|
||||||
|
|
||||||
let nRows;
|
let nRows;
|
||||||
if (nColumns > 0)
|
if (nColumns > 0)
|
||||||
nRows = Math.ceil(children.length / nColumns);
|
nRows = Math.ceil(children.length / nColumns);
|
||||||
@ -206,18 +246,25 @@ const IconGrid = new Lang.Class({
|
|||||||
nRows = 0;
|
nRows = 0;
|
||||||
if (this._rowLimit)
|
if (this._rowLimit)
|
||||||
nRows = Math.min(nRows, this._rowLimit);
|
nRows = Math.min(nRows, this._rowLimit);
|
||||||
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
|
let totalSpacing = Math.max(0, nRows - 1) * spacing;
|
||||||
let height = nRows * this._vItemSize + totalSpacing;
|
let height = nRows * this._vItemSize + totalSpacing;
|
||||||
alloc.min_size = height;
|
alloc.min_size = height;
|
||||||
alloc.natural_size = height;
|
alloc.natural_size = height;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function (grid, box, flags) {
|
_allocate: function (grid, box, flags) {
|
||||||
|
if (this._fillParent) {
|
||||||
|
// Reset the passed in box to fill the parent
|
||||||
|
let parentBox = this.actor.get_parent().allocation;
|
||||||
|
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
|
||||||
|
box = this._grid.get_theme_node().get_content_box(gridBox);
|
||||||
|
}
|
||||||
|
|
||||||
let children = this._getVisibleChildren();
|
let children = this._getVisibleChildren();
|
||||||
let availWidth = box.x2 - box.x1;
|
let availWidth = box.x2 - box.x1;
|
||||||
let availHeight = box.y2 - box.y1;
|
let availHeight = box.y2 - box.y1;
|
||||||
|
|
||||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
|
||||||
|
|
||||||
let leftPadding;
|
let leftPadding;
|
||||||
switch(this._xAlign) {
|
switch(this._xAlign) {
|
||||||
@ -256,7 +303,8 @@ const IconGrid = new Lang.Class({
|
|||||||
childBox.x2 = childBox.x1 + width;
|
childBox.x2 = childBox.x1 + width;
|
||||||
childBox.y2 = childBox.y1 + height;
|
childBox.y2 = childBox.y1 + height;
|
||||||
|
|
||||||
if (this._rowLimit && rowIndex >= this._rowLimit) {
|
if (this._rowLimit && rowIndex >= this._rowLimit ||
|
||||||
|
this._fillParent && childBox.y2 > availHeight) {
|
||||||
this._grid.set_skip_paint(children[i], true);
|
this._grid.set_skip_paint(children[i], true);
|
||||||
} else {
|
} else {
|
||||||
children[i].allocate(childBox, flags);
|
children[i].allocate(childBox, flags);
|
||||||
@ -270,10 +318,10 @@ const IconGrid = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (columnIndex == 0) {
|
if (columnIndex == 0) {
|
||||||
y += this._vItemSize + this._spacing;
|
y += this._vItemSize + spacing;
|
||||||
x = box.x1 + leftPadding;
|
x = box.x1 + leftPadding;
|
||||||
} else {
|
} else {
|
||||||
x += this._hItemSize + this._spacing;
|
x += this._hItemSize + spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -289,16 +337,25 @@ const IconGrid = new Lang.Class({
|
|||||||
_computeLayout: function (forWidth) {
|
_computeLayout: function (forWidth) {
|
||||||
let nColumns = 0;
|
let nColumns = 0;
|
||||||
let usedWidth = 0;
|
let usedWidth = 0;
|
||||||
|
let spacing = this._spacing;
|
||||||
|
|
||||||
|
if (this._colLimit) {
|
||||||
|
let itemWidth = this._hItemSize * this._colLimit;
|
||||||
|
let emptyArea = forWidth - itemWidth;
|
||||||
|
spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
|
||||||
|
spacing = Math.round(spacing);
|
||||||
|
}
|
||||||
|
|
||||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||||
(usedWidth + this._hItemSize <= forWidth)) {
|
(usedWidth + this._hItemSize <= forWidth)) {
|
||||||
usedWidth += this._hItemSize + this._spacing;
|
usedWidth += this._hItemSize + spacing;
|
||||||
nColumns += 1;
|
nColumns += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nColumns > 0)
|
if (nColumns > 0)
|
||||||
usedWidth -= this._spacing;
|
usedWidth -= spacing;
|
||||||
|
|
||||||
return [nColumns, usedWidth];
|
return [nColumns, usedWidth, spacing];
|
||||||
},
|
},
|
||||||
|
|
||||||
_onStyleChanged: function() {
|
_onStyleChanged: function() {
|
||||||
|
@ -2,43 +2,27 @@
|
|||||||
|
|
||||||
const Caribou = imports.gi.Caribou;
|
const Caribou = imports.gi.Caribou;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const DBus = imports.dbus;
|
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
|
||||||
|
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
|
||||||
|
|
||||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||||
const KEYBOARD_TYPE = 'keyboard-type';
|
const KEYBOARD_TYPE = 'keyboard-type';
|
||||||
|
|
||||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
||||||
|
|
||||||
// Key constants taken from Antler
|
|
||||||
// FIXME: ought to be moved into libcaribou
|
|
||||||
const PRETTY_KEYS = {
|
|
||||||
'BackSpace': '\u232b',
|
|
||||||
'space': ' ',
|
|
||||||
'Return': '\u23ce',
|
|
||||||
'Caribou_Prefs': '\u2328',
|
|
||||||
'Caribou_ShiftUp': '\u2b06',
|
|
||||||
'Caribou_ShiftDown': '\u2b07',
|
|
||||||
'Caribou_Emoticons': '\u263a',
|
|
||||||
'Caribou_Symbols': '123',
|
|
||||||
'Caribou_Symbols_More': '{#*',
|
|
||||||
'Caribou_Alpha': 'Abc',
|
|
||||||
'Tab': 'Tab',
|
|
||||||
'Escape': 'Esc',
|
|
||||||
'Control_L': 'Ctrl',
|
|
||||||
'Alt_L': 'Alt'
|
|
||||||
};
|
|
||||||
|
|
||||||
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
|
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
|
||||||
<method name='Show'>
|
<method name='Show'>
|
||||||
<arg type='u' direction='in' />
|
<arg type='u' direction='in' />
|
||||||
@ -75,14 +59,7 @@ const Key = new Lang.Class({
|
|||||||
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
||||||
this._key.latch = true;
|
this._key.latch = true;
|
||||||
|
|
||||||
this._key.connect('key-pressed', Lang.bind(this, function ()
|
|
||||||
{ this.actor.checked = true }));
|
|
||||||
this._key.connect('key-released', Lang.bind(this, function ()
|
|
||||||
{ this.actor.checked = false; }));
|
|
||||||
|
|
||||||
if (this._extended_keys.length > 0) {
|
if (this._extended_keys.length > 0) {
|
||||||
this._grabbed = false;
|
|
||||||
this._eventCaptureId = 0;
|
|
||||||
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
||||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
@ -93,22 +70,12 @@ const Key = new Lang.Class({
|
|||||||
this._getExtendedKeys();
|
this._getExtendedKeys();
|
||||||
this.actor._extended_keys = this._extended_keyboard;
|
this.actor._extended_keys = this._extended_keyboard;
|
||||||
this._boxPointer.actor.hide();
|
this._boxPointer.actor.hide();
|
||||||
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
|
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_makeKey: function () {
|
_makeKey: function () {
|
||||||
let label = this._key.name;
|
let label = GLib.markup_escape_text(this._key.label, -1);
|
||||||
|
|
||||||
if (label.length > 1) {
|
|
||||||
let pretty = PRETTY_KEYS[label];
|
|
||||||
if (pretty)
|
|
||||||
label = pretty;
|
|
||||||
else
|
|
||||||
label = this._getUnichar(this._key);
|
|
||||||
}
|
|
||||||
|
|
||||||
label = GLib.markup_escape_text(label, -1);
|
|
||||||
let button = new St.Button ({ label: label,
|
let button = new St.Button ({ label: label,
|
||||||
style_class: 'keyboard-key' });
|
style_class: 'keyboard-key' });
|
||||||
|
|
||||||
@ -144,52 +111,23 @@ const Key = new Lang.Class({
|
|||||||
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEventCapture: function (actor, event) {
|
get subkeys() {
|
||||||
let source = event.get_source();
|
return this._boxPointer;
|
||||||
let type = event.type();
|
|
||||||
|
|
||||||
if ((type == Clutter.EventType.BUTTON_PRESS ||
|
|
||||||
type == Clutter.EventType.BUTTON_RELEASE) &&
|
|
||||||
this._extended_keyboard.contains(source)) {
|
|
||||||
source.extended_key.press();
|
|
||||||
source.extended_key.release();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (type == Clutter.EventType.BUTTON_PRESS) {
|
|
||||||
this._boxPointer.actor.hide();
|
|
||||||
this._ungrab();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_ungrab: function () {
|
|
||||||
global.stage.disconnect(this._eventCaptureId);
|
|
||||||
this._eventCaptureId = 0;
|
|
||||||
this._grabbed = false;
|
|
||||||
Main.popModal(this.actor);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onShowSubkeysChanged: function () {
|
_onShowSubkeysChanged: function () {
|
||||||
if (this._key.show_subkeys) {
|
if (this._key.show_subkeys) {
|
||||||
this.actor.fake_release();
|
|
||||||
this._boxPointer.actor.raise_top();
|
this._boxPointer.actor.raise_top();
|
||||||
this._boxPointer.setPosition(this.actor, 0.5);
|
this._boxPointer.setPosition(this.actor, 0.5);
|
||||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
this.emit('show-subkeys');
|
||||||
|
this.actor.fake_release();
|
||||||
this.actor.set_hover(false);
|
this.actor.set_hover(false);
|
||||||
if (!this._grabbed) {
|
|
||||||
Main.pushModal(this.actor);
|
|
||||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
|
||||||
this._grabbed = true;
|
|
||||||
}
|
|
||||||
this._key.release();
|
|
||||||
} else {
|
} else {
|
||||||
if (this._grabbed)
|
this.emit('hide-subkeys');
|
||||||
this._ungrab();
|
|
||||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(Key.prototype);
|
||||||
|
|
||||||
const Keyboard = new Lang.Class({
|
const Keyboard = new Lang.Class({
|
||||||
// HACK: we can't set Name, because it collides with Name dbus property
|
// HACK: we can't set Name, because it collides with Name dbus property
|
||||||
@ -200,18 +138,30 @@ const Keyboard = new Lang.Class({
|
|||||||
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
|
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
|
||||||
|
|
||||||
this.actor = null;
|
this.actor = null;
|
||||||
|
this._focusInTray = false;
|
||||||
|
this._focusInExtendedKeys = false;
|
||||||
|
|
||||||
this._timestamp = global.get_current_time();
|
this._timestamp = global.display.get_current_time_roundtrip();
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
|
||||||
|
|
||||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||||
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
||||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||||
this._settingsChanged();
|
this._settingsChanged();
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
this._showIdleId = 0;
|
||||||
|
this._subkeysBoxPointer = null;
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
this._capturedPress = false;
|
||||||
|
|
||||||
|
this._keyboardVisible = false;
|
||||||
|
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
|
||||||
|
this._keyboardVisible = visible;
|
||||||
|
}));
|
||||||
|
this._keyboardRequested = false;
|
||||||
|
this._keyboardRestingId = 0;
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||||
this._redraw();
|
this._redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -226,25 +176,19 @@ const Keyboard = new Lang.Class({
|
|||||||
if (this._keyboard)
|
if (this._keyboard)
|
||||||
this._destroyKeyboard();
|
this._destroyKeyboard();
|
||||||
|
|
||||||
if (this._enableKeyboard) {
|
if (this._enableKeyboard)
|
||||||
// If we've been called because the setting actually just
|
this._setupKeyboard();
|
||||||
// changed to true (as opposed to being called from
|
else
|
||||||
// this._init()), then we want to pop up the keyboard.
|
|
||||||
let showKeyboard = (settings != null);
|
|
||||||
|
|
||||||
// However, caribou-gtk-module or this._onKeyFocusChanged
|
|
||||||
// will probably immediately tell us to hide it, so we
|
|
||||||
// have to fake things out so we'll ignore that request.
|
|
||||||
if (showKeyboard)
|
|
||||||
this._timestamp = global.display.get_current_time_roundtrip() + 1;
|
|
||||||
this._setupKeyboard(showKeyboard);
|
|
||||||
} else
|
|
||||||
Main.layoutManager.hideKeyboard(true);
|
Main.layoutManager.hideKeyboard(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_destroyKeyboard: function() {
|
_destroyKeyboard: function() {
|
||||||
if (this._keyboardNotifyId)
|
if (this._keyboardNotifyId)
|
||||||
this._keyboard.disconnect(this._keyboardNotifyId);
|
this._keyboard.disconnect(this._keyboardNotifyId);
|
||||||
|
if (this._keyboardGroupAddedId)
|
||||||
|
this._keyboard.disconnect(this._keyboardGroupAddedId);
|
||||||
|
if (this._keyboardGroupRemovedId)
|
||||||
|
this._keyboard.disconnect(this._keyboardGroupRemovedId);
|
||||||
if (this._focusNotifyId)
|
if (this._focusNotifyId)
|
||||||
global.stage.disconnect(this._focusNotifyId);
|
global.stage.disconnect(this._focusNotifyId);
|
||||||
this._keyboard = null;
|
this._keyboard = null;
|
||||||
@ -254,7 +198,7 @@ const Keyboard = new Lang.Class({
|
|||||||
this._destroySource();
|
this._destroySource();
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupKeyboard: function(show) {
|
_setupKeyboard: function() {
|
||||||
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
||||||
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
||||||
Main.layoutManager.trackChrome(this.actor);
|
Main.layoutManager.trackChrome(this.actor);
|
||||||
@ -275,12 +219,11 @@ const Keyboard = new Lang.Class({
|
|||||||
this.actor.text_direction = Clutter.TextDirection.LTR;
|
this.actor.text_direction = Clutter.TextDirection.LTR;
|
||||||
|
|
||||||
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
||||||
|
this._keyboardGroupAddedId = this._keyboard.connect('group-added', Lang.bind(this, this._onGroupAdded));
|
||||||
|
this._keyboardGroupRemovedId = this._keyboard.connect('group-removed', Lang.bind(this, this._onGroupRemoved));
|
||||||
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||||
|
|
||||||
if (show)
|
this._createSource();
|
||||||
this.show();
|
|
||||||
else
|
|
||||||
this._createSource();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyFocusChanged: function () {
|
_onKeyFocusChanged: function () {
|
||||||
@ -288,85 +231,114 @@ const Keyboard = new Lang.Class({
|
|||||||
|
|
||||||
// Showing an extended key popup and clicking a key from the extended keys
|
// Showing an extended key popup and clicking a key from the extended keys
|
||||||
// will grab focus, but ignore that
|
// will grab focus, but ignore that
|
||||||
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
|
let extendedKeysWereFocused = this._focusInExtendedKeys;
|
||||||
|
this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key);
|
||||||
|
if (this._focusInExtendedKeys || extendedKeysWereFocused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ignore focus changes caused by message tray showing/hiding
|
||||||
|
let trayWasFocused = this._focusInTray;
|
||||||
|
this._focusInTray = (focus && Main.messageTray.actor.contains(focus));
|
||||||
|
if (this._focusInTray || trayWasFocused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let time = global.get_current_time();
|
let time = global.get_current_time();
|
||||||
if (focus instanceof Clutter.Text)
|
if (!(focus instanceof Clutter.Text)) {
|
||||||
this.Show(time);
|
|
||||||
else
|
|
||||||
this.Hide(time);
|
this.Hide(time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._showIdleId)
|
||||||
|
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||||
|
Lang.bind(this, function() { this.Show(time); }));
|
||||||
|
},
|
||||||
|
|
||||||
|
_createLayersForGroup: function (gname) {
|
||||||
|
let group = this._keyboard.get_group(gname);
|
||||||
|
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
||||||
|
let layers = {};
|
||||||
|
let levels = group.get_levels();
|
||||||
|
for (let j = 0; j < levels.length; ++j) {
|
||||||
|
let lname = levels[j];
|
||||||
|
let level = group.get_level(lname);
|
||||||
|
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||||
|
vertical: true });
|
||||||
|
this._loadRows(level, layout);
|
||||||
|
layers[lname] = layout;
|
||||||
|
this.actor.add(layout, { x_fill: false });
|
||||||
|
|
||||||
|
layout.hide();
|
||||||
|
}
|
||||||
|
return layers;
|
||||||
},
|
},
|
||||||
|
|
||||||
_addKeys: function () {
|
_addKeys: function () {
|
||||||
let groups = this._keyboard.get_groups();
|
let groups = this._keyboard.get_groups();
|
||||||
for (let i = 0; i < groups.length; ++i) {
|
for (let i = 0; i < groups.length; ++i) {
|
||||||
let gname = groups[i];
|
let gname = groups[i];
|
||||||
let group = this._keyboard.get_group(gname);
|
this._groups[gname] = this._createLayersForGroup(gname);
|
||||||
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
|
||||||
let layers = {};
|
|
||||||
let levels = group.get_levels();
|
|
||||||
for (let j = 0; j < levels.length; ++j) {
|
|
||||||
let lname = levels[j];
|
|
||||||
let level = group.get_level(lname);
|
|
||||||
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
|
|
||||||
vertical: true });
|
|
||||||
this._loadRows(level, layout);
|
|
||||||
layers[lname] = layout;
|
|
||||||
this.actor.add(layout, { x_fill: false });
|
|
||||||
|
|
||||||
layout.hide();
|
|
||||||
}
|
|
||||||
this._groups[gname] = layers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._setActiveLayer();
|
this._setActiveLayer();
|
||||||
},
|
},
|
||||||
|
|
||||||
_getTrayIcon: function () {
|
_onCapturedEvent: function(actor, event) {
|
||||||
let trayButton = new St.Button ({ label: _("tray"),
|
let type = event.type();
|
||||||
style_class: 'keyboard-key' });
|
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||||
trayButton.key_width = 1;
|
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||||
trayButton.connect('button-press-event', Lang.bind(this, function () {
|
|
||||||
Main.messageTray.toggle();
|
|
||||||
}));
|
|
||||||
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
if (press)
|
||||||
trayButton.reactive = false;
|
this._capturedPress = true;
|
||||||
trayButton.add_style_pseudo_class('grayed');
|
else if (release && this._capturedPress)
|
||||||
}));
|
this._hideSubkeys();
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
|
||||||
trayButton.reactive = true;
|
|
||||||
trayButton.remove_style_pseudo_class('grayed');
|
|
||||||
}));
|
|
||||||
|
|
||||||
return trayButton;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_addRows : function (keys, layout) {
|
_addRows : function (keys, layout) {
|
||||||
let keyboard_row = new St.BoxLayout();
|
let keyboard_row = new St.BoxLayout();
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
let children = keys[i].get_children();
|
let children = keys[i].get_children();
|
||||||
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
|
||||||
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
|
let center_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
|
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
for (let j = 0; j < children.length; ++j) {
|
for (let j = 0; j < children.length; ++j) {
|
||||||
if (this._numOfHorizKeys == 0)
|
if (this._numOfHorizKeys == 0)
|
||||||
this._numOfHorizKeys = children.length;
|
this._numOfHorizKeys = children.length;
|
||||||
let key = children[j];
|
let key = children[j];
|
||||||
let button = new Key(key);
|
let button = new Key(key);
|
||||||
|
|
||||||
if (key.align == 'right')
|
switch (key.align) {
|
||||||
|
case 'right':
|
||||||
right_box.add(button.actor);
|
right_box.add(button.actor);
|
||||||
else
|
break;
|
||||||
|
case 'center':
|
||||||
|
center_box.add(button.actor);
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
default:
|
||||||
left_box.add(button.actor);
|
left_box.add(button.actor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (key.name == 'Caribou_Prefs') {
|
if (key.name == 'Caribou_Prefs') {
|
||||||
key.connect('key-released', Lang.bind(this, this.hide));
|
key.connect('key-released', Lang.bind(this, this.hide));
|
||||||
|
|
||||||
// Add new key for hiding message tray
|
|
||||||
right_box.add(this._getTrayIcon());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.connect('show-subkeys', Lang.bind(this, function() {
|
||||||
|
if (this._subkeysBoxPointer)
|
||||||
|
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||||
|
this._subkeysBoxPointer = button.subkeys;
|
||||||
|
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||||
|
if (!this._capturedEventId)
|
||||||
|
this._capturedEventId = this.actor.connect('captured-event',
|
||||||
|
Lang.bind(this, this._onCapturedEvent));
|
||||||
|
}));
|
||||||
|
button.connect('hide-subkeys', Lang.bind(this, function() {
|
||||||
|
this._hideSubkeys();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
keyboard_row.add(center_box, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||||
}
|
}
|
||||||
layout.add(keyboard_row);
|
layout.add(keyboard_row);
|
||||||
@ -387,7 +359,7 @@ const Keyboard = new Lang.Class({
|
|||||||
if (!this._enableKeyboard)
|
if (!this._enableKeyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let monitor = Main.layoutManager.bottomMonitor;
|
let monitor = Main.layoutManager.keyboardMonitor;
|
||||||
let maxHeight = monitor.height / 3;
|
let maxHeight = monitor.height / 3;
|
||||||
this.actor.width = monitor.width;
|
this.actor.width = monitor.width;
|
||||||
|
|
||||||
@ -439,6 +411,14 @@ const Keyboard = new Lang.Class({
|
|||||||
this._redraw();
|
this._redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onGroupAdded: function (keyboard, gname) {
|
||||||
|
this._groups[gname] = this._createLayersForGroup(gname);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onGroupRemoved: function (keyboard, gname) {
|
||||||
|
delete this._groups[gname];
|
||||||
|
},
|
||||||
|
|
||||||
_setActiveLayer: function () {
|
_setActiveLayer: function () {
|
||||||
let active_group_name = this._keyboard.active_group;
|
let active_group_name = this._keyboard.active_group;
|
||||||
let active_group = this._keyboard.get_group(active_group_name);
|
let active_group = this._keyboard.get_group(active_group_name);
|
||||||
@ -468,18 +448,85 @@ const Keyboard = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
show: function () {
|
shouldTakeEvent: function(event) {
|
||||||
this._redraw();
|
let actor = event.get_source();
|
||||||
|
return Main.layoutManager.keyboardBox.contains(actor) ||
|
||||||
|
actor._extended_keys || actor.extended_key;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearKeyboardRestTimer: function() {
|
||||||
|
if (!this._keyboardRestingId)
|
||||||
|
return;
|
||||||
|
GLib.source_remove(this._keyboardRestingId);
|
||||||
|
this._keyboardRestingId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function (monitor) {
|
||||||
|
this._keyboardRequested = true;
|
||||||
|
|
||||||
|
if (this._keyboardVisible) {
|
||||||
|
if (monitor != Main.layoutManager.keyboardIndex) {
|
||||||
|
Main.layoutManager.keyboardIndex = monitor;
|
||||||
|
this._redraw();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
KEYBOARD_REST_TIME,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._show(monitor);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_show: function(monitor) {
|
||||||
|
if (!this._keyboardRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Main.layoutManager.keyboardIndex = monitor;
|
||||||
|
this._redraw();
|
||||||
Main.layoutManager.showKeyboard();
|
Main.layoutManager.showKeyboard();
|
||||||
this._destroySource();
|
this._destroySource();
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function () {
|
hide: function () {
|
||||||
|
this._keyboardRequested = false;
|
||||||
|
|
||||||
|
if (!this._keyboardVisible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
KEYBOARD_REST_TIME,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._hide();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_hide: function() {
|
||||||
|
if (this._keyboardRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._hideSubkeys();
|
||||||
Main.layoutManager.hideKeyboard();
|
Main.layoutManager.hideKeyboard();
|
||||||
this._createSource();
|
this._createSource();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_hideSubkeys: function() {
|
||||||
|
if (this._subkeysBoxPointer) {
|
||||||
|
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||||
|
this._subkeysBoxPointer = null;
|
||||||
|
}
|
||||||
|
if (this._capturedEventId) {
|
||||||
|
this.actor.disconnect(this._capturedEventId);
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
}
|
||||||
|
this._capturedPress = false;
|
||||||
|
},
|
||||||
|
|
||||||
_moveTemporarily: function () {
|
_moveTemporarily: function () {
|
||||||
let currentWindow = global.screen.get_display().focus_window;
|
let currentWindow = global.screen.get_display().focus_window;
|
||||||
let rect = currentWindow.get_outer_rect();
|
let rect = currentWindow.get_outer_rect();
|
||||||
@ -494,26 +541,53 @@ const Keyboard = new Lang.Class({
|
|||||||
this._moveTemporarily();
|
this._moveTemporarily();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// _compareTimestamp:
|
||||||
|
//
|
||||||
|
// Compare two timestamps taking into account
|
||||||
|
// CURRENT_TIME (0)
|
||||||
|
_compareTimestamp: function(one, two) {
|
||||||
|
if (one == two)
|
||||||
|
return 0;
|
||||||
|
if (one == Clutter.CURRENT_TIME)
|
||||||
|
return 1;
|
||||||
|
if (two == Clutter.CURRENT_TIME)
|
||||||
|
return -1;
|
||||||
|
return one - two;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearShowIdle: function() {
|
||||||
|
if (!this._showIdleId)
|
||||||
|
return;
|
||||||
|
GLib.source_remove(this._showIdleId);
|
||||||
|
this._showIdleId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
// D-Bus methods
|
// D-Bus methods
|
||||||
Show: function(timestamp) {
|
Show: function(timestamp) {
|
||||||
if (!this._enableKeyboard)
|
if (!this._enableKeyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (timestamp - this._timestamp < 0)
|
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._timestamp = timestamp;
|
this._clearShowIdle();
|
||||||
this.show();
|
|
||||||
|
if (timestamp != Clutter.CURRENT_TIME)
|
||||||
|
this._timestamp = timestamp;
|
||||||
|
this.show(Main.layoutManager.focusIndex);
|
||||||
},
|
},
|
||||||
|
|
||||||
Hide: function(timestamp) {
|
Hide: function(timestamp) {
|
||||||
if (!this._enableKeyboard)
|
if (!this._enableKeyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (timestamp - this._timestamp < 0)
|
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._timestamp = timestamp;
|
this._clearShowIdle();
|
||||||
|
|
||||||
|
if (timestamp != Clutter.CURRENT_TIME)
|
||||||
|
this._timestamp = timestamp;
|
||||||
this.hide();
|
this.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -542,19 +616,17 @@ const KeyboardSource = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(keyboard) {
|
_init: function(keyboard) {
|
||||||
this._keyboard = keyboard;
|
this._keyboard = keyboard;
|
||||||
this.parent(_("Keyboard"), 'input-keyboard', St.IconType.SYMBOLIC);
|
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
|
||||||
|
this.keepTrayOnSummaryClick = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSummaryClick: function() {
|
handleSummaryClick: function(button) {
|
||||||
let event = Clutter.get_current_event();
|
|
||||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
this.open();
|
this.open();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function() {
|
open: function() {
|
||||||
this._keyboard.show();
|
// Show the OSK below the message tray
|
||||||
|
this._keyboard.show(Main.layoutManager.bottomIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
1648
js/ui/layout.js
@ -3,11 +3,14 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const DEFAULT_FADE_FACTOR = 0.4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lightbox:
|
* Lightbox:
|
||||||
* @container: parent Clutter.Container
|
* @container: parent Clutter.Container
|
||||||
@ -15,7 +18,8 @@ const Tweener = imports.ui.tweener;
|
|||||||
* - inhibitEvents: whether to inhibit events for @container
|
* - inhibitEvents: whether to inhibit events for @container
|
||||||
* - width: shade actor width
|
* - width: shade actor width
|
||||||
* - height: shade actor height
|
* - height: shade actor height
|
||||||
* - fadeTime: seconds used to fade in/out
|
* - fadeInTime: seconds used to fade in
|
||||||
|
* - fadeOutTime: seconds used to fade out
|
||||||
*
|
*
|
||||||
* Lightbox creates a dark translucent "shade" actor to hide the
|
* Lightbox creates a dark translucent "shade" actor to hide the
|
||||||
* contents of @container, and allows you to specify particular actors
|
* contents of @container, and allows you to specify particular actors
|
||||||
@ -38,12 +42,16 @@ const Lightbox = new Lang.Class({
|
|||||||
params = Params.parse(params, { inhibitEvents: false,
|
params = Params.parse(params, { inhibitEvents: false,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
fadeTime: null
|
fadeInTime: null,
|
||||||
|
fadeOutTime: null,
|
||||||
|
fadeFactor: DEFAULT_FADE_FACTOR
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container = container;
|
this._container = container;
|
||||||
this._children = container.get_children();
|
this._children = container.get_children();
|
||||||
this._fadeTime = params.fadeTime;
|
this._fadeInTime = params.fadeInTime;
|
||||||
|
this._fadeOutTime = params.fadeOutTime;
|
||||||
|
this._fadeFactor = params.fadeFactor;
|
||||||
this.actor = new St.Bin({ x: 0,
|
this.actor = new St.Bin({ x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
style_class: 'lightbox',
|
style_class: 'lightbox',
|
||||||
@ -52,6 +60,7 @@ const Lightbox = new Lang.Class({
|
|||||||
container.add_actor(this.actor);
|
container.add_actor(this.actor);
|
||||||
this.actor.raise_top();
|
this.actor.raise_top();
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
this.shown = false;
|
||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
@ -93,24 +102,34 @@ const Lightbox = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
if (this._fadeTime) {
|
Tweener.removeTweens(this.actor);
|
||||||
|
if (this._fadeInTime) {
|
||||||
|
this.shown = false;
|
||||||
this.actor.opacity = 0;
|
this.actor.opacity = 0;
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 255,
|
{ opacity: 255 * this._fadeFactor,
|
||||||
time: this._fadeTime,
|
time: this._fadeInTime,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this.shown = true;
|
||||||
|
this.emit('shown');
|
||||||
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.actor.opacity = 255;
|
this.actor.opacity = 255 * this._fadeFactor;
|
||||||
|
this.shown = true;
|
||||||
|
this.emit('shown');
|
||||||
}
|
}
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
if (this._fadeTime) {
|
this.shown = false;
|
||||||
|
Tweener.removeTweens(this.actor);
|
||||||
|
if (this._fadeOutTime) {
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: this._fadeTime,
|
time: this._fadeOutTime,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
@ -183,3 +202,4 @@ const Lightbox = new Lang.Class({
|
|||||||
this.highlight(null);
|
this.highlight(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(Lightbox.prototype);
|
||||||
|
@ -38,8 +38,8 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
|||||||
/* Utility functions...we should probably be able to use these
|
/* Utility functions...we should probably be able to use these
|
||||||
* in the shell core code too. */
|
* in the shell core code too. */
|
||||||
'const stage = global.stage; ' +
|
'const stage = global.stage; ' +
|
||||||
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
|
|
||||||
/* Special lookingGlass functions */
|
/* Special lookingGlass functions */
|
||||||
|
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
|
||||||
'const it = Main.lookingGlass.getIt(); ' +
|
'const it = Main.lookingGlass.getIt(); ' +
|
||||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||||
|
|
||||||
@ -308,10 +308,6 @@ const Result = new Lang.Class({
|
|||||||
box.add(resultTxt);
|
box.add(resultTxt);
|
||||||
let objLink = new ObjLink(this._lookingGlass, o);
|
let objLink = new ObjLink(this._lookingGlass, o);
|
||||||
box.add(objLink.actor);
|
box.add(objLink.actor);
|
||||||
let line = new Clutter.Rectangle({ name: 'Separator' });
|
|
||||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
|
||||||
padBin.add_actor(line);
|
|
||||||
this.actor.add(padBin);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -372,7 +368,8 @@ const ObjInspector = new Lang.Class({
|
|||||||
|
|
||||||
this._parentList = [];
|
this._parentList = [];
|
||||||
|
|
||||||
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
|
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
this.actor.get_hscroll_bar().hide();
|
this.actor.get_hscroll_bar().hide();
|
||||||
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
|
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
|
||||||
style_class: 'lg-dialog',
|
style_class: 'lg-dialog',
|
||||||
@ -444,10 +441,6 @@ const ObjInspector = new Lang.Class({
|
|||||||
this.actor.show();
|
this.actor.show();
|
||||||
if (sourceActor) {
|
if (sourceActor) {
|
||||||
this.actor.set_scale(0, 0);
|
this.actor.set_scale(0, 0);
|
||||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
|
||||||
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
|
||||||
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
|
|
||||||
Math.floor(sourceY + sourceHeight / 2));
|
|
||||||
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
time: 0.2 });
|
time: 0.2 });
|
||||||
@ -555,8 +548,8 @@ const Inspector = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_close: function() {
|
_close: function() {
|
||||||
Clutter.ungrab_pointer(this._eventHandler);
|
Clutter.ungrab_pointer();
|
||||||
Clutter.ungrab_keyboard(this._eventHandler);
|
Clutter.ungrab_keyboard();
|
||||||
this._eventHandler.destroy();
|
this._eventHandler.destroy();
|
||||||
this._eventHandler = null;
|
this._eventHandler = null;
|
||||||
this.emit('closed');
|
this.emit('closed');
|
||||||
@ -688,7 +681,8 @@ const Memory = new Lang.Class({
|
|||||||
const Extensions = new Lang.Class({
|
const Extensions = new Lang.Class({
|
||||||
Name: 'Extensions',
|
Name: 'Extensions',
|
||||||
|
|
||||||
_init: function() {
|
_init: function(lookingGlass) {
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
this.actor = new St.BoxLayout({ vertical: true,
|
this.actor = new St.BoxLayout({ vertical: true,
|
||||||
name: 'lookingGlassExtensions' });
|
name: 'lookingGlassExtensions' });
|
||||||
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||||
@ -869,15 +863,13 @@ const LookingGlass = new Lang.Class({
|
|||||||
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
||||||
this.actor.add_actor(toolbar);
|
this.actor.add_actor(toolbar);
|
||||||
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: 24 });
|
icon_size: 24 });
|
||||||
toolbar.add_actor(inspectIcon);
|
toolbar.add_actor(inspectIcon);
|
||||||
inspectIcon.reactive = true;
|
inspectIcon.reactive = true;
|
||||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||||
let inspector = new Inspector(this);
|
let inspector = new Inspector(this);
|
||||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
|
||||||
target);
|
|
||||||
}));
|
}));
|
||||||
inspector.connect('closed', Lang.bind(this, function() {
|
inspector.connect('closed', Lang.bind(this, function() {
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
@ -917,7 +909,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._memory = new Memory();
|
this._memory = new Memory();
|
||||||
notebook.appendPage('Memory', this._memory.actor);
|
notebook.appendPage('Memory', this._memory.actor);
|
||||||
|
|
||||||
this._extensions = new Extensions();
|
this._extensions = new Extensions(this);
|
||||||
notebook.appendPage('Extensions', this._extensions.actor);
|
notebook.appendPage('Extensions', this._extensions.actor);
|
||||||
|
|
||||||
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
||||||
@ -955,9 +947,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
_updateFont: function() {
|
_updateFont: function() {
|
||||||
let fontName = this._interfaceSettings.get_string('monospace-font-name');
|
let fontName = this._interfaceSettings.get_string('monospace-font-name');
|
||||||
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
|
let fontDesc = Pango.FontDescription.from_string(fontName);
|
||||||
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
|
|
||||||
let fontDesc = Pango.font_description_from_string(fontName);
|
|
||||||
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
||||||
// monospace font to be bold/oblique/etc. Could easily be added here.
|
// monospace font to be bold/oblique/etc. Could easily be added here.
|
||||||
this.actor.style =
|
this.actor.style =
|
||||||
@ -995,28 +985,18 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
_showCompletions: function(completions) {
|
_showCompletions: function(completions) {
|
||||||
if (!this._completionActor) {
|
if (!this._completionActor) {
|
||||||
let actor = new St.BoxLayout({ vertical: true });
|
this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
|
||||||
|
this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
|
this._completionActor.clutter_text.line_wrap = true;
|
||||||
this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
|
||||||
this._completionText.clutter_text.line_wrap = true;
|
|
||||||
actor.add(this._completionText);
|
|
||||||
|
|
||||||
let line = new Clutter.Rectangle();
|
|
||||||
let padBin = new St.Bin({ x_fill: true, y_fill: true });
|
|
||||||
padBin.add_actor(line);
|
|
||||||
actor.add(padBin);
|
|
||||||
|
|
||||||
this._completionActor = actor;
|
|
||||||
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._completionText.set_text(completions.join(', '));
|
this._completionActor.set_text(completions.join(', '));
|
||||||
|
|
||||||
// Setting the height to -1 allows us to get its actual preferred height rather than
|
// Setting the height to -1 allows us to get its actual preferred height rather than
|
||||||
// whatever was last given in set_height by Tweener.
|
// whatever was last given in set_height by Tweener.
|
||||||
this._completionActor.set_height(-1);
|
this._completionActor.set_height(-1);
|
||||||
let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
|
let [minHeight, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
|
||||||
|
|
||||||
// Don't reanimate if we are already visible
|
// Don't reanimate if we are already visible
|
||||||
if (this._completionActor.visible) {
|
if (this._completionActor.visible) {
|
||||||
@ -1062,6 +1042,10 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._entry.text = '';
|
this._entry.text = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
inspect: function(x, y) {
|
||||||
|
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||||
|
},
|
||||||
|
|
||||||
getIt: function () {
|
getIt: function () {
|
||||||
return this._it;
|
return this._it;
|
||||||
},
|
},
|
||||||
@ -1094,8 +1078,8 @@ const LookingGlass = new Lang.Class({
|
|||||||
this.actor.width = myWidth;
|
this.actor.width = myWidth;
|
||||||
this.actor.height = myHeight;
|
this.actor.height = myHeight;
|
||||||
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
||||||
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
|
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
|
||||||
this._targetY + Math.floor(myHeight * 0.1));
|
primary.y + this._targetY + Math.floor(myHeight * 0.1));
|
||||||
},
|
},
|
||||||
|
|
||||||
insertObject: function(obj) {
|
insertObject: function(obj) {
|
||||||
@ -1134,7 +1118,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
if (this._open)
|
if (this._open)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!Main.pushModal(this._entry))
|
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._notebook.selectIndex(0);
|
this._notebook.selectIndex(0);
|
||||||
|
@ -12,7 +12,7 @@ const Signals = imports.signals;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MagnifierDBus = imports.ui.magnifierDBus;
|
const MagnifierDBus = imports.ui.magnifierDBus;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const PointerWatcher = imports.ui.pointerWatcher;
|
||||||
|
|
||||||
const MOUSE_POLL_FREQUENCY = 50;
|
const MOUSE_POLL_FREQUENCY = 50;
|
||||||
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
||||||
@ -56,7 +56,7 @@ const Magnifier = new Lang.Class({
|
|||||||
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
||||||
this._mouseSprite = new Clutter.Texture();
|
this._mouseSprite = new Clutter.Texture();
|
||||||
xfixesCursor.update_texture_image(this._mouseSprite);
|
xfixesCursor.update_texture_image(this._mouseSprite);
|
||||||
this._cursorRoot = new Clutter.Group();
|
this._cursorRoot = new Clutter.Actor();
|
||||||
this._cursorRoot.add_actor(this._mouseSprite);
|
this._cursorRoot.add_actor(this._mouseSprite);
|
||||||
|
|
||||||
// Create the first ZoomRegion and initialize it according to the
|
// Create the first ZoomRegion and initialize it according to the
|
||||||
@ -136,11 +136,8 @@ const Magnifier = new Lang.Class({
|
|||||||
* Turn on mouse tracking, if not already doing so.
|
* Turn on mouse tracking, if not already doing so.
|
||||||
*/
|
*/
|
||||||
startTrackingMouse: function() {
|
startTrackingMouse: function() {
|
||||||
if (!this._mouseTrackingId)
|
if (!this._pointerWatch)
|
||||||
this._mouseTrackingId = Mainloop.timeout_add(
|
this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(MOUSE_POLL_FREQUENCY, Lang.bind(this, this.scrollToMousePos));
|
||||||
MOUSE_POLL_FREQUENCY,
|
|
||||||
Lang.bind(this, this.scrollToMousePos)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,10 +145,10 @@ const Magnifier = new Lang.Class({
|
|||||||
* Turn off mouse tracking, if not already doing so.
|
* Turn off mouse tracking, if not already doing so.
|
||||||
*/
|
*/
|
||||||
stopTrackingMouse: function() {
|
stopTrackingMouse: function() {
|
||||||
if (this._mouseTrackingId)
|
if (this._pointerWatch)
|
||||||
Mainloop.source_remove(this._mouseTrackingId);
|
this._pointerWatch.remove();
|
||||||
|
|
||||||
this._mouseTrackingId = null;
|
this._pointerWatch = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,8 +300,7 @@ const Magnifier = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
setCrosshairsColor: function(color) {
|
setCrosshairsColor: function(color) {
|
||||||
if (this._crossHairs) {
|
if (this._crossHairs) {
|
||||||
let clutterColor = new Clutter.Color();
|
let [res, clutterColor] = Clutter.Color.from_string(color);
|
||||||
clutterColor.from_string(color);
|
|
||||||
this._crossHairs.setColor(clutterColor);
|
this._crossHairs.setColor(clutterColor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -602,7 +598,7 @@ const Magnifier = new Lang.Class({
|
|||||||
// Applies only to the first zoom region.
|
// Applies only to the first zoom region.
|
||||||
if (this._zoomRegions.length) {
|
if (this._zoomRegions.length) {
|
||||||
this._zoomRegions[0].setColorSaturation(
|
this._zoomRegions[0].setColorSaturation(
|
||||||
this._settings.get_boolean(COLOR_SATURATION_KEY)
|
this._settings.get_double(COLOR_SATURATION_KEY)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1081,21 +1077,21 @@ const ZoomRegion = new Lang.Class({
|
|||||||
// hide the magnified region from CLUTTER_PICK_ALL
|
// hide the magnified region from CLUTTER_PICK_ALL
|
||||||
Shell.util_set_hidden_from_pick (this._magView, true);
|
Shell.util_set_hidden_from_pick (this._magView, true);
|
||||||
|
|
||||||
// Append a Clutter.Group to clip the contents of the magnified view.
|
// Add a group to clip the contents of the magnified view.
|
||||||
let mainGroup = new Clutter.Group({ clip_to_allocation: true });
|
let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
|
||||||
this._magView.set_child(mainGroup);
|
this._magView.set_child(mainGroup);
|
||||||
|
|
||||||
// Add a background for when the magnified uiGroup is scrolled
|
// Add a background for when the magnified uiGroup is scrolled
|
||||||
// out of view (don't want to see desktop showing through).
|
// out of view (don't want to see desktop showing through).
|
||||||
this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
|
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
|
||||||
|
width: global.screen_width,
|
||||||
|
height: global.screen_height });
|
||||||
mainGroup.add_actor(this._background);
|
mainGroup.add_actor(this._background);
|
||||||
|
|
||||||
// Clone the group that contains all of UI on the screen. This is the
|
// Clone the group that contains all of UI on the screen. This is the
|
||||||
// chrome, the windows, etc.
|
// chrome, the windows, etc.
|
||||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
||||||
mainGroup.add_actor(this._uiGroupClone);
|
mainGroup.add_actor(this._uiGroupClone);
|
||||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
|
||||||
this._background.set_size(global.screen_width, global.screen_height);
|
|
||||||
|
|
||||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||||
// it.
|
// it.
|
||||||
@ -1329,7 +1325,7 @@ const ZoomRegion = new Lang.Class({
|
|||||||
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
|
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
|
||||||
|
|
||||||
let [x, y] = this._screenToViewPort(0, 0);
|
let [x, y] = this._screenToViewPort(0, 0);
|
||||||
this._uiGroupClone.set_position(x, y);
|
this._uiGroupClone.set_position(Math.round(x), Math.round(y));
|
||||||
|
|
||||||
this._updateMousePosition();
|
this._updateMousePosition();
|
||||||
},
|
},
|
||||||
@ -1357,7 +1353,6 @@ const ZoomRegion = new Lang.Class({
|
|||||||
if (!this.isActive())
|
if (!this.isActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
|
||||||
this._background.set_size(global.screen_width, global.screen_height);
|
this._background.set_size(global.screen_width, global.screen_height);
|
||||||
|
|
||||||
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
||||||
@ -1381,15 +1376,15 @@ const Crosshairs = new Lang.Class({
|
|||||||
let groupWidth = global.screen_width * 3;
|
let groupWidth = global.screen_width * 3;
|
||||||
let groupHeight = global.screen_height * 3;
|
let groupHeight = global.screen_height * 3;
|
||||||
|
|
||||||
this._actor = new Clutter.Group({
|
this._actor = new Clutter.Actor({
|
||||||
clip_to_allocation: false,
|
clip_to_allocation: false,
|
||||||
width: groupWidth,
|
width: groupWidth,
|
||||||
height: groupHeight
|
height: groupHeight
|
||||||
});
|
});
|
||||||
this._horizLeftHair = new Clutter.Rectangle();
|
this._horizLeftHair = new Clutter.Actor();
|
||||||
this._horizRightHair = new Clutter.Rectangle();
|
this._horizRightHair = new Clutter.Actor();
|
||||||
this._vertTopHair = new Clutter.Rectangle();
|
this._vertTopHair = new Clutter.Actor();
|
||||||
this._vertBottomHair = new Clutter.Rectangle();
|
this._vertBottomHair = new Clutter.Actor();
|
||||||
this._actor.add_actor(this._horizLeftHair);
|
this._actor.add_actor(this._horizLeftHair);
|
||||||
this._actor.add_actor(this._horizRightHair);
|
this._actor.add_actor(this._horizRightHair);
|
||||||
this._actor.add_actor(this._vertTopHair);
|
this._actor.add_actor(this._vertTopHair);
|
||||||
@ -1460,10 +1455,10 @@ const Crosshairs = new Lang.Class({
|
|||||||
* @clutterColor: The color as a Clutter.Color.
|
* @clutterColor: The color as a Clutter.Color.
|
||||||
*/
|
*/
|
||||||
setColor: function(clutterColor) {
|
setColor: function(clutterColor) {
|
||||||
this._horizLeftHair.set_color(clutterColor);
|
this._horizLeftHair.background_color = clutterColor;
|
||||||
this._horizRightHair.set_color(clutterColor);
|
this._horizRightHair.background_color = clutterColor;
|
||||||
this._vertTopHair.set_color(clutterColor);
|
this._vertTopHair.background_color = clutterColor;
|
||||||
this._vertBottomHair.set_color(clutterColor);
|
this._vertBottomHair.background_color = clutterColor;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1472,9 +1467,7 @@ const Crosshairs = new Lang.Class({
|
|||||||
* @color: The color as a Clutter.Color.
|
* @color: The color as a Clutter.Color.
|
||||||
*/
|
*/
|
||||||
getColor: function() {
|
getColor: function() {
|
||||||
let clutterColor = new Clutter.Color();
|
return this._horizLeftHair.get_color();
|
||||||
this._horizLeftHair.get_color(clutterColor);
|
|
||||||
return clutterColor;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1641,14 +1634,14 @@ const MagShaderEffects = new Lang.Class({
|
|||||||
_init: function(uiGroupClone) {
|
_init: function(uiGroupClone) {
|
||||||
this._inverse = new Shell.InvertLightnessEffect();
|
this._inverse = new Shell.InvertLightnessEffect();
|
||||||
this._brightnessContrast = new Clutter.BrightnessContrastEffect();
|
this._brightnessContrast = new Clutter.BrightnessContrastEffect();
|
||||||
this._colorSaturation = new Clutter.DesaturateEffect();
|
this._colorDesaturation = new Clutter.DesaturateEffect();
|
||||||
this._inverse.set_enabled(false);
|
this._inverse.set_enabled(false);
|
||||||
this._brightnessContrast.set_enabled(false);
|
this._brightnessContrast.set_enabled(false);
|
||||||
|
|
||||||
this._magView = uiGroupClone;
|
this._magView = uiGroupClone;
|
||||||
this._magView.add_effect(this._inverse);
|
this._magView.add_effect(this._inverse);
|
||||||
this._magView.add_effect(this._brightnessContrast);
|
this._magView.add_effect(this._brightnessContrast);
|
||||||
this._magView.add_effect(this._colorSaturation);
|
this._magView.add_effect(this._colorDesaturation);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1659,7 +1652,7 @@ const MagShaderEffects = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
destroyEffects: function() {
|
destroyEffects: function() {
|
||||||
this._magView.clear_effects();
|
this._magView.clear_effects();
|
||||||
this._colorSaturation = null;
|
this._colorDesaturation = null;
|
||||||
this._brightnessContrast = null;
|
this._brightnessContrast = null;
|
||||||
this._inverse = null;
|
this._inverse = null;
|
||||||
this._magView = null;
|
this._magView = null;
|
||||||
@ -1684,11 +1677,11 @@ const MagShaderEffects = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setColorSaturation: function(factor) {
|
setColorSaturation: function(factor) {
|
||||||
this._colorSaturation.set_factor(factor);
|
this._colorDesaturation.set_factor(1.0 - factor);
|
||||||
},
|
},
|
||||||
|
|
||||||
getColorSaturation: function() {
|
getColorSaturation: function() {
|
||||||
return this._colorSaturation.get_factor();
|
return 1.0 - this._colorDesaturation.get_factor();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
611
js/ui/main.js
@ -10,134 +10,86 @@ const Meta = imports.gi.Meta;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const AutomountManager = imports.ui.automountManager;
|
const Components = imports.ui.components;
|
||||||
const AutorunManager = imports.ui.autorunManager;
|
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
|
||||||
const KeyringPrompt = imports.ui.keyringPrompt;
|
|
||||||
const Environment = imports.ui.environment;
|
const Environment = imports.ui.environment;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||||
const Keyboard = imports.ui.keyboard;
|
const Keyboard = imports.ui.keyboard;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const OsdWindow = imports.ui.osdWindow;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const PlaceDisplay = imports.ui.placeDisplay;
|
const Params = imports.misc.params;
|
||||||
const RunDialog = imports.ui.runDialog;
|
const RunDialog = imports.ui.runDialog;
|
||||||
const Layout = imports.ui.layout;
|
const Layout = imports.ui.layout;
|
||||||
|
const LoginManager = imports.misc.loginManager;
|
||||||
const LookingGlass = imports.ui.lookingGlass;
|
const LookingGlass = imports.ui.lookingGlass;
|
||||||
const NetworkAgent = imports.ui.networkAgent;
|
|
||||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||||
|
const ScreenShield = imports.ui.screenShield;
|
||||||
const Scripting = imports.ui.scripting;
|
const Scripting = imports.ui.scripting;
|
||||||
const SessionMode = imports.ui.sessionMode;
|
const SessionMode = imports.ui.sessionMode;
|
||||||
const ShellDBus = imports.ui.shellDBus;
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
const TelepathyClient = imports.ui.telepathyClient;
|
|
||||||
const WindowManager = imports.ui.windowManager;
|
const WindowManager = imports.ui.windowManager;
|
||||||
const Magnifier = imports.ui.magnifier;
|
const Magnifier = imports.ui.magnifier;
|
||||||
const XdndHandler = imports.ui.xdndHandler;
|
const XdndHandler = imports.ui.xdndHandler;
|
||||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
|
||||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
|
||||||
|
|
||||||
let automountManager = null;
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||||
let autorunManager = null;
|
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
|
||||||
|
|
||||||
|
let componentManager = null;
|
||||||
let panel = null;
|
let panel = null;
|
||||||
let hotCorners = [];
|
|
||||||
let overview = null;
|
let overview = null;
|
||||||
let runDialog = null;
|
let runDialog = null;
|
||||||
let lookingGlass = null;
|
let lookingGlass = null;
|
||||||
let wm = null;
|
let wm = null;
|
||||||
let messageTray = null;
|
let messageTray = null;
|
||||||
|
let screenShield = null;
|
||||||
let notificationDaemon = null;
|
let notificationDaemon = null;
|
||||||
let windowAttentionHandler = null;
|
let windowAttentionHandler = null;
|
||||||
let telepathyClient = null;
|
|
||||||
let ctrlAltTabManager = null;
|
let ctrlAltTabManager = null;
|
||||||
let recorder = null;
|
let osdWindow = null;
|
||||||
let sessionMode = null;
|
let sessionMode = null;
|
||||||
let shellDBusService = null;
|
let shellDBusService = null;
|
||||||
let shellMountOpDBusService = null;
|
let shellMountOpDBusService = null;
|
||||||
|
let screenSaverDBus = null;
|
||||||
let modalCount = 0;
|
let modalCount = 0;
|
||||||
|
let keybindingMode = Shell.KeyBindingMode.NONE;
|
||||||
let modalActorFocusStack = [];
|
let modalActorFocusStack = [];
|
||||||
let uiGroup = null;
|
let uiGroup = null;
|
||||||
let magnifier = null;
|
let magnifier = null;
|
||||||
let xdndHandler = null;
|
let xdndHandler = null;
|
||||||
let statusIconDispatcher = null;
|
|
||||||
let keyboard = null;
|
let keyboard = null;
|
||||||
let layoutManager = null;
|
let layoutManager = null;
|
||||||
let networkAgent = null;
|
|
||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
let _overridesSettings = null;
|
let _a11ySettings = null;
|
||||||
|
let dynamicWorkspacesSchema = null;
|
||||||
|
|
||||||
let background = null;
|
function _sessionUpdated() {
|
||||||
|
_loadDefaultStylesheet();
|
||||||
|
|
||||||
function createUserSession() {
|
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||||
// Load the calendar server. Note that we are careful about
|
Shell.KeyBindingMode.NORMAL |
|
||||||
// not loading any events until the user presses the clock
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
global.launch_calendar_server();
|
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
||||||
|
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW);
|
||||||
|
|
||||||
telepathyClient = new TelepathyClient.Client();
|
wm.setCustomKeybindingHandler('panel-run-dialog',
|
||||||
automountManager = new AutomountManager.AutomountManager();
|
Shell.KeyBindingMode.NORMAL |
|
||||||
autorunManager = new AutorunManager.AutorunManager();
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
networkAgent = new NetworkAgent.NetworkAgent();
|
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||||
|
|
||||||
_initRecorder();
|
if (!sessionMode.hasRunDialog && lookingGlass)
|
||||||
}
|
lookingGlass.close();
|
||||||
|
|
||||||
function createGDMSession() {
|
|
||||||
// We do this this here instead of at the top to prevent GDM
|
|
||||||
// related code from getting loaded in normal user sessions
|
|
||||||
const LoginDialog = imports.gdm.loginDialog;
|
|
||||||
|
|
||||||
let loginDialog = new LoginDialog.LoginDialog();
|
|
||||||
loginDialog.connect('loaded', function() {
|
|
||||||
loginDialog.open();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createInitialSetupSession() {
|
|
||||||
networkAgent = new NetworkAgent.NetworkAgent();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initRecorder() {
|
|
||||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
|
||||||
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
|
||||||
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
|
||||||
|
|
||||||
global.display.add_keybinding('toggle-recording',
|
|
||||||
bindingSettings,
|
|
||||||
Meta.KeyBindingFlags.NONE, function() {
|
|
||||||
if (recorder == null) {
|
|
||||||
recorder = new Shell.Recorder({ stage: global.stage });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recorder.is_recording()) {
|
|
||||||
recorder.close();
|
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
|
||||||
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
|
||||||
// read the parameters from GSettings always in case they have changed
|
|
||||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
|
||||||
/* Translators: this is a filename used for screencast recording */
|
|
||||||
// xgettext:no-c-format
|
|
||||||
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
|
|
||||||
let pipeline = recorderSettings.get_string('pipeline');
|
|
||||||
|
|
||||||
if (!pipeline.match(/^\s*$/))
|
|
||||||
recorder.set_pipeline(pipeline);
|
|
||||||
else
|
|
||||||
recorder.set_pipeline(null);
|
|
||||||
|
|
||||||
Meta.disable_unredirect_for_screen(global.screen);
|
|
||||||
recorder.record();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
@ -151,9 +103,33 @@ function start() {
|
|||||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||||
|
|
||||||
sessionMode = new SessionMode.SessionMode();
|
sessionMode = new SessionMode.SessionMode();
|
||||||
|
sessionMode.connect('sessions-loaded', _sessionsLoaded);
|
||||||
|
sessionMode.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sessionsLoaded() {
|
||||||
|
sessionMode.connect('updated', _sessionUpdated);
|
||||||
|
_initializePrefs();
|
||||||
|
_initializeUI();
|
||||||
|
|
||||||
shellDBusService = new ShellDBus.GnomeShell();
|
shellDBusService = new ShellDBus.GnomeShell();
|
||||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||||
|
|
||||||
|
_sessionUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initializePrefs() {
|
||||||
|
let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
|
||||||
|
for (let i = 0; i < keys.length; i++)
|
||||||
|
Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
|
||||||
|
|
||||||
|
if (keys.indexOf('dynamic-workspaces') > -1)
|
||||||
|
dynamicWorkspacesSchema = sessionMode.overridesSchema;
|
||||||
|
else
|
||||||
|
dynamicWorkspacesSchema = 'org.gnome.mutter';
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initializeUI() {
|
||||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||||
// also initialize ShellAppSystem first. ShellAppSystem
|
// also initialize ShellAppSystem first. ShellAppSystem
|
||||||
// needs to load all the .desktop files, and ShellWindowTracker
|
// needs to load all the .desktop files, and ShellWindowTracker
|
||||||
@ -162,103 +138,54 @@ function start() {
|
|||||||
// and recalculate application associations, so to avoid
|
// and recalculate application associations, so to avoid
|
||||||
// races for now we initialize it here. It's better to
|
// races for now we initialize it here. It's better to
|
||||||
// be predictable anyways.
|
// be predictable anyways.
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
Shell.WindowTracker.get_default();
|
||||||
Shell.AppUsage.get_default();
|
Shell.AppUsage.get_default();
|
||||||
|
|
||||||
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
|
_loadDefaultStylesheet();
|
||||||
|
|
||||||
// The stage is always covered so Clutter doesn't need to clear it; however
|
|
||||||
// the color is used as the default contents for the Mutter root background
|
|
||||||
// actor so set it anyways.
|
|
||||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
|
||||||
global.stage.no_clear_hint = true;
|
|
||||||
|
|
||||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
|
||||||
loadTheme();
|
|
||||||
|
|
||||||
// Set up stage hierarchy to group all UI actors under one container.
|
|
||||||
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
|
|
||||||
uiGroup.connect('allocate',
|
|
||||||
function (actor, box, flags) {
|
|
||||||
let children = uiGroup.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++)
|
|
||||||
children[i].allocate_preferred_size(flags);
|
|
||||||
});
|
|
||||||
uiGroup.connect('get-preferred-width',
|
|
||||||
function(actor, forHeight, alloc) {
|
|
||||||
let width = global.stage.width;
|
|
||||||
[alloc.min_size, alloc.natural_size] = [width, width];
|
|
||||||
});
|
|
||||||
uiGroup.connect('get-preferred-height',
|
|
||||||
function(actor, forWidth, alloc) {
|
|
||||||
let height = global.stage.height;
|
|
||||||
[alloc.min_size, alloc.natural_size] = [height, height];
|
|
||||||
});
|
|
||||||
global.window_group.reparent(uiGroup);
|
|
||||||
global.overlay_group.reparent(uiGroup);
|
|
||||||
global.stage.add_actor(uiGroup);
|
|
||||||
|
|
||||||
|
// Setup the stage hierarchy early
|
||||||
layoutManager = new Layout.LayoutManager();
|
layoutManager = new Layout.LayoutManager();
|
||||||
|
|
||||||
|
// Various parts of the codebase still refers to Main.uiGroup
|
||||||
|
// instead using the layoutManager. This keeps that code
|
||||||
|
// working until it's updated.
|
||||||
|
uiGroup = layoutManager.uiGroup;
|
||||||
|
|
||||||
xdndHandler = new XdndHandler.XdndHandler();
|
xdndHandler = new XdndHandler.XdndHandler();
|
||||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||||
|
osdWindow = new OsdWindow.OsdWindow();
|
||||||
overview = new Overview.Overview();
|
overview = new Overview.Overview();
|
||||||
magnifier = new Magnifier.Magnifier();
|
|
||||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
|
||||||
panel = new Panel.Panel();
|
|
||||||
wm = new WindowManager.WindowManager();
|
wm = new WindowManager.WindowManager();
|
||||||
|
magnifier = new Magnifier.Magnifier();
|
||||||
|
if (LoginManager.canLock())
|
||||||
|
screenShield = new ScreenShield.ScreenShield();
|
||||||
|
|
||||||
|
panel = new Panel.Panel();
|
||||||
messageTray = new MessageTray.MessageTray();
|
messageTray = new MessageTray.MessageTray();
|
||||||
keyboard = new Keyboard.Keyboard();
|
keyboard = new Keyboard.Keyboard();
|
||||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||||
|
componentManager = new Components.ComponentManager();
|
||||||
sessionMode.createSession();
|
|
||||||
|
|
||||||
panel.startStatusArea();
|
|
||||||
|
|
||||||
layoutManager.init();
|
layoutManager.init();
|
||||||
keyboard.init();
|
|
||||||
overview.init();
|
overview.init();
|
||||||
|
|
||||||
if (sessionMode.hasWorkspaces)
|
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
||||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
|
||||||
false, -1, 1);
|
|
||||||
|
|
||||||
if (sessionMode.allowExtensions) {
|
global.display.connect('overlay-key', Lang.bind(overview, function () {
|
||||||
ExtensionDownloader.init();
|
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
|
||||||
ExtensionSystem.loadExtensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionMode.hasRunDialog) {
|
|
||||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
|
||||||
getRunDialog().open();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionMode.hasOverview) {
|
|
||||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
|
||||||
overview.toggle();
|
overview.toggle();
|
||||||
});
|
}));
|
||||||
|
|
||||||
global.display.connect('overlay-key',
|
|
||||||
Lang.bind(overview, overview.toggle));
|
|
||||||
}
|
|
||||||
|
|
||||||
statusIconDispatcher.start(messageTray.actor);
|
|
||||||
|
|
||||||
// Provide the bus object for gnome-session to
|
// Provide the bus object for gnome-session to
|
||||||
// initiate logouts.
|
// initiate logouts.
|
||||||
EndSessionDialog.init();
|
EndSessionDialog.init();
|
||||||
|
|
||||||
// Attempt to become a PolicyKit authentication agent
|
// We're ready for the session manager to move to the next phase
|
||||||
PolkitAuthenticationAgent.init()
|
Meta.register_with_session();
|
||||||
|
|
||||||
// Become a prompter for gnome keyring
|
|
||||||
KeyringPrompt.init();
|
|
||||||
|
|
||||||
_startDate = new Date();
|
_startDate = new Date();
|
||||||
|
|
||||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
|
||||||
|
|
||||||
log('GNOME Shell started at ' + _startDate);
|
log('GNOME Shell started at ' + _startDate);
|
||||||
|
|
||||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||||
@ -268,197 +195,35 @@ function start() {
|
|||||||
Scripting.runPerfScript(module, perfOutput);
|
Scripting.runPerfScript(module, perfOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
_overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
|
ExtensionDownloader.init();
|
||||||
_overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
|
ExtensionSystem.init();
|
||||||
|
|
||||||
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
if (sessionMode.isGreeter && screenShield) {
|
||||||
|
layoutManager.connect('startup-prepared', function() {
|
||||||
|
screenShield.showDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
layoutManager.connect('startup-complete', function() {
|
||||||
global.screen.connect('window-left-monitor', _windowLeftMonitor);
|
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
||||||
global.screen.connect('restacked', _windowsRestacked);
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
|
}
|
||||||
_nWorkspacesChanged();
|
if (screenShield) {
|
||||||
|
screenShield.lockIfWasLocked();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let _workspaces = [];
|
function _loadDefaultStylesheet() {
|
||||||
let _checkWorkspacesId = 0;
|
if (!sessionMode.isPrimary)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
|
||||||
* When the last window closed on a workspace is a dialog or splash
|
if (_defaultCssStylesheet == stylesheet)
|
||||||
* screen, we assume that it might be an initial window shown before
|
return;
|
||||||
* the main window of an application, and give the app a grace period
|
|
||||||
* where it can map another window before we remove the workspace.
|
|
||||||
*/
|
|
||||||
const LAST_WINDOW_GRACE_TIME = 1000;
|
|
||||||
|
|
||||||
function _checkWorkspaces() {
|
_defaultCssStylesheet = stylesheet;
|
||||||
let i;
|
loadTheme();
|
||||||
let emptyWorkspaces = [];
|
|
||||||
|
|
||||||
if (!Meta.prefs_get_dynamic_workspaces()) {
|
|
||||||
_checkWorkspacesId = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < _workspaces.length; i++) {
|
|
||||||
let lastRemoved = _workspaces[i]._lastRemovedWindow;
|
|
||||||
if ((lastRemoved &&
|
|
||||||
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
|
||||||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
|
||||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
|
||||||
_workspaces[i]._keepAliveId)
|
|
||||||
emptyWorkspaces[i] = false;
|
|
||||||
else
|
|
||||||
emptyWorkspaces[i] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
|
||||||
for (i = 0; i < sequences.length; i++) {
|
|
||||||
let index = sequences[i].get_workspace();
|
|
||||||
if (index >= 0 && index <= global.screen.n_workspaces)
|
|
||||||
emptyWorkspaces[index] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let windows = global.get_window_actors();
|
|
||||||
for (i = 0; i < windows.length; i++) {
|
|
||||||
let win = windows[i];
|
|
||||||
|
|
||||||
if (win.get_meta_window().is_on_all_workspaces())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let workspaceIndex = win.get_workspace();
|
|
||||||
emptyWorkspaces[workspaceIndex] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have an empty workspace at the end, add one
|
|
||||||
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
|
||||||
global.screen.append_new_workspace(false, global.get_current_time());
|
|
||||||
emptyWorkspaces.push(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
||||||
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
|
|
||||||
activeWorkspaceIndex < emptyWorkspaces.length - 1);
|
|
||||||
// Don't enter the overview when removing multiple empty workspaces at startup
|
|
||||||
let showOverview = (removingCurrentWorkspace &&
|
|
||||||
!emptyWorkspaces.every(function(x) { return x; }));
|
|
||||||
|
|
||||||
if (removingCurrentWorkspace) {
|
|
||||||
// "Merge" the empty workspace we are removing with the one at the end
|
|
||||||
wm.blockAnimations();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
|
||||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
|
||||||
if (emptyWorkspaces[i])
|
|
||||||
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removingCurrentWorkspace) {
|
|
||||||
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
|
||||||
wm.unblockAnimations();
|
|
||||||
|
|
||||||
if (!overview.visible && showOverview)
|
|
||||||
overview.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
_checkWorkspacesId = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keepWorkspaceAlive(workspace, duration) {
|
|
||||||
if (workspace._keepAliveId)
|
|
||||||
Mainloop.source_remove(workspace._keepAliveId);
|
|
||||||
|
|
||||||
workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
|
|
||||||
workspace._keepAliveId = 0;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowRemoved(workspace, window) {
|
|
||||||
workspace._lastRemovedWindow = window;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
|
|
||||||
if (workspace._lastRemovedWindow == window) {
|
|
||||||
workspace._lastRemovedWindow = null;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
|
|
||||||
// If the window left the primary monitor, that
|
|
||||||
// might make that workspace empty
|
|
||||||
if (monitorIndex == layoutManager.primaryIndex)
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
|
|
||||||
// If the window entered the primary monitor, that
|
|
||||||
// might make that workspace non-empty
|
|
||||||
if (monitorIndex == layoutManager.primaryIndex)
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowsRestacked() {
|
|
||||||
// Figure out where the pointer is in case we lost track of
|
|
||||||
// it during a grab. (In particular, if a trayicon popup menu
|
|
||||||
// is dismissed, see if we need to close the message tray.)
|
|
||||||
global.sync_pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _queueCheckWorkspaces() {
|
|
||||||
if (_checkWorkspacesId == 0)
|
|
||||||
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _nWorkspacesChanged() {
|
|
||||||
let oldNumWorkspaces = _workspaces.length;
|
|
||||||
let newNumWorkspaces = global.screen.n_workspaces;
|
|
||||||
|
|
||||||
if (oldNumWorkspaces == newNumWorkspaces)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
let lostWorkspaces = [];
|
|
||||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
||||||
let w;
|
|
||||||
|
|
||||||
// Assume workspaces are only added at the end
|
|
||||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
|
||||||
_workspaces[w] = global.screen.get_workspace_by_index(w);
|
|
||||||
|
|
||||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
||||||
let workspace = _workspaces[w];
|
|
||||||
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
|
|
||||||
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Assume workspaces are only removed sequentially
|
|
||||||
// (e.g. 2,3,4 - not 2,4,7)
|
|
||||||
let removedIndex;
|
|
||||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
||||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
|
||||||
let workspace = global.screen.get_workspace_by_index(w);
|
|
||||||
if (_workspaces[w] != workspace) {
|
|
||||||
removedIndex = w;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
|
|
||||||
lostWorkspaces.forEach(function(workspace) {
|
|
||||||
workspace.disconnect(workspace._windowAddedId);
|
|
||||||
workspace.disconnect(workspace._windowRemovedId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -499,10 +264,8 @@ function loadTheme() {
|
|||||||
if (_cssStylesheet != null)
|
if (_cssStylesheet != null)
|
||||||
cssStylesheet = _cssStylesheet;
|
cssStylesheet = _cssStylesheet;
|
||||||
|
|
||||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
|
||||||
|
default_stylesheet: _defaultCssStylesheet });
|
||||||
if (sessionMode.extraStylesheet)
|
|
||||||
theme.load_stylesheet(sessionMode.extraStylesheet);
|
|
||||||
|
|
||||||
if (previousTheme) {
|
if (previousTheme) {
|
||||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||||
@ -544,96 +307,6 @@ function notifyError(msg, details) {
|
|||||||
notify(msg, details);
|
notify(msg, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
|
||||||
return win.get_workspace() == workspaceIndex ||
|
|
||||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWindowActorsForWorkspace(workspaceIndex) {
|
|
||||||
return global.get_window_actors().filter(function (win) {
|
|
||||||
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function encapsulates hacks to make certain global keybindings
|
|
||||||
// work even when we are in one of our modes where global keybindings
|
|
||||||
// are disabled with a global grab. (When there is a global grab, then
|
|
||||||
// all key events will be delivered to the stage, so ::captured-event
|
|
||||||
// on the stage can be used for global keybindings.)
|
|
||||||
function _globalKeyPressHandler(actor, event) {
|
|
||||||
if (modalCount == 0)
|
|
||||||
return false;
|
|
||||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!sessionMode.allowKeybindingsWhenModal) {
|
|
||||||
if (modalCount > (overview.visible ? 1 : 0))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let symbol = event.get_key_symbol();
|
|
||||||
let keyCode = event.get_key_code();
|
|
||||||
let ignoredModifiers = global.display.get_ignored_modifier_mask();
|
|
||||||
let modifierState = event.get_state() & ~ignoredModifiers;
|
|
||||||
|
|
||||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
|
||||||
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
|
||||||
|
|
||||||
// This isn't a Meta.KeyBindingAction yet
|
|
||||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
|
||||||
overview.hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
|
||||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
|
|
||||||
modifierState);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
|
||||||
// but that could be considered confusing; we also disable them in the main view.
|
|
||||||
//
|
|
||||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
|
||||||
// if (!sessionMode.hasWorkspaces)
|
|
||||||
// return false;
|
|
||||||
//
|
|
||||||
// wm.actionMoveWorkspaceLeft();
|
|
||||||
// return true;
|
|
||||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
|
||||||
// if (!sessionMode.hasWorkspaces)
|
|
||||||
// return false;
|
|
||||||
//
|
|
||||||
// wm.actionMoveWorkspaceRight();
|
|
||||||
// return true;
|
|
||||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
|
||||||
if (!sessionMode.hasWorkspaces)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
|
||||||
return true;
|
|
||||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
|
||||||
if (!sessionMode.hasWorkspaces)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
|
||||||
return true;
|
|
||||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
|
||||||
case Meta.KeyBindingAction.COMMAND_2:
|
|
||||||
if (!sessionMode.hasRunDialog)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
getRunDialog().open();
|
|
||||||
return true;
|
|
||||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
|
||||||
overview.hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _findModal(actor) {
|
function _findModal(actor) {
|
||||||
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
||||||
if (modalActorFocusStack[i].actor == actor)
|
if (modalActorFocusStack[i].actor == actor)
|
||||||
@ -645,7 +318,7 @@ function _findModal(actor) {
|
|||||||
/**
|
/**
|
||||||
* pushModal:
|
* pushModal:
|
||||||
* @actor: #ClutterActor which will be given keyboard focus
|
* @actor: #ClutterActor which will be given keyboard focus
|
||||||
* @timestamp: optional timestamp
|
* @params: optional parameters
|
||||||
*
|
*
|
||||||
* Ensure we are in a mode where all keyboard and mouse input goes to
|
* Ensure we are in a mode where all keyboard and mouse input goes to
|
||||||
* the stage, and focus @actor. Multiple calls to this function act in
|
* the stage, and focus @actor. Multiple calls to this function act in
|
||||||
@ -656,21 +329,27 @@ function _findModal(actor) {
|
|||||||
* modal stack returns to this actor, reset the focus to the actor
|
* modal stack returns to this actor, reset the focus to the actor
|
||||||
* which was focused at the time pushModal() was invoked.
|
* which was focused at the time pushModal() was invoked.
|
||||||
*
|
*
|
||||||
* @timestamp is optionally used to associate the call with a specific user
|
* @params may be used to provide the following parameters:
|
||||||
* initiated event. If not provided then the value of
|
* - timestamp: used to associate the call with a specific user initiated
|
||||||
* global.get_current_time() is assumed.
|
* event. If not provided then the value of
|
||||||
|
* global.get_current_time() is assumed.
|
||||||
*
|
*
|
||||||
* @options: optional Meta.ModalOptions flags to indicate that the
|
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
||||||
* pointer is alrady grabbed
|
* already grabbed
|
||||||
|
*
|
||||||
|
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
|
||||||
|
* global keybindings; the default of NONE will filter
|
||||||
|
* out all keybindings
|
||||||
*
|
*
|
||||||
* Returns: true iff we successfully acquired a grab or already had one
|
* Returns: true iff we successfully acquired a grab or already had one
|
||||||
*/
|
*/
|
||||||
function pushModal(actor, timestamp, options) {
|
function pushModal(actor, params) {
|
||||||
if (timestamp == undefined)
|
params = Params.parse(params, { timestamp: global.get_current_time(),
|
||||||
timestamp = global.get_current_time();
|
options: 0,
|
||||||
|
keybindingMode: Shell.KeyBindingMode.NONE });
|
||||||
|
|
||||||
if (modalCount == 0) {
|
if (modalCount == 0) {
|
||||||
if (!global.begin_modal(timestamp, options ? options : 0)) {
|
if (!global.begin_modal(params.timestamp, params.options)) {
|
||||||
log('pushModal: invocation of begin_modal failed');
|
log('pushModal: invocation of begin_modal failed');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -683,22 +362,25 @@ function pushModal(actor, timestamp, options) {
|
|||||||
let actorDestroyId = actor.connect('destroy', function() {
|
let actorDestroyId = actor.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
modalActorFocusStack.splice(index, 1);
|
popModal(actor);
|
||||||
});
|
});
|
||||||
let curFocus = global.stage.get_key_focus();
|
|
||||||
let curFocusDestroyId;
|
let prevFocus = global.stage.get_key_focus();
|
||||||
if (curFocus != null) {
|
let prevFocusDestroyId;
|
||||||
curFocusDestroyId = curFocus.connect('destroy', function() {
|
if (prevFocus != null) {
|
||||||
|
prevFocusDestroyId = prevFocus.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
modalActorFocusStack[index].actor = null;
|
modalActorFocusStack[index].prevFocus = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
modalActorFocusStack.push({ actor: actor,
|
modalActorFocusStack.push({ actor: actor,
|
||||||
focus: curFocus,
|
|
||||||
destroyId: actorDestroyId,
|
destroyId: actorDestroyId,
|
||||||
focusDestroyId: curFocusDestroyId });
|
prevFocus: prevFocus,
|
||||||
|
prevFocusDestroyId: prevFocusDestroyId,
|
||||||
|
keybindingMode: keybindingMode });
|
||||||
|
|
||||||
|
keybindingMode = params.keybindingMode;
|
||||||
global.stage.set_key_focus(actor);
|
global.stage.set_key_focus(actor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -725,6 +407,7 @@ function popModal(actor, timestamp) {
|
|||||||
global.stage.set_key_focus(null);
|
global.stage.set_key_focus(null);
|
||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||||
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
|
|
||||||
throw new Error('incorrect pop');
|
throw new Error('incorrect pop');
|
||||||
}
|
}
|
||||||
@ -735,17 +418,34 @@ function popModal(actor, timestamp) {
|
|||||||
record.actor.disconnect(record.destroyId);
|
record.actor.disconnect(record.destroyId);
|
||||||
|
|
||||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||||
if (record.focus)
|
if (record.prevFocus)
|
||||||
record.focus.disconnect(record.focusDestroyId);
|
record.prevFocus.disconnect(record.prevFocusDestroyId);
|
||||||
global.stage.set_key_focus(record.focus);
|
keybindingMode = record.keybindingMode;
|
||||||
|
global.stage.set_key_focus(record.prevFocus);
|
||||||
} else {
|
} else {
|
||||||
|
// If we have:
|
||||||
|
// global.stage.set_focus(a);
|
||||||
|
// Main.pushModal(b);
|
||||||
|
// Main.pushModal(c);
|
||||||
|
// Main.pushModal(d);
|
||||||
|
//
|
||||||
|
// then we have the stack:
|
||||||
|
// [{ prevFocus: a, actor: b },
|
||||||
|
// { prevFocus: b, actor: c },
|
||||||
|
// { prevFocus: c, actor: d }]
|
||||||
|
//
|
||||||
|
// When actor c is destroyed/popped, if we only simply remove the
|
||||||
|
// record, then the focus stack will be [a, c], rather than the correct
|
||||||
|
// [a, b]. Shift the focus stack up before removing the record to ensure
|
||||||
|
// that we get the correct result.
|
||||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||||
if (t.focus)
|
if (t.prevFocus)
|
||||||
t.focus.disconnect(t.focusDestroyId);
|
t.prevFocus.disconnect(t.prevFocusDestroyId);
|
||||||
// Remove from the middle, shift the focus chain up
|
// Remove from the middle, shift the focus chain up
|
||||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
|
||||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
|
||||||
|
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modalActorFocusStack.splice(focusIndex, 1);
|
modalActorFocusStack.splice(focusIndex, 1);
|
||||||
@ -756,6 +456,7 @@ function popModal(actor, timestamp) {
|
|||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLookingGlass() {
|
function createLookingGlass() {
|
||||||
@ -765,11 +466,11 @@ function createLookingGlass() {
|
|||||||
return lookingGlass;
|
return lookingGlass;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRunDialog() {
|
function openRunDialog() {
|
||||||
if (runDialog == null) {
|
if (runDialog == null) {
|
||||||
runDialog = new RunDialog.RunDialog();
|
runDialog = new RunDialog.RunDialog();
|
||||||
}
|
}
|
||||||
return runDialog;
|
runDialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
2115
js/ui/messageTray.js
@ -14,14 +14,19 @@ const Atk = imports.gi.Atk;
|
|||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const Lightbox = imports.ui.lightbox;
|
const Lightbox = imports.ui.lightbox;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||||
const FADE_IN_BUTTONS_TIME = 0.33;
|
|
||||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||||
|
|
||||||
|
const WORK_SPINNER_ICON_SIZE = 24;
|
||||||
|
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
|
||||||
|
const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
||||||
|
|
||||||
const State = {
|
const State = {
|
||||||
OPENED: 0,
|
OPENED: 0,
|
||||||
CLOSED: 1,
|
CLOSED: 1,
|
||||||
@ -35,17 +40,24 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(params) {
|
_init: function(params) {
|
||||||
params = Params.parse(params, { shellReactive: false,
|
params = Params.parse(params, { shellReactive: false,
|
||||||
styleClass: null });
|
styleClass: null,
|
||||||
|
parentActor: Main.uiGroup,
|
||||||
|
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
|
||||||
|
shouldFadeIn: true,
|
||||||
|
destroyOnClose: true });
|
||||||
|
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this._hasModal = false;
|
this._hasModal = false;
|
||||||
|
this._keybindingMode = params.keybindingMode;
|
||||||
this._shellReactive = params.shellReactive;
|
this._shellReactive = params.shellReactive;
|
||||||
|
this._shouldFadeIn = params.shouldFadeIn;
|
||||||
|
this._destroyOnClose = params.destroyOnClose;
|
||||||
|
|
||||||
this._group = new St.Widget({ visible: false,
|
this._group = new St.Widget({ visible: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
accessible_role: Atk.Role.DIALOG });
|
accessible_role: Atk.Role.DIALOG });
|
||||||
Main.uiGroup.add_actor(this._group);
|
params.parentActor.add_actor(this._group);
|
||||||
|
|
||||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||||
coordinate: Clutter.BindCoordinate.ALL });
|
coordinate: Clutter.BindCoordinate.ALL });
|
||||||
@ -53,16 +65,22 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||||
|
|
||||||
this._actionKeys = {};
|
this._pressedKey = null;
|
||||||
|
this._buttonKeys = {};
|
||||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
|
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||||
|
|
||||||
this._backgroundBin = new St.Bin();
|
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||||
|
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
|
this._monitorConstraint = new Layout.MonitorConstraint();
|
||||||
|
this._backgroundBin.add_constraint(this._monitorConstraint);
|
||||||
this._group.add_actor(this._backgroundBin);
|
this._group.add_actor(this._backgroundBin);
|
||||||
|
|
||||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
if (params.styleClass != null) {
|
if (params.styleClass != null) {
|
||||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._shellReactive) {
|
if (!this._shellReactive) {
|
||||||
@ -70,60 +88,48 @@ const ModalDialog = new Lang.Class({
|
|||||||
{ inhibitEvents: true });
|
{ inhibitEvents: true });
|
||||||
this._lightbox.highlight(this._backgroundBin);
|
this._lightbox.highlight(this._backgroundBin);
|
||||||
|
|
||||||
let stack = new Shell.Stack();
|
this._eventBlocker = new Clutter.Actor({ reactive: true });
|
||||||
this._backgroundBin.child = stack;
|
this.backgroundStack.add_actor(this._eventBlocker);
|
||||||
|
|
||||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
|
||||||
stack.add_actor(this._eventBlocker);
|
|
||||||
stack.add_actor(this._dialogLayout);
|
|
||||||
} else {
|
|
||||||
this._backgroundBin.child = this._dialogLayout;
|
|
||||||
}
|
}
|
||||||
|
this.backgroundStack.add_actor(this.dialogLayout);
|
||||||
|
|
||||||
|
|
||||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||||
this._dialogLayout.add(this.contentLayout,
|
this.dialogLayout.add(this.contentLayout,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: true,
|
y_fill: true,
|
||||||
x_align: St.Align.MIDDLE,
|
x_align: St.Align.MIDDLE,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||||
visible: false,
|
vertical: false });
|
||||||
vertical: false });
|
this.dialogLayout.add(this.buttonLayout,
|
||||||
this._dialogLayout.add(this._buttonLayout,
|
{ expand: true,
|
||||||
{ expand: true,
|
x_align: St.Align.MIDDLE,
|
||||||
x_align: St.Align.MIDDLE,
|
y_align: St.Align.END });
|
||||||
y_align: St.Align.END });
|
|
||||||
|
|
||||||
global.focus_manager.add_group(this._dialogLayout);
|
global.focus_manager.add_group(this.dialogLayout);
|
||||||
this._initialKeyFocus = this._dialogLayout;
|
this._initialKeyFocus = this.dialogLayout;
|
||||||
this._initialKeyFocusDestroyId = 0;
|
this._initialKeyFocusDestroyId = 0;
|
||||||
this._savedKeyFocus = null;
|
this._savedKeyFocus = null;
|
||||||
|
|
||||||
|
this._workSpinner = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this._group.destroy();
|
this._group.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearButtons: function() {
|
||||||
|
this.buttonLayout.destroy_all_children();
|
||||||
|
this._buttonKeys = {};
|
||||||
|
},
|
||||||
|
|
||||||
setButtons: function(buttons) {
|
setButtons: function(buttons) {
|
||||||
let hadChildren = this._buttonLayout.get_children() > 0;
|
this.clearButtons();
|
||||||
|
|
||||||
this._buttonLayout.destroy_all_children();
|
|
||||||
this._actionKeys = {};
|
|
||||||
|
|
||||||
this._buttonLayout.visible = (buttons.length > 0);
|
|
||||||
|
|
||||||
for (let i = 0; i < buttons.length; i++) {
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
let buttonInfo = buttons[i];
|
let buttonInfo = buttons[i];
|
||||||
let label = buttonInfo['label'];
|
|
||||||
let action = buttonInfo['action'];
|
|
||||||
let key = buttonInfo['key'];
|
|
||||||
|
|
||||||
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
|
||||||
reactive: true,
|
|
||||||
can_focus: true,
|
|
||||||
label: label });
|
|
||||||
|
|
||||||
let x_alignment;
|
let x_alignment;
|
||||||
if (buttons.length == 1)
|
if (buttons.length == 1)
|
||||||
@ -135,66 +141,135 @@ const ModalDialog = new Lang.Class({
|
|||||||
else
|
else
|
||||||
x_alignment = St.Align.MIDDLE;
|
x_alignment = St.Align.MIDDLE;
|
||||||
|
|
||||||
if (!this._initialKeyFocusDestroyId)
|
this.addButton(buttonInfo, { expand: true,
|
||||||
this._initialKeyFocus = buttonInfo.button;
|
x_fill: false,
|
||||||
this._buttonLayout.add(buttonInfo.button,
|
y_fill: false,
|
||||||
{ expand: true,
|
x_align: x_alignment,
|
||||||
x_fill: false,
|
y_align: St.Align.MIDDLE });
|
||||||
y_fill: false,
|
|
||||||
x_align: x_alignment,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
buttonInfo.button.connect('clicked', action);
|
|
||||||
|
|
||||||
if (key)
|
|
||||||
this._actionKeys[key] = action;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade in buttons if there weren't any before
|
|
||||||
if (!hadChildren && buttons.length > 0) {
|
|
||||||
this._buttonLayout.opacity = 0;
|
|
||||||
Tweener.addTween(this._buttonLayout,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: FADE_IN_BUTTONS_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
onComplete: Lang.bind(this, function() {
|
|
||||||
this.emit('buttons-set');
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.emit('buttons-set');
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
addButton: function(buttonInfo, layoutInfo) {
|
||||||
let symbol = keyPressEvent.get_key_symbol();
|
let label = buttonInfo['label'];
|
||||||
let action = this._actionKeys[symbol];
|
let action = buttonInfo['action'];
|
||||||
|
let key = buttonInfo['key'];
|
||||||
|
let isDefault = buttonInfo['default'];
|
||||||
|
|
||||||
if (action)
|
let keys;
|
||||||
|
|
||||||
|
if (key)
|
||||||
|
keys = [key];
|
||||||
|
else if (isDefault)
|
||||||
|
keys = [Clutter.KEY_Return, Clutter.KEY_KP_Enter, Clutter.KEY_ISO_Enter];
|
||||||
|
else
|
||||||
|
keys = [];
|
||||||
|
|
||||||
|
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
label: label });
|
||||||
|
button.connect('clicked', action);
|
||||||
|
|
||||||
|
buttonInfo['button'] = button;
|
||||||
|
|
||||||
|
if (isDefault)
|
||||||
|
button.add_style_pseudo_class('default');
|
||||||
|
|
||||||
|
if (!this._initialKeyFocusDestroyId)
|
||||||
|
this._initialKeyFocus = button;
|
||||||
|
|
||||||
|
for (let i in keys)
|
||||||
|
this._buttonKeys[keys[i]] = buttonInfo;
|
||||||
|
|
||||||
|
this.buttonLayout.add(button, layoutInfo);
|
||||||
|
|
||||||
|
return button;
|
||||||
|
},
|
||||||
|
|
||||||
|
placeSpinner: function(layoutInfo) {
|
||||||
|
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||||
|
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||||
|
this._workSpinner.actor.opacity = 0;
|
||||||
|
this._workSpinner.actor.show();
|
||||||
|
|
||||||
|
this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
|
||||||
|
},
|
||||||
|
|
||||||
|
setWorking: function(working) {
|
||||||
|
if (!this._workSpinner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tweener.removeTweens(this._workSpinner.actor);
|
||||||
|
if (working) {
|
||||||
|
this._workSpinner.play();
|
||||||
|
Tweener.addTween(this._workSpinner.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
delay: WORK_SPINNER_ANIMATION_DELAY,
|
||||||
|
time: WORK_SPINNER_ANIMATION_TIME,
|
||||||
|
transition: 'linear'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Tweener.addTween(this._workSpinner.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: WORK_SPINNER_ANIMATION_TIME,
|
||||||
|
transition: 'linear',
|
||||||
|
onCompleteScope: this,
|
||||||
|
onComplete: function() {
|
||||||
|
if (this._workSpinner)
|
||||||
|
this._workSpinner.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyPressEvent: function(object, event) {
|
||||||
|
this._pressedKey = event.get_key_symbol();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyReleaseEvent: function(object, event) {
|
||||||
|
let pressedKey = this._pressedKey;
|
||||||
|
this._pressedKey = null;
|
||||||
|
|
||||||
|
let symbol = event.get_key_symbol();
|
||||||
|
if (symbol != pressedKey)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let buttonInfo = this._buttonKeys[symbol];
|
||||||
|
if (!buttonInfo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let button = buttonInfo['button'];
|
||||||
|
let action = buttonInfo['action'];
|
||||||
|
|
||||||
|
if (action && button.reactive) {
|
||||||
action();
|
action();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onGroupDestroy: function() {
|
_onGroupDestroy: function() {
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeOpen: function() {
|
_fadeOpen: function(onPrimary) {
|
||||||
let monitor = Main.layoutManager.currentMonitor;
|
if (onPrimary)
|
||||||
|
this._monitorConstraint.primary = true;
|
||||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
else
|
||||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
this._monitorConstraint.index = global.screen.get_current_monitor();
|
||||||
|
|
||||||
this.state = State.OPENING;
|
this.state = State.OPENING;
|
||||||
|
|
||||||
this._dialogLayout.opacity = 255;
|
this.dialogLayout.opacity = 255;
|
||||||
if (this._lightbox)
|
if (this._lightbox)
|
||||||
this._lightbox.show();
|
this._lightbox.show();
|
||||||
this._group.opacity = 0;
|
this._group.opacity = 0;
|
||||||
this._group.show();
|
this._group.show();
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
time: OPEN_AND_CLOSE_TIME,
|
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this,
|
onComplete: Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
@ -211,19 +286,19 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._initialKeyFocus = actor;
|
this._initialKeyFocus = actor;
|
||||||
|
|
||||||
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
|
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
|
||||||
this._initialKeyFocus = this._dialogLayout;
|
this._initialKeyFocus = this.dialogLayout;
|
||||||
this._initialKeyFocusDestroyId = 0;
|
this._initialKeyFocusDestroyId = 0;
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(timestamp) {
|
open: function(timestamp, onPrimary) {
|
||||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!this.pushModal(timestamp))
|
if (!this.pushModal({ timestamp: timestamp }))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._fadeOpen();
|
this._fadeOpen(onPrimary);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -243,6 +318,10 @@ const ModalDialog = new Lang.Class({
|
|||||||
function() {
|
function() {
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this._group.hide();
|
this._group.hide();
|
||||||
|
this.emit('closed');
|
||||||
|
|
||||||
|
if (this._destroyOnClose)
|
||||||
|
this.destroy();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -270,7 +349,8 @@ const ModalDialog = new Lang.Class({
|
|||||||
pushModal: function (timestamp) {
|
pushModal: function (timestamp) {
|
||||||
if (this._hasModal)
|
if (this._hasModal)
|
||||||
return true;
|
return true;
|
||||||
if (!Main.pushModal(this._group, timestamp))
|
if (!Main.pushModal(this._group, { timestamp: timestamp,
|
||||||
|
keybindingMode: this._keybindingMode }))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._hasModal = true;
|
this._hasModal = true;
|
||||||
@ -304,7 +384,7 @@ const ModalDialog = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.popModal(timestamp);
|
this.popModal(timestamp);
|
||||||
Tweener.addTween(this._dialogLayout,
|
Tweener.addTween(this.dialogLayout,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: FADE_OUT_DIALOG_TIME,
|
time: FADE_OUT_DIALOG_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
@ -87,6 +88,141 @@ const rewriteRules = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||||
|
'bluetooth-applet': 'bluetooth',
|
||||||
|
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
|
||||||
|
// when moved to control center
|
||||||
|
'gnome-sound-applet': 'volume',
|
||||||
|
'nm-applet': 'network',
|
||||||
|
'gnome-power-manager': 'battery',
|
||||||
|
'keyboard': 'keyboard',
|
||||||
|
'a11y-keyboard': 'a11y',
|
||||||
|
'kbd-scrolllock': 'keyboard',
|
||||||
|
'kbd-numlock': 'keyboard',
|
||||||
|
'kbd-capslock': 'keyboard',
|
||||||
|
'ibus-ui-gtk': 'keyboard'
|
||||||
|
};
|
||||||
|
|
||||||
|
const NotificationGenericPolicy = new Lang.Class({
|
||||||
|
Name: 'NotificationGenericPolicy',
|
||||||
|
Extends: MessageTray.NotificationPolicy,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
// Don't chain to parent, it would try setting
|
||||||
|
// our properties to the defaults
|
||||||
|
|
||||||
|
this.id = 'generic';
|
||||||
|
|
||||||
|
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||||
|
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||||
|
},
|
||||||
|
|
||||||
|
store: function() { },
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._masterSettings.run_dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
_changed: function(settings, key) {
|
||||||
|
this.emit('policy-changed', key);
|
||||||
|
},
|
||||||
|
|
||||||
|
get enable() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
get enableSound() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
get showBanners() {
|
||||||
|
return this._masterSettings.get_boolean('show-banners');
|
||||||
|
},
|
||||||
|
|
||||||
|
get forceExpanded() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get showInLockScreen() {
|
||||||
|
return this._masterSettings.get_boolean('show-in-lock-screen');
|
||||||
|
},
|
||||||
|
|
||||||
|
get detailsInLockScreen() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const NotificationApplicationPolicy = new Lang.Class({
|
||||||
|
Name: 'NotificationApplicationPolicy',
|
||||||
|
Extends: MessageTray.NotificationPolicy,
|
||||||
|
|
||||||
|
_init: function(id) {
|
||||||
|
// Don't chain to parent, it would try setting
|
||||||
|
// our properties to the defaults
|
||||||
|
|
||||||
|
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',
|
||||||
|
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
|
||||||
|
|
||||||
|
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||||
|
this._settings.connect('changed', Lang.bind(this, this._changed));
|
||||||
|
},
|
||||||
|
|
||||||
|
store: function() {
|
||||||
|
this._settings.set_string('application-id', this.id + '.desktop');
|
||||||
|
|
||||||
|
let apps = this._masterSettings.get_strv('application-children');
|
||||||
|
if (apps.indexOf(this._canonicalId) < 0) {
|
||||||
|
apps.push(this._canonicalId);
|
||||||
|
this._masterSettings.set_strv('application-children', apps);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._masterSettings.run_dispose();
|
||||||
|
this._settings.run_dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
_changed: function(settings, key) {
|
||||||
|
this.emit('policy-changed', key);
|
||||||
|
},
|
||||||
|
|
||||||
|
_canonicalizeId: function(id) {
|
||||||
|
// Keys are restricted to lowercase alphanumeric characters and dash,
|
||||||
|
// and two dashes cannot be in succession
|
||||||
|
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
|
||||||
|
},
|
||||||
|
|
||||||
|
get enable() {
|
||||||
|
return this._settings.get_boolean('enable');
|
||||||
|
},
|
||||||
|
|
||||||
|
get enableSound() {
|
||||||
|
return this._settings.get_boolean('enable-sound-alerts');
|
||||||
|
},
|
||||||
|
|
||||||
|
get showBanners() {
|
||||||
|
return this._masterSettings.get_boolean('show-banners') &&
|
||||||
|
this._settings.get_boolean('show-banners');
|
||||||
|
},
|
||||||
|
|
||||||
|
get forceExpanded() {
|
||||||
|
return this._settings.get_boolean('force-expanded');
|
||||||
|
},
|
||||||
|
|
||||||
|
get showInLockScreen() {
|
||||||
|
return this._masterSettings.get_boolean('show-in-lock-screen') &&
|
||||||
|
this._settings.get_boolean('show-in-lock-screen');
|
||||||
|
},
|
||||||
|
|
||||||
|
get detailsInLockScreen() {
|
||||||
|
return this._settings.get_boolean('details-in-lock-screen');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const NotificationDaemon = new Lang.Class({
|
const NotificationDaemon = new Lang.Class({
|
||||||
Name: 'NotificationDaemon',
|
Name: 'NotificationDaemon',
|
||||||
|
|
||||||
@ -99,57 +235,54 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
this._notifications = {};
|
this._notifications = {};
|
||||||
this._busProxy = new Bus();
|
this._busProxy = new Bus();
|
||||||
|
|
||||||
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
this._trayManager = new Shell.TrayManager();
|
||||||
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
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',
|
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
Main.overview.connect('hidden',
|
Main.overview.connect('hidden',
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
|
|
||||||
|
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_iconForNotificationData: function(icon, hints, size) {
|
_imageForNotificationData: function(hints) {
|
||||||
let textureCache = St.TextureCache.get_default();
|
if (hints['image-data']) {
|
||||||
|
|
||||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
|
||||||
// and don't show a large image. There are currently many applications that use
|
|
||||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
|
||||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
|
||||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
|
||||||
// So the logic here does the right thing for this case. If both an icon and either
|
|
||||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
|
||||||
// a large image.
|
|
||||||
if (icon) {
|
|
||||||
if (icon.substr(0, 7) == 'file://')
|
|
||||||
return textureCache.load_uri_async(icon, size, size);
|
|
||||||
else if (icon[0] == '/') {
|
|
||||||
let uri = GLib.filename_to_uri(icon, null);
|
|
||||||
return textureCache.load_uri_async(uri, size, size);
|
|
||||||
} else
|
|
||||||
return new St.Icon({ icon_name: icon,
|
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: size });
|
|
||||||
} else if (hints['image-data']) {
|
|
||||||
let [width, height, rowStride, hasAlpha,
|
let [width, height, rowStride, hasAlpha,
|
||||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||||
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
|
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||||
|
bitsPerSample, width, height, rowStride);
|
||||||
} else if (hints['image-path']) {
|
} else if (hints['image-path']) {
|
||||||
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||||
} else {
|
|
||||||
let stockIcon;
|
|
||||||
switch (hints.urgency) {
|
|
||||||
case Urgency.LOW:
|
|
||||||
case Urgency.NORMAL:
|
|
||||||
stockIcon = 'gtk-dialog-info';
|
|
||||||
break;
|
|
||||||
case Urgency.CRITICAL:
|
|
||||||
stockIcon = 'gtk-dialog-error';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return new St.Icon({ icon_name: stockIcon,
|
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: size });
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_fallbackIconForNotificationData: function(hints) {
|
||||||
|
let stockIcon;
|
||||||
|
switch (hints.urgency) {
|
||||||
|
case Urgency.LOW:
|
||||||
|
case Urgency.NORMAL:
|
||||||
|
stockIcon = 'gtk-dialog-info';
|
||||||
|
break;
|
||||||
|
case Urgency.CRITICAL:
|
||||||
|
stockIcon = 'gtk-dialog-error';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new Gio.ThemedIcon({ name: stockIcon });
|
||||||
|
},
|
||||||
|
|
||||||
|
_iconForNotificationData: function(icon) {
|
||||||
|
if (icon) {
|
||||||
|
if (icon.substr(0, 7) == 'file://')
|
||||||
|
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||||
|
else if (icon[0] == '/')
|
||||||
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||||
|
else
|
||||||
|
return new Gio.ThemedIcon({ name: icon });
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_lookupSource: function(title, pid, trayIcon) {
|
_lookupSource: function(title, pid, trayIcon) {
|
||||||
@ -200,7 +333,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = new Source(title, pid, sender, trayIcon);
|
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||||
source.setTransient(isForTransientNotification);
|
source.setTransient(isForTransientNotification);
|
||||||
|
|
||||||
if (!isForTransientNotification) {
|
if (!isForTransientNotification) {
|
||||||
@ -243,6 +376,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
Mainloop.idle_add(Lang.bind(this,
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
function () {
|
function () {
|
||||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||||
|
return false;
|
||||||
}));
|
}));
|
||||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||||
}
|
}
|
||||||
@ -341,12 +475,8 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||||
ndata.actions, ndata.hints, ndata.notification];
|
ndata.actions, ndata.hints, ndata.notification];
|
||||||
|
|
||||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
|
||||||
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
notification = new MessageTray.Notification(source, summary, body,
|
notification = new MessageTray.Notification(source);
|
||||||
{ icon: iconActor,
|
|
||||||
bannerMarkup: true });
|
|
||||||
ndata.notification = notification;
|
ndata.notification = notification;
|
||||||
notification.connect('destroy', Lang.bind(this,
|
notification.connect('destroy', Lang.bind(this,
|
||||||
function(n, reason) {
|
function(n, reason) {
|
||||||
@ -369,29 +499,38 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
function(n, actionId) {
|
function(n, actionId) {
|
||||||
this._emitActionInvoked(ndata.id, actionId);
|
this._emitActionInvoked(ndata.id, actionId);
|
||||||
}));
|
}));
|
||||||
} else {
|
|
||||||
notification.update(summary, body, { icon: iconActor,
|
|
||||||
bannerMarkup: true,
|
|
||||||
clear: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only display a large image if an icon is also specified.
|
// Mark music notifications so they can be shown in the screen shield
|
||||||
if (icon && (hints['image-data'] || hints['image-path'])) {
|
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
|
||||||
let image = null;
|
|
||||||
if (hints['image-data']) {
|
let gicon = this._iconForNotificationData(icon, hints);
|
||||||
let [width, height, rowStride, hasAlpha,
|
let gimage = this._imageForNotificationData(hints);
|
||||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
|
||||||
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
|
let image = null;
|
||||||
width, height, rowStride, notification.IMAGE_SIZE);
|
|
||||||
} else if (hints['image-path']) {
|
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||||
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
|
// and don't show a large image. There are currently many applications that use
|
||||||
notification.IMAGE_SIZE,
|
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||||
notification.IMAGE_SIZE);
|
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||||
}
|
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||||
notification.setImage(image);
|
// So the logic here does the right thing for this case. If both an icon and either
|
||||||
} else {
|
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||||
notification.unsetImage();
|
// a large image.
|
||||||
}
|
if (gicon && gimage)
|
||||||
|
image = new St.Icon({ gicon: gimage,
|
||||||
|
icon_size: notification.IMAGE_SIZE });
|
||||||
|
else if (!gicon && gimage)
|
||||||
|
gicon = gimage;
|
||||||
|
else if (!gicon)
|
||||||
|
gicon = this._fallbackIconForNotificationData(hints);
|
||||||
|
|
||||||
|
notification.update(summary, body, { gicon: gicon,
|
||||||
|
bannerMarkup: true,
|
||||||
|
clear: true,
|
||||||
|
soundFile: hints['sound-file'],
|
||||||
|
soundName: hints['sound-name'] });
|
||||||
|
notification.setImage(image);
|
||||||
|
|
||||||
if (actions.length) {
|
if (actions.length) {
|
||||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
notification.setUseActionIcons(hints['action-icons'] == true);
|
||||||
@ -421,8 +560,8 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||||
notification.setTransient(hints['transient'] == true);
|
notification.setTransient(hints['transient'] == true);
|
||||||
|
|
||||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
let sourceGIcon = source.useNotificationIcon ? gicon : null;
|
||||||
source.processNotification(notification, sourceIconActor);
|
source.processNotification(notification, sourceGIcon);
|
||||||
},
|
},
|
||||||
|
|
||||||
CloseNotification: function(id) {
|
CloseNotification: function(id) {
|
||||||
@ -445,7 +584,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
// 'icon-multi',
|
// 'icon-multi',
|
||||||
'icon-static',
|
'icon-static',
|
||||||
'persistence',
|
'persistence',
|
||||||
// 'sound',
|
'sound',
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -483,6 +622,10 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon) {
|
_onTrayIconAdded: function(o, icon) {
|
||||||
|
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||||
|
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
|
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -497,12 +640,22 @@ const Source = new Lang.Class({
|
|||||||
Name: 'NotificationDaemonSource',
|
Name: 'NotificationDaemonSource',
|
||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(title, pid, sender, trayIcon) {
|
_init: function(title, pid, sender, trayIcon, appId) {
|
||||||
|
// Need to set the app before chaining up, so
|
||||||
|
// methods called from the parent constructor can find it
|
||||||
|
this.trayIcon = trayIcon;
|
||||||
|
this.pid = pid;
|
||||||
|
this.app = this._getApp(appId);
|
||||||
|
|
||||||
this.parent(title);
|
this.parent(title);
|
||||||
|
|
||||||
this.initialTitle = title;
|
this.initialTitle = title;
|
||||||
|
|
||||||
this.pid = pid;
|
if (this.app)
|
||||||
|
this.title = this.app.get_name();
|
||||||
|
else
|
||||||
|
this.useNotificationIcon = true;
|
||||||
|
|
||||||
if (sender)
|
if (sender)
|
||||||
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
||||||
Gio.BusNameWatcherFlags.NONE,
|
Gio.BusNameWatcherFlags.NONE,
|
||||||
@ -511,16 +664,19 @@ const Source = new Lang.Class({
|
|||||||
else
|
else
|
||||||
this._nameWatcherId = 0;
|
this._nameWatcherId = 0;
|
||||||
|
|
||||||
this._setApp();
|
|
||||||
if (this.app)
|
|
||||||
this.title = this.app.get_name();
|
|
||||||
else
|
|
||||||
this.useNotificationIcon = true;
|
|
||||||
|
|
||||||
this.trayIcon = trayIcon;
|
|
||||||
if (this.trayIcon) {
|
if (this.trayIcon) {
|
||||||
this._setSummaryIcon(this.trayIcon);
|
// Try again finding the app, using the WM_CLASS from the tray icon
|
||||||
this.useNotificationIcon = false;
|
this._setSummaryIcon(this.trayIcon);
|
||||||
|
this.useNotificationIcon = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
if (this.app) {
|
||||||
|
let id = this.app.get_id().replace(/\.desktop$/,'');
|
||||||
|
return new NotificationApplicationPolicy(id);
|
||||||
|
} else {
|
||||||
|
return new NotificationGenericPolicy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -534,11 +690,11 @@ const Source = new Lang.Class({
|
|||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
processNotification: function(notification, icon) {
|
processNotification: function(notification, gicon) {
|
||||||
if (!this.app)
|
if (gicon)
|
||||||
this._setApp();
|
this._gicon = gicon;
|
||||||
if (!this.app && icon)
|
if (!this.trayIcon)
|
||||||
this._setSummaryIcon(icon);
|
this.iconUpdated();
|
||||||
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
if (notification.resident && this.app && tracker.focus_app == this.app)
|
if (notification.resident && this.app && tracker.focus_app == this.app)
|
||||||
@ -547,38 +703,30 @@ const Source = new Lang.Class({
|
|||||||
this.notify(notification);
|
this.notify(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSummaryClick: function() {
|
handleSummaryClick: function(button) {
|
||||||
if (!this.trayIcon)
|
if (!this.trayIcon)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let event = Clutter.get_current_event();
|
let event = Clutter.get_current_event();
|
||||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Left clicks are passed through only where there aren't unacknowledged
|
// Left clicks are passed through only where there aren't unacknowledged
|
||||||
// notifications, so it possible to open them in summary mode; right
|
// notifications, so it possible to open them in summary mode; right
|
||||||
// clicks are always forwarded, as the right click menu is not useful for
|
// clicks are always forwarded, as the right click menu is not useful for
|
||||||
// tray icons
|
// tray icons
|
||||||
if (event.get_button() == 1 &&
|
if (button == 1 &&
|
||||||
this.notifications.length > 0)
|
this.notifications.length > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Main.overview.visible) {
|
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
|
||||||
// We can't just connect to Main.overview's 'hidden' signal,
|
global.disconnect(id);
|
||||||
// because it's emitted *before* it calls popModal()...
|
|
||||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
|
|
||||||
function () {
|
|
||||||
global.disconnect(id);
|
|
||||||
this.trayIcon.click(event);
|
|
||||||
}));
|
|
||||||
Main.overview.hide();
|
|
||||||
} else {
|
|
||||||
this.trayIcon.click(event);
|
this.trayIcon.click(event);
|
||||||
}
|
}));
|
||||||
|
|
||||||
|
Main.overview.hide();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getApp: function() {
|
_getApp: function(appId) {
|
||||||
let app;
|
let app;
|
||||||
|
|
||||||
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||||
@ -586,7 +734,13 @@ const Source = new Lang.Class({
|
|||||||
return app;
|
return app;
|
||||||
|
|
||||||
if (this.trayIcon) {
|
if (this.trayIcon) {
|
||||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
|
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
|
||||||
|
if (app != null)
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appId) {
|
||||||
|
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
|
||||||
if (app != null)
|
if (app != null)
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@ -594,11 +748,11 @@ const Source = new Lang.Class({
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_setApp: function() {
|
_setApp: function(appId) {
|
||||||
if (this.app)
|
if (this.app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.app = this._getApp();
|
this.app = this._getApp(appId);
|
||||||
if (!this.app)
|
if (!this.app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -606,10 +760,20 @@ const Source = new Lang.Class({
|
|||||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||||
if (!this.trayIcon) {
|
if (!this.trayIcon) {
|
||||||
this.useNotificationIcon = false;
|
this.useNotificationIcon = false;
|
||||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
this.iconUpdated();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setTitle: function(title) {
|
||||||
|
// Do nothing if .app is set, we don't want to override the
|
||||||
|
// app name with whatever is provided through libnotify (usually
|
||||||
|
// garbage)
|
||||||
|
if (this.app)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.parent(title);
|
||||||
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function(notification) {
|
||||||
this.destroyNonResidentNotifications();
|
this.destroyNonResidentNotifications();
|
||||||
this.openApp();
|
this.openApp();
|
||||||
@ -640,8 +804,18 @@ const Source = new Lang.Class({
|
|||||||
this.parent();
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
createIcon: function(size) {
|
||||||
// We set the summary icon ourselves.
|
if (this.trayIcon) {
|
||||||
return null;
|
return new Clutter.Clone({ width: size,
|
||||||
|
height: size,
|
||||||
|
source: this.trayIcon });
|
||||||
|
} else if (this.app) {
|
||||||
|
return this.app.create_icon_texture(size);
|
||||||
|
} else if (this._gicon) {
|
||||||
|
return new St.Icon({ gicon: this._gicon,
|
||||||
|
icon_size: size });
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
210
js/ui/osdWindow.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
|
||||||
|
const HIDE_TIMEOUT = 1500;
|
||||||
|
const FADE_TIME = 0.1;
|
||||||
|
const LEVEL_ANIMATION_TIME = 0.1;
|
||||||
|
|
||||||
|
const LevelBar = new Lang.Class({
|
||||||
|
Name: 'LevelBar',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._level = 0;
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ style_class: 'level',
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
|
this._bar = new St.DrawingArea();
|
||||||
|
this._bar.connect('repaint', Lang.bind(this, this._repaint));
|
||||||
|
|
||||||
|
this.actor.set_child(this._bar);
|
||||||
|
},
|
||||||
|
|
||||||
|
get level() {
|
||||||
|
return this._level;
|
||||||
|
},
|
||||||
|
|
||||||
|
set level(value) {
|
||||||
|
let newValue = Math.max(0, Math.min(value, 100));
|
||||||
|
if (newValue == this._level)
|
||||||
|
return;
|
||||||
|
this._level = newValue;
|
||||||
|
this._bar.queue_repaint();
|
||||||
|
},
|
||||||
|
|
||||||
|
_repaint: function() {
|
||||||
|
let cr = this._bar.get_context();
|
||||||
|
|
||||||
|
let node = this.actor.get_theme_node();
|
||||||
|
let radius = node.get_border_radius(0); // assume same radius for all corners
|
||||||
|
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
||||||
|
|
||||||
|
let [w, h] = this._bar.get_surface_size();
|
||||||
|
w *= (this._level / 100.);
|
||||||
|
|
||||||
|
if (w == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cr.moveTo(radius, 0);
|
||||||
|
if (w >= radius)
|
||||||
|
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
|
||||||
|
else
|
||||||
|
cr.lineTo(w, 0);
|
||||||
|
if (w >= radius)
|
||||||
|
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
|
||||||
|
else
|
||||||
|
cr.lineTo(w, h);
|
||||||
|
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
|
||||||
|
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
|
||||||
|
cr.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const OsdWindow = new Lang.Class({
|
||||||
|
Name: 'OsdWindow',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._popupSize = 0;
|
||||||
|
this.actor = new St.Widget({ x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
|
y_align: Clutter.ActorAlign.CENTER });
|
||||||
|
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||||
|
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
||||||
|
vertical: true });
|
||||||
|
this.actor.add_actor(this._box);
|
||||||
|
|
||||||
|
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
|
this._box.connect('notify::height', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._box.width = this._box.height;
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._icon = new St.Icon();
|
||||||
|
this._box.add(this._icon, { expand: true });
|
||||||
|
|
||||||
|
this._label = new St.Label();
|
||||||
|
this._box.add(this._label);
|
||||||
|
|
||||||
|
this._level = new LevelBar();
|
||||||
|
this._box.add(this._level.actor);
|
||||||
|
|
||||||
|
this._hideTimeoutId = 0;
|
||||||
|
this._reset();
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed',
|
||||||
|
Lang.bind(this, this._monitorsChanged));
|
||||||
|
this._monitorsChanged();
|
||||||
|
|
||||||
|
Main.uiGroup.add_child(this.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
setIcon: function(icon) {
|
||||||
|
this._icon.gicon = icon;
|
||||||
|
},
|
||||||
|
|
||||||
|
setLabel: function(label) {
|
||||||
|
this._label.visible = (label != undefined);
|
||||||
|
if (label)
|
||||||
|
this._label.text = label;
|
||||||
|
},
|
||||||
|
|
||||||
|
setLevel: function(level) {
|
||||||
|
this._level.actor.visible = (level != undefined);
|
||||||
|
if (this.actor.visible)
|
||||||
|
Tweener.addTween(this._level,
|
||||||
|
{ level: level,
|
||||||
|
time: LEVEL_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
else
|
||||||
|
this._level.level = level;
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
if (!this._icon.gicon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.actor.visible) {
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
|
this.actor.show();
|
||||||
|
this.actor.opacity = 0;
|
||||||
|
this.actor.get_parent().set_child_above_sibling(this.actor, null);
|
||||||
|
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: FADE_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._hideTimeoutId)
|
||||||
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
|
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
|
||||||
|
Lang.bind(this, this._hide));
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
if (!this._hideTimeoutId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
|
this._hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_hide: function() {
|
||||||
|
this._hideTimeoutId = 0;
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: FADE_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this._reset();
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_reset: function() {
|
||||||
|
this.actor.hide();
|
||||||
|
this.setLabel(null);
|
||||||
|
this.setLevel(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_monitorsChanged: function() {
|
||||||
|
/* assume 110x110 on a 640x480 display and scale from there */
|
||||||
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
|
let scalew = monitor.width / 640.0;
|
||||||
|
let scaleh = monitor.height / 480.0;
|
||||||
|
let scale = Math.min(scalew, scaleh);
|
||||||
|
this._popupSize = 110 * Math.max(1, scale);
|
||||||
|
|
||||||
|
this._box.translation_y = monitor.height / 4;
|
||||||
|
this._icon.icon_size = this._popupSize / 2;
|
||||||
|
this._box.style_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStyleChanged: function() {
|
||||||
|
let themeNode = this._box.get_theme_node();
|
||||||
|
let horizontalPadding = themeNode.get_horizontal_padding();
|
||||||
|
let verticalPadding = themeNode.get_vertical_padding();
|
||||||
|
let topBorder = themeNode.get_border_width(St.Side.TOP);
|
||||||
|
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
|
||||||
|
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
|
||||||
|
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
|
||||||
|
|
||||||
|
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
|
||||||
|
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
|
||||||
|
|
||||||
|
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
|
||||||
|
}
|
||||||
|
});
|
@ -10,40 +10,28 @@ const St = imports.gi.St;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
|
|
||||||
const AppDisplay = imports.ui.appDisplay;
|
const Background = imports.ui.background;
|
||||||
const Dash = imports.ui.dash;
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
|
const LayoutManager = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const OverviewControls = imports.ui.overviewControls;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const PlaceDisplay = imports.ui.placeDisplay;
|
|
||||||
const RemoteSearch = imports.ui.remoteSearch;
|
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const ViewSelector = imports.ui.viewSelector;
|
|
||||||
const Wanda = imports.ui.wanda;
|
|
||||||
const WorkspacesView = imports.ui.workspacesView;
|
|
||||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||||
|
|
||||||
// Time for initial animation going into Overview mode
|
// Time for initial animation going into Overview mode
|
||||||
const ANIMATION_TIME = 0.25;
|
const ANIMATION_TIME = 0.25;
|
||||||
|
|
||||||
// We split the screen vertically between the dash and the view selector.
|
// Must be less than ANIMATION_TIME, since we switch to
|
||||||
const DASH_SPLIT_FRACTION = 0.1;
|
// or from the overview completely after ANIMATION_TIME,
|
||||||
|
// and don't want the shading animation to get cut off
|
||||||
|
const SHADE_ANIMATION_TIME = .20;
|
||||||
|
|
||||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
const DND_WINDOW_SWITCH_TIMEOUT = 750;
|
||||||
|
|
||||||
const SwipeScrollDirection = {
|
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
|
||||||
NONE: 0,
|
|
||||||
HORIZONTAL: 1,
|
|
||||||
VERTICAL: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
const SwipeScrollResult = {
|
|
||||||
CANCEL: 0,
|
|
||||||
SWIPE: 1,
|
|
||||||
CLICK: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
const ShellInfo = new Lang.Class({
|
const ShellInfo = new Lang.Class({
|
||||||
Name: 'ShellInfo',
|
Name: 'ShellInfo',
|
||||||
@ -62,7 +50,14 @@ const ShellInfo = new Lang.Class({
|
|||||||
this._source.destroy();
|
this._source.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
setMessage: function(text, undoCallback, undoLabel) {
|
setMessage: function(text, options) {
|
||||||
|
options = Params.parse(options, { undoCallback: null,
|
||||||
|
forFeedback: false
|
||||||
|
});
|
||||||
|
|
||||||
|
let undoCallback = options.undoCallback;
|
||||||
|
let forFeedback = options.forFeedback;
|
||||||
|
|
||||||
if (this._source == null) {
|
if (this._source == null) {
|
||||||
this._source = new MessageTray.SystemNotificationSource();
|
this._source = new MessageTray.SystemNotificationSource();
|
||||||
this._source.connect('destroy', Lang.bind(this,
|
this._source.connect('destroy', Lang.bind(this,
|
||||||
@ -75,19 +70,17 @@ const ShellInfo = new Lang.Class({
|
|||||||
let notification = null;
|
let notification = null;
|
||||||
if (this._source.notifications.length == 0) {
|
if (this._source.notifications.length == 0) {
|
||||||
notification = new MessageTray.Notification(this._source, text, null);
|
notification = new MessageTray.Notification(this._source, text, null);
|
||||||
|
notification.setTransient(true);
|
||||||
|
notification.setForFeedback(forFeedback);
|
||||||
} else {
|
} else {
|
||||||
notification = this._source.notifications[0];
|
notification = this._source.notifications[0];
|
||||||
notification.update(text, null, { clear: true });
|
notification.update(text, null, { clear: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.setTransient(true);
|
|
||||||
|
|
||||||
this._undoCallback = undoCallback;
|
this._undoCallback = undoCallback;
|
||||||
if (undoCallback) {
|
if (undoCallback) {
|
||||||
notification.addButton('system-undo',
|
notification.addButton('system-undo', _("Undo"));
|
||||||
undoLabel ? undoLabel : _("Undo"));
|
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
|
||||||
notification.connect('action-invoked',
|
|
||||||
Lang.bind(this, this._onUndoClicked));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._source.notify(notification);
|
this._source.notify(notification);
|
||||||
@ -97,72 +90,69 @@ const ShellInfo = new Lang.Class({
|
|||||||
const Overview = new Lang.Class({
|
const Overview = new Lang.Class({
|
||||||
Name: 'Overview',
|
Name: 'Overview',
|
||||||
|
|
||||||
_init : function() {
|
_init: function() {
|
||||||
this.isDummy = !Main.sessionMode.hasOverview;
|
this._overviewCreated = false;
|
||||||
|
this._initCalled = false;
|
||||||
|
|
||||||
// We only have an overview in user sessions, so
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
// create a dummy overview in other cases
|
this._sessionUpdated();
|
||||||
if (this.isDummy) {
|
},
|
||||||
this.animationInProgress = false;
|
|
||||||
this.visible = false;
|
_createOverview: function() {
|
||||||
|
if (this._overviewCreated)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// The main BackgroundActor is inside global.window_group which is
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._overviewCreated = true;
|
||||||
|
|
||||||
|
// The main Background actors are inside global.window_group which are
|
||||||
// hidden when displaying the overview, so we create a new
|
// hidden when displaying the overview, so we create a new
|
||||||
// one. Instances of this class share a single CoglTexture behind the
|
// one. Instances of this class share a single CoglTexture behind the
|
||||||
// scenes which allows us to show the background with different
|
// scenes which allows us to show the background with different
|
||||||
// rendering options without duplicating the texture data.
|
// rendering options without duplicating the texture data.
|
||||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
this._background.hide();
|
|
||||||
global.overlay_group.add_actor(this._background);
|
|
||||||
|
|
||||||
this._desktopFade = new St.Bin();
|
this._desktopFade = new St.Bin();
|
||||||
global.overlay_group.add_actor(this._desktopFade);
|
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
|
||||||
|
|
||||||
this._spacing = 0;
|
let layout = new Clutter.BinLayout();
|
||||||
|
this._stack = new Clutter.Actor({ layout_manager: layout });
|
||||||
|
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||||
|
|
||||||
/* Translators: This is the main view to select
|
/* Translators: This is the main view to select
|
||||||
activities. See also note for "Activities" string. */
|
activities. See also note for "Activities" string. */
|
||||||
this._group = new St.Widget({ name: 'overview',
|
this._overview = new St.BoxLayout({ name: 'overview',
|
||||||
accessible_name: _("Overview"),
|
accessible_name: _("Overview"),
|
||||||
reactive: true });
|
reactive: true,
|
||||||
this._group._delegate = this;
|
vertical: true,
|
||||||
this._group.connect('style-changed',
|
x_expand: true,
|
||||||
Lang.bind(this, function() {
|
y_expand: true });
|
||||||
let node = this._group.get_theme_node();
|
this._overview._delegate = this;
|
||||||
let spacing = node.get_length('spacing');
|
|
||||||
if (spacing != this._spacing) {
|
|
||||||
this._spacing = spacing;
|
|
||||||
this._relayout();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||||
this._scrollAdjustment = null;
|
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
|
||||||
this._capturedEventId = 0;
|
this._bgManagers = [];
|
||||||
this._buttonPressId = 0;
|
|
||||||
|
|
||||||
this._workspacesDisplay = null;
|
this._activationTime = 0;
|
||||||
|
|
||||||
this.visible = false; // animating to overview, in overview, animating out
|
this.visible = false; // animating to overview, in overview, animating out
|
||||||
this._shown = false; // show() and not hide()
|
this._shown = false; // show() and not hide()
|
||||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
|
||||||
this._modal = false; // have a modal grab
|
this._modal = false; // have a modal grab
|
||||||
this.animationInProgress = false;
|
this.animationInProgress = false;
|
||||||
this._hideInProgress = false;
|
this.visibleTarget = false;
|
||||||
|
|
||||||
// During transitions, we raise this to the top to avoid having the overview
|
// During transitions, we raise this to the top to avoid having the overview
|
||||||
// area be reactive; it causes too many issues such as double clicks on
|
// area be reactive; it causes too many issues such as double clicks on
|
||||||
// Dash elements, or mouseover handlers in the workspaces.
|
// Dash elements, or mouseover handlers in the workspaces.
|
||||||
this._coverPane = new Clutter.Rectangle({ opacity: 0,
|
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
this._group.add_actor(this._coverPane);
|
this._overview.add_actor(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 true; }));
|
||||||
|
|
||||||
|
this._stack.add_actor(this._overview);
|
||||||
this._group.hide();
|
Main.layoutManager.overviewGroup.add_child(this._stack);
|
||||||
global.overlay_group.add_actor(this._group);
|
|
||||||
|
|
||||||
this._coverPane.hide();
|
this._coverPane.hide();
|
||||||
|
|
||||||
@ -174,11 +164,71 @@ const Overview = new Lang.Class({
|
|||||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||||
|
|
||||||
|
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
||||||
|
|
||||||
this._windowSwitchTimeoutId = 0;
|
this._windowSwitchTimeoutId = 0;
|
||||||
this._windowSwitchTimestamp = 0;
|
this._windowSwitchTimestamp = 0;
|
||||||
this._lastActiveWorkspaceIndex = -1;
|
this._lastActiveWorkspaceIndex = -1;
|
||||||
this._lastHoveredWindow = null;
|
this._lastHoveredWindow = null;
|
||||||
this._needsFakePointerEvent = false;
|
this._needsFakePointerEvent = false;
|
||||||
|
|
||||||
|
if (this._initCalled)
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBackgrounds: function() {
|
||||||
|
for (let i = 0; i < this._bgManagers.length; i++)
|
||||||
|
this._bgManagers[i].destroy();
|
||||||
|
|
||||||
|
this._bgManagers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||||
|
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
|
||||||
|
monitorIndex: i,
|
||||||
|
effects: Meta.BackgroundEffects.VIGNETTE });
|
||||||
|
this._bgManagers.push(bgManager);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_unshadeBackgrounds: function() {
|
||||||
|
let backgrounds = this._backgroundGroup.get_children();
|
||||||
|
for (let i = 0; i < backgrounds.length; i++) {
|
||||||
|
let background = backgrounds[i]._delegate;
|
||||||
|
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ brightness: 1.0,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ vignetteSharpness: 0.0,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_shadeBackgrounds: function() {
|
||||||
|
let backgrounds = this._backgroundGroup.get_children();
|
||||||
|
for (let i = 0; i < backgrounds.length; i++) {
|
||||||
|
let background = backgrounds[i]._delegate;
|
||||||
|
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ brightness: 0.8,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ vignetteSharpness: 0.7,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
this.isDummy = !Main.sessionMode.hasOverview;
|
||||||
|
this._createOverview();
|
||||||
},
|
},
|
||||||
|
|
||||||
// The members we construct that are implemented in JS might
|
// The members we construct that are implemented in JS might
|
||||||
@ -186,45 +236,50 @@ const Overview = new Lang.Class({
|
|||||||
// signal handlers and so forth. So we create them after
|
// signal handlers and so forth. So we create them after
|
||||||
// construction in this init() method.
|
// construction in this init() method.
|
||||||
init: function() {
|
init: function() {
|
||||||
|
this._initCalled = true;
|
||||||
|
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._shellInfo = new ShellInfo();
|
this._shellInfo = new ShellInfo();
|
||||||
|
|
||||||
this._viewSelector = new ViewSelector.ViewSelector();
|
// Add a clone of the panel to the overview so spacing and such is
|
||||||
this._group.add_actor(this._viewSelector.actor);
|
// automatic
|
||||||
|
this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel.actor }),
|
||||||
|
reactive: false,
|
||||||
|
opacity: 0 });
|
||||||
|
this._overview.add_actor(this._panelGhost);
|
||||||
|
|
||||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
this._searchEntry = new St.Entry({ name: 'searchEntry',
|
||||||
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
/* Translators: this is the text displayed
|
||||||
|
in the search entry when no search is
|
||||||
|
active; it should not exceed ~30
|
||||||
|
characters. */
|
||||||
|
hint_text: _("Type to search…"),
|
||||||
|
track_hover: true,
|
||||||
|
can_focus: true });
|
||||||
|
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
||||||
|
x_align: St.Align.MIDDLE });
|
||||||
|
this._overview.add_actor(this._searchEntryBin);
|
||||||
|
|
||||||
let appView = new AppDisplay.AllAppDisplay();
|
// Create controls
|
||||||
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
|
||||||
|
this._dash = this._controls.dash;
|
||||||
|
this._viewSelector = this._controls.viewSelector;
|
||||||
|
|
||||||
// Default search providers
|
// Add our same-line elements after the search entry
|
||||||
// Wanda comes obviously first
|
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
|
||||||
this.addSearchProvider(new Wanda.WandaSearchProvider());
|
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
|
||||||
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
|
||||||
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
|
||||||
|
|
||||||
// Load remote search providers provided by applications
|
this._stack.add_actor(this._controls.indicatorActor);
|
||||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
|
|
||||||
|
|
||||||
// TODO - recalculate everything when desktop size changes
|
// TODO - recalculate everything when desktop size changes
|
||||||
this._dash = new Dash.Dash();
|
|
||||||
this._group.add_actor(this._dash.actor);
|
|
||||||
this._dash.actor.add_constraint(this._viewSelector.constrainY);
|
|
||||||
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
|
||||||
this.dashIconSize = this._dash.iconSize;
|
this.dashIconSize = this._dash.iconSize;
|
||||||
this._dash.connect('icon-size-changed',
|
this._dash.connect('icon-size-changed',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.dashIconSize = this._dash.iconSize;
|
this.dashIconSize = this._dash.iconSize;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Translators: this is the name of the dock/favorites area on
|
|
||||||
// the left of the overview
|
|
||||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
|
|
||||||
|
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||||
this._relayout();
|
this._relayout();
|
||||||
},
|
},
|
||||||
@ -237,26 +292,35 @@ const Overview = new Lang.Class({
|
|||||||
this._viewSelector.removeSearchProvider(provider);
|
this._viewSelector.removeSearchProvider(provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
setMessage: function(text, undoCallback, undoLabel) {
|
//
|
||||||
|
// options:
|
||||||
|
// - undoCallback (function): the callback to be called if undo support is needed
|
||||||
|
// - forFeedback (boolean): whether the message is for direct feedback of a user action
|
||||||
|
//
|
||||||
|
setMessage: function(text, options) {
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._shellInfo.setMessage(text, undoCallback, undoLabel);
|
this._shellInfo.setMessage(text, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragBegin: function() {
|
_onDragBegin: function() {
|
||||||
|
this._inXdndDrag = true;
|
||||||
|
|
||||||
DND.addDragMonitor(this._dragMonitor);
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
// Remember the workspace we started from
|
// Remember the workspace we started from
|
||||||
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragEnd: function(time) {
|
_onDragEnd: function(time) {
|
||||||
|
this._inXdndDrag = false;
|
||||||
|
|
||||||
// In case the drag was canceled while in the overview
|
// In case the drag was canceled while in the overview
|
||||||
// we have to go back to where we started and hide
|
// we have to go back to where we started and hide
|
||||||
// the overview
|
// the overview
|
||||||
if (this._shownTemporarily) {
|
if (this._shown) {
|
||||||
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
||||||
this.hideTemporarily();
|
this.hide();
|
||||||
}
|
}
|
||||||
this._resetWindowSwitchTimeout();
|
this._resetWindowSwitchTimeout();
|
||||||
this._lastHoveredWindow = null;
|
this._lastHoveredWindow = null;
|
||||||
@ -304,7 +368,7 @@ const Overview = new Lang.Class({
|
|||||||
this._needsFakePointerEvent = true;
|
this._needsFakePointerEvent = true;
|
||||||
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
||||||
this._windowSwitchTimestamp);
|
this._windowSwitchTimestamp);
|
||||||
this.hideTemporarily();
|
this.hide();
|
||||||
this._lastHoveredWindow = null;
|
this._lastHoveredWindow = null;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -312,159 +376,15 @@ const Overview = new Lang.Class({
|
|||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
|
|
||||||
setScrollAdjustment: function(adjustment, direction) {
|
_onScrollEvent: function(actor, event) {
|
||||||
|
this.emit('scroll-event', event);
|
||||||
|
},
|
||||||
|
|
||||||
|
addAction: function(action) {
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._scrollAdjustment = adjustment;
|
this._overview.add_action(action);
|
||||||
if (this._scrollAdjustment == null)
|
|
||||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
|
||||||
else
|
|
||||||
this._scrollDirection = direction;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onButtonPress: function(actor, event) {
|
|
||||||
if (this._scrollDirection == SwipeScrollDirection.NONE
|
|
||||||
|| event.get_button() != 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let [stageX, stageY] = event.get_coords();
|
|
||||||
this._dragStartX = this._dragX = stageX;
|
|
||||||
this._dragStartY = this._dragY = stageY;
|
|
||||||
this._dragStartValue = this._scrollAdjustment.value;
|
|
||||||
this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
|
|
||||||
this._capturedEventId = global.stage.connect('captured-event',
|
|
||||||
Lang.bind(this, this._onCapturedEvent));
|
|
||||||
this.emit('swipe-scroll-begin');
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCapturedEvent: function(actor, event) {
|
|
||||||
let stageX, stageY;
|
|
||||||
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
|
|
||||||
|
|
||||||
switch(event.type()) {
|
|
||||||
case Clutter.EventType.BUTTON_RELEASE:
|
|
||||||
[stageX, stageY] = event.get_coords();
|
|
||||||
|
|
||||||
// default to snapping back to the original value
|
|
||||||
let newValue = this._dragStartValue;
|
|
||||||
|
|
||||||
let minValue = this._scrollAdjustment.lower;
|
|
||||||
let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
|
|
||||||
|
|
||||||
let direction;
|
|
||||||
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
|
|
||||||
direction = stageX > this._dragStartX ? -1 : 1;
|
|
||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
|
||||||
direction *= -1;
|
|
||||||
} else {
|
|
||||||
direction = stageY > this._dragStartY ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We default to scroll a full page size; both the first
|
|
||||||
// and the last page may be smaller though, so we need to
|
|
||||||
// adjust difference in those cases.
|
|
||||||
let difference = direction * this._scrollAdjustment.page_size;
|
|
||||||
if (this._dragStartValue + difference > maxValue)
|
|
||||||
difference = maxValue - this._dragStartValue;
|
|
||||||
else if (this._dragStartValue + difference < minValue)
|
|
||||||
difference = minValue - this._dragStartValue;
|
|
||||||
|
|
||||||
// If the user has moved more than half the scroll
|
|
||||||
// difference, we want to "settle" to the new value
|
|
||||||
// even if the user stops dragging rather "throws" by
|
|
||||||
// releasing during the drag.
|
|
||||||
let distance = this._dragStartValue - this._scrollAdjustment.value;
|
|
||||||
let noStop = Math.abs(distance / difference) > 0.5;
|
|
||||||
|
|
||||||
// We detect if the user is stopped by comparing the
|
|
||||||
// timestamp of the button release with the timestamp of
|
|
||||||
// the last motion. Experimentally, a difference of 0 or 1
|
|
||||||
// millisecond indicates that the mouse is in motion, a
|
|
||||||
// larger difference indicates that the mouse is stopped.
|
|
||||||
if ((this._lastMotionTime > 0 &&
|
|
||||||
this._lastMotionTime > event.get_time() - 2) ||
|
|
||||||
noStop) {
|
|
||||||
if (this._dragStartValue + difference >= minValue &&
|
|
||||||
this._dragStartValue + difference <= maxValue)
|
|
||||||
newValue += difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
let result;
|
|
||||||
|
|
||||||
// See if the user has moved the mouse enough to trigger
|
|
||||||
// a drag
|
|
||||||
if (Math.abs(stageX - this._dragStartX) < threshold &&
|
|
||||||
Math.abs(stageY - this._dragStartY) < threshold) {
|
|
||||||
// no motion? It's a click!
|
|
||||||
result = SwipeScrollResult.CLICK;
|
|
||||||
this.emit('swipe-scroll-end', result);
|
|
||||||
} else {
|
|
||||||
if (newValue == this._dragStartValue)
|
|
||||||
result = SwipeScrollResult.CANCEL;
|
|
||||||
else
|
|
||||||
result = SwipeScrollResult.SWIPE;
|
|
||||||
|
|
||||||
// The event capture handler is disconnected
|
|
||||||
// while scrolling to the final position, so
|
|
||||||
// to avoid undesired prelights we raise
|
|
||||||
// the cover pane.
|
|
||||||
this._coverPane.raise_top();
|
|
||||||
this._coverPane.show();
|
|
||||||
|
|
||||||
Tweener.addTween(this._scrollAdjustment,
|
|
||||||
{ value: newValue,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
onCompleteScope: this,
|
|
||||||
onComplete: function() {
|
|
||||||
this._coverPane.hide();
|
|
||||||
this.emit('swipe-scroll-end',
|
|
||||||
result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
global.stage.disconnect(this._capturedEventId);
|
|
||||||
this._capturedEventId = 0;
|
|
||||||
|
|
||||||
return result != SwipeScrollResult.CLICK;
|
|
||||||
|
|
||||||
case Clutter.EventType.MOTION:
|
|
||||||
[stageX, stageY] = event.get_coords();
|
|
||||||
let dx = this._dragX - stageX;
|
|
||||||
let dy = this._dragY - stageY;
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
|
||||||
|
|
||||||
this._dragX = stageX;
|
|
||||||
this._dragY = stageY;
|
|
||||||
this._lastMotionTime = event.get_time();
|
|
||||||
|
|
||||||
// See if the user has moved the mouse enough to trigger
|
|
||||||
// a drag
|
|
||||||
if (Math.abs(stageX - this._dragStartX) < threshold &&
|
|
||||||
Math.abs(stageY - this._dragStartY) < threshold)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
|
|
||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
|
||||||
this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
|
|
||||||
else
|
|
||||||
this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
|
|
||||||
} else {
|
|
||||||
this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Block enter/leave events to avoid prelights
|
|
||||||
// during swipe-scroll
|
|
||||||
case Clutter.EventType.ENTER:
|
|
||||||
case Clutter.EventType.LEAVE:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getDesktopClone: function() {
|
_getDesktopClone: function() {
|
||||||
@ -474,7 +394,9 @@ const Overview = new Lang.Class({
|
|||||||
if (windows.length == 0)
|
if (windows.length == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
let clone = new Clutter.Clone({ source: windows[0].get_texture() });
|
let window = windows[0];
|
||||||
|
let clone = new Clutter.Clone({ source: window.get_texture(),
|
||||||
|
x: window.x, y: window.y });
|
||||||
clone.source.connect('destroy', Lang.bind(this, function() {
|
clone.source.connect('destroy', Lang.bind(this, function() {
|
||||||
clone.destroy();
|
clone.destroy();
|
||||||
}));
|
}));
|
||||||
@ -487,42 +409,31 @@ const Overview = new Lang.Class({
|
|||||||
// when it is next shown.
|
// when it is next shown.
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||||
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
|
||||||
|
|
||||||
let contentY = Main.panel.actor.height;
|
this._coverPane.set_position(0, workArea.y);
|
||||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
this._coverPane.set_size(workArea.width, workArea.height);
|
||||||
|
|
||||||
this._group.set_position(primary.x, primary.y);
|
this._updateBackgrounds();
|
||||||
this._group.set_size(primary.width, primary.height);
|
},
|
||||||
|
|
||||||
this._coverPane.set_position(0, contentY);
|
_onRestacked: function() {
|
||||||
this._coverPane.set_size(primary.width, contentHeight);
|
let stack = global.get_window_actors();
|
||||||
|
let stackIndices = {};
|
||||||
|
|
||||||
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
|
for (let i = 0; i < stack.length; i++) {
|
||||||
let viewWidth = primary.width - dashWidth - this._spacing;
|
// Use the stable sequence for an integer to use as a hash key
|
||||||
let viewHeight = contentHeight - 2 * this._spacing;
|
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||||
let viewY = contentY + this._spacing;
|
|
||||||
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
|
||||||
|
|
||||||
// Set the dash's x position - y is handled by a constraint
|
|
||||||
let dashX;
|
|
||||||
if (rtl) {
|
|
||||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
|
||||||
dashX = primary.width;
|
|
||||||
} else {
|
|
||||||
dashX = 0;
|
|
||||||
}
|
}
|
||||||
this._dash.actor.set_x(dashX);
|
|
||||||
|
|
||||||
this._viewSelector.actor.set_position(viewX, viewY);
|
this.emit('windows-restacked', stackIndices);
|
||||||
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Public methods ////
|
//// Public methods ////
|
||||||
|
|
||||||
beginItemDrag: function(source) {
|
beginItemDrag: function(source) {
|
||||||
this.emit('item-drag-begin');
|
this.emit('item-drag-begin');
|
||||||
|
this._inDrag = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelledItemDrag: function(source) {
|
cancelledItemDrag: function(source) {
|
||||||
@ -531,10 +442,12 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
endItemDrag: function(source) {
|
endItemDrag: function(source) {
|
||||||
this.emit('item-drag-end');
|
this.emit('item-drag-end');
|
||||||
|
this._inDrag = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
beginWindowDrag: function(source) {
|
beginWindowDrag: function(source) {
|
||||||
this.emit('window-drag-begin');
|
this.emit('window-drag-begin');
|
||||||
|
this._inDrag = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelledWindowDrag: function(source) {
|
cancelledWindowDrag: function(source) {
|
||||||
@ -543,25 +456,51 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
endWindowDrag: function(source) {
|
endWindowDrag: function(source) {
|
||||||
this.emit('window-drag-end');
|
this.emit('window-drag-end');
|
||||||
|
this._inDrag = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// show:
|
// show:
|
||||||
//
|
//
|
||||||
// Animates the overview visible and grabs mouse and keyboard input
|
// Animates the overview visible and grabs mouse and keyboard input
|
||||||
show : function() {
|
show: function() {
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
if (this._shown)
|
if (this._shown)
|
||||||
return;
|
return;
|
||||||
// Do this manually instead of using _syncInputMode, to handle failure
|
|
||||||
if (!Main.pushModal(this._group))
|
|
||||||
return;
|
|
||||||
this._modal = true;
|
|
||||||
this._animateVisible();
|
|
||||||
this._shown = true;
|
this._shown = true;
|
||||||
|
|
||||||
this._buttonPressId = this._group.connect('button-press-event',
|
if (!this._syncGrab())
|
||||||
Lang.bind(this, this._onButtonPress));
|
return;
|
||||||
|
|
||||||
|
Main.layoutManager.showOverview();
|
||||||
|
this._animateVisible();
|
||||||
|
},
|
||||||
|
|
||||||
|
focusSearch: function() {
|
||||||
|
this.show();
|
||||||
|
this._searchEntry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeInDesktop: function() {
|
||||||
|
this._desktopFade.opacity = 0;
|
||||||
|
this._desktopFade.show();
|
||||||
|
Tweener.addTween(this._desktopFade,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeOutDesktop: function() {
|
||||||
|
if (!this._desktopFade.child)
|
||||||
|
this._desktopFade.child = this._getDesktopClone();
|
||||||
|
|
||||||
|
this._desktopFade.opacity = 255;
|
||||||
|
this._desktopFade.show();
|
||||||
|
Tweener.addTween(this._desktopFade,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_animateVisible: function() {
|
_animateVisible: function() {
|
||||||
@ -570,6 +509,8 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.animationInProgress = true;
|
this.animationInProgress = true;
|
||||||
|
this.visibleTarget = true;
|
||||||
|
this._activationTime = Date.now() / 1000;
|
||||||
|
|
||||||
// All the the actors in the window group are completely obscured,
|
// All the the actors in the window group are completely obscured,
|
||||||
// hiding the group holding them while the Overview is displayed greatly
|
// hiding the group holding them while the Overview is displayed greatly
|
||||||
@ -581,63 +522,23 @@ const Overview = new Lang.Class({
|
|||||||
//
|
//
|
||||||
// Disable unredirection while in the overview
|
// Disable unredirection while in the overview
|
||||||
Meta.disable_unredirect_for_screen(global.screen);
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
global.window_group.hide();
|
this._viewSelector.show();
|
||||||
this._group.show();
|
|
||||||
this._background.show();
|
|
||||||
|
|
||||||
this._workspacesDisplay.show();
|
this._stack.opacity = 0;
|
||||||
|
Tweener.addTween(this._stack,
|
||||||
if (!this._desktopFade.child)
|
|
||||||
this._desktopFade.child = this._getDesktopClone();
|
|
||||||
|
|
||||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
|
||||||
this._desktopFade.opacity = 255;
|
|
||||||
this._desktopFade.show();
|
|
||||||
Tweener.addTween(this._desktopFade,
|
|
||||||
{ opacity: 0,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._group.opacity = 0;
|
|
||||||
Tweener.addTween(this._group,
|
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
onComplete: this._showDone,
|
onComplete: this._showDone,
|
||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
|
this._shadeBackgrounds();
|
||||||
Tweener.addTween(this._background,
|
|
||||||
{ dim_factor: 0.4,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
|
|
||||||
this._coverPane.raise_top();
|
this._coverPane.raise_top();
|
||||||
this._coverPane.show();
|
this._coverPane.show();
|
||||||
this.emit('showing');
|
this.emit('showing');
|
||||||
},
|
},
|
||||||
|
|
||||||
// showTemporarily:
|
|
||||||
//
|
|
||||||
// Animates the overview visible without grabbing mouse and keyboard input;
|
|
||||||
// if show() has already been called, this has no immediate effect, but
|
|
||||||
// will result in the overview not being hidden until hideTemporarily() is
|
|
||||||
// called.
|
|
||||||
showTemporarily: function() {
|
|
||||||
if (this.isDummy)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._shownTemporarily)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._syncInputMode();
|
|
||||||
this._animateVisible();
|
|
||||||
this._shownTemporarily = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
// hide:
|
// hide:
|
||||||
//
|
//
|
||||||
// Reverses the effect of show()
|
// Reverses the effect of show()
|
||||||
@ -648,74 +549,77 @@ const Overview = new Lang.Class({
|
|||||||
if (!this._shown)
|
if (!this._shown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this._shownTemporarily)
|
let event = Clutter.get_current_event();
|
||||||
this._animateNotVisible();
|
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._shown = false;
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
|
|
||||||
if (this._buttonPressId > 0)
|
|
||||||
this._group.disconnect(this._buttonPressId);
|
|
||||||
this._buttonPressId = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
// hideTemporarily:
|
|
||||||
//
|
|
||||||
// Reverses the effect of showTemporarily()
|
|
||||||
hideTemporarily: function() {
|
|
||||||
if (this.isDummy)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._shownTemporarily)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._shown)
|
|
||||||
this._animateNotVisible();
|
|
||||||
|
|
||||||
this._shownTemporarily = false;
|
|
||||||
this._syncInputMode();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._shown)
|
if (this.visible)
|
||||||
this.hide();
|
this.hide();
|
||||||
else
|
else
|
||||||
this.show();
|
this.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Checks if the Activities button is currently sensitive to
|
||||||
|
// clicks. The first call to this function within the
|
||||||
|
// OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
|
||||||
|
// triggered will return false. This avoids opening and closing
|
||||||
|
// the overview if the user both triggered the hot corner and
|
||||||
|
// clicked the Activities button.
|
||||||
|
shouldToggleByCornerOrButton: function() {
|
||||||
|
if (this.animationInProgress)
|
||||||
|
return false;
|
||||||
|
if (this._inDrag)
|
||||||
|
return false;
|
||||||
|
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
_syncInputMode: function() {
|
_syncGrab: function() {
|
||||||
// We delay input mode changes during animation so that when removing the
|
// We delay grab changes during animation so that when removing the
|
||||||
// overview we don't have a problem with the release of a press/release
|
// overview we don't have a problem with the release of a press/release
|
||||||
// going to an application.
|
// going to an application.
|
||||||
if (this.animationInProgress)
|
if (this.animationInProgress)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
if (this._shown) {
|
if (this._shown) {
|
||||||
if (!this._modal) {
|
let shouldBeModal = !this._inXdndDrag;
|
||||||
if (Main.pushModal(this._group))
|
if (shouldBeModal) {
|
||||||
this._modal = true;
|
if (!this._modal) {
|
||||||
else
|
if (Main.pushModal(this._overview,
|
||||||
this.hide();
|
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) {
|
||||||
|
this._modal = true;
|
||||||
|
} else {
|
||||||
|
this.hide();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (this._shownTemporarily) {
|
|
||||||
if (this._modal) {
|
|
||||||
Main.popModal(this._group);
|
|
||||||
this._modal = false;
|
|
||||||
}
|
|
||||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
|
||||||
} else {
|
} else {
|
||||||
if (this._modal) {
|
if (this._modal) {
|
||||||
Main.popModal(this._group);
|
Main.popModal(this._overview);
|
||||||
this._modal = false;
|
this._modal = false;
|
||||||
}
|
}
|
||||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
|
||||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_animateNotVisible: function() {
|
_animateNotVisible: function() {
|
||||||
@ -723,33 +627,19 @@ const Overview = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.animationInProgress = true;
|
this.animationInProgress = true;
|
||||||
this._hideInProgress = true;
|
this.visibleTarget = false;
|
||||||
|
|
||||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
this._viewSelector.zoomFromOverview();
|
||||||
this._desktopFade.opacity = 0;
|
|
||||||
this._desktopFade.show();
|
|
||||||
Tweener.addTween(this._desktopFade,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad' });
|
|
||||||
}
|
|
||||||
|
|
||||||
this._workspacesDisplay.zoomFromOverview();
|
|
||||||
|
|
||||||
// Make other elements fade out.
|
// Make other elements fade out.
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._stack,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
onComplete: this._hideDone,
|
onComplete: this._hideDone,
|
||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
|
this._unshadeBackgrounds();
|
||||||
Tweener.addTween(this._background,
|
|
||||||
{ dim_factor: 1.0,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
|
|
||||||
this._coverPane.raise_top();
|
this._coverPane.raise_top();
|
||||||
this._coverPane.show();
|
this._coverPane.show();
|
||||||
@ -763,10 +653,10 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this.emit('shown');
|
this.emit('shown');
|
||||||
// Handle any calls to hide* while we were showing
|
// Handle any calls to hide* while we were showing
|
||||||
if (!this._shown && !this._shownTemporarily)
|
if (!this._shown)
|
||||||
this._animateNotVisible();
|
this._animateNotVisible();
|
||||||
|
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
global.sync_pointer();
|
global.sync_pointer();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -774,26 +664,21 @@ const Overview = new Lang.Class({
|
|||||||
// Re-enable unredirection
|
// Re-enable unredirection
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
|
||||||
global.window_group.show();
|
this._viewSelector.hide();
|
||||||
|
|
||||||
this._workspacesDisplay.hide();
|
|
||||||
|
|
||||||
this._desktopFade.hide();
|
this._desktopFade.hide();
|
||||||
this._background.hide();
|
this._coverPane.hide();
|
||||||
this._group.hide();
|
|
||||||
|
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.animationInProgress = false;
|
this.animationInProgress = false;
|
||||||
this._hideInProgress = false;
|
|
||||||
|
|
||||||
this._coverPane.hide();
|
|
||||||
|
|
||||||
this.emit('hidden');
|
this.emit('hidden');
|
||||||
// Handle any calls to show* while we were hiding
|
// Handle any calls to show* while we were hiding
|
||||||
if (this._shown || this._shownTemporarily)
|
if (this._shown)
|
||||||
this._animateVisible();
|
this._animateVisible();
|
||||||
|
else
|
||||||
|
Main.layoutManager.hideOverview();
|
||||||
|
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
|
|
||||||
// Fake a pointer event if requested
|
// Fake a pointer event if requested
|
||||||
if (this._needsFakePointerEvent) {
|
if (this._needsFakePointerEvent) {
|
||||||
|
603
js/ui/overviewControls.js
Normal file
@ -0,0 +1,603 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Dash = imports.ui.dash;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const ViewSelector = imports.ui.viewSelector;
|
||||||
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||||
|
|
||||||
|
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
|
||||||
|
|
||||||
|
function getRtlSlideDirection(direction, actor) {
|
||||||
|
let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
|
||||||
|
if (rtl)
|
||||||
|
direction = (direction == SlideDirection.LEFT) ?
|
||||||
|
SlideDirection.RIGHT : SlideDirection.LEFT;
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SlideDirection = {
|
||||||
|
LEFT: 0,
|
||||||
|
RIGHT: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const SlideLayout = new Lang.Class({
|
||||||
|
Name: 'SlideLayout',
|
||||||
|
Extends: Clutter.FixedLayout,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this._slideX = 1;
|
||||||
|
this._direction = SlideDirection.LEFT;
|
||||||
|
|
||||||
|
this.parent(params);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_width: function(container, forHeight) {
|
||||||
|
let child = container.get_first_child();
|
||||||
|
|
||||||
|
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
|
||||||
|
|
||||||
|
minWidth *= this._slideX;
|
||||||
|
natWidth *= this._slideX;
|
||||||
|
|
||||||
|
return [minWidth, natWidth];
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_allocate: function(container, box, flags) {
|
||||||
|
let child = container.get_first_child();
|
||||||
|
|
||||||
|
let [, , natWidth, natHeight] = child.get_preferred_size();
|
||||||
|
let availWidth = Math.round(box.x2 - box.x1);
|
||||||
|
let availHeight = Math.round(box.y2 - box.y1);
|
||||||
|
|
||||||
|
let realDirection = getRtlSlideDirection(this._direction, child);
|
||||||
|
let translationX = (realDirection == SlideDirection.LEFT) ?
|
||||||
|
(availWidth - natWidth) : (natWidth - availWidth);
|
||||||
|
|
||||||
|
let actorBox = new Clutter.ActorBox({ x1: translationX,
|
||||||
|
y1: 0,
|
||||||
|
x2: child.x_expand ? availWidth : natWidth,
|
||||||
|
y2: child.y_expand ? availHeight : natHeight });
|
||||||
|
|
||||||
|
child.allocate(actorBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
set slideX(value) {
|
||||||
|
this._slideX = value;
|
||||||
|
this.layout_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
get slideX() {
|
||||||
|
return this._slideX;
|
||||||
|
},
|
||||||
|
|
||||||
|
set slideDirection(direction) {
|
||||||
|
this._direction = direction;
|
||||||
|
this.layout_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
get slideDirection() {
|
||||||
|
return this._direction;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SlidingControl = new Lang.Class({
|
||||||
|
Name: 'SlidingControl',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
|
||||||
|
|
||||||
|
this.visible = true;
|
||||||
|
this.inDrag = false;
|
||||||
|
|
||||||
|
this.layout = new SlideLayout();
|
||||||
|
this.layout.slideDirection = params.slideDirection;
|
||||||
|
this.actor = new St.Widget({ layout_manager: this.layout,
|
||||||
|
style_class: 'overview-controls',
|
||||||
|
clip_to_allocation: true });
|
||||||
|
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
|
||||||
|
|
||||||
|
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
|
||||||
|
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
|
||||||
|
Main.overview.connect('item-drag-cancelled', Lang.bind(this, this._onDragEnd));
|
||||||
|
|
||||||
|
Main.overview.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
|
||||||
|
Main.overview.connect('window-drag-cancelled', Lang.bind(this, this._onWindowDragEnd));
|
||||||
|
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
|
||||||
|
},
|
||||||
|
|
||||||
|
getSlide: function() {
|
||||||
|
throw new Error('getSlide() must be overridden');
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSlide: function() {
|
||||||
|
Tweener.addTween(this.layout, { slideX: this.getSlide(),
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
},
|
||||||
|
|
||||||
|
getVisibleWidth: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
let [, , natWidth, ] = child.get_preferred_size();
|
||||||
|
return natWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getTranslation: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
let direction = getRtlSlideDirection(this.layout.slideDirection, child);
|
||||||
|
let visibleWidth = this.getVisibleWidth();
|
||||||
|
|
||||||
|
if (direction == SlideDirection.LEFT)
|
||||||
|
return - visibleWidth;
|
||||||
|
else
|
||||||
|
return visibleWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateTranslation: function() {
|
||||||
|
let translationStart = 0;
|
||||||
|
let translationEnd = 0;
|
||||||
|
let translation = this._getTranslation();
|
||||||
|
|
||||||
|
if (this.visible) {
|
||||||
|
translationStart = translation;
|
||||||
|
} else {
|
||||||
|
translationEnd = translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actor.translation_x == translationEnd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.actor.translation_x = translationStart;
|
||||||
|
Tweener.addTween(this.actor, { translation_x: 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;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragBegin: function() {
|
||||||
|
this._onDragBegin();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragEnd: function() {
|
||||||
|
this._onDragEnd();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragBegin: function() {
|
||||||
|
this.inDrag = true;
|
||||||
|
this.actor.translation_x = 0;
|
||||||
|
this.updateSlide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragEnd: function() {
|
||||||
|
this.inDrag = false;
|
||||||
|
this.updateSlide();
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeIn: function() {
|
||||||
|
Tweener.addTween(this.actor, { opacity: 255,
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||||
|
transition: 'easeInQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeHalf: function() {
|
||||||
|
Tweener.addTween(this.actor, { opacity: 128,
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
slideIn: function() {
|
||||||
|
this.visible = true;
|
||||||
|
// we will update slideX and the translation from pageEmpty
|
||||||
|
},
|
||||||
|
|
||||||
|
slideOut: function() {
|
||||||
|
this.visible = false;
|
||||||
|
this._updateTranslation();
|
||||||
|
// we will update slideX from pageEmpty
|
||||||
|
},
|
||||||
|
|
||||||
|
pageEmpty: function() {
|
||||||
|
// When pageEmpty is received, there's no visible view in the
|
||||||
|
// selector; this means we can now safely set the full slide for
|
||||||
|
// the next page, since slideIn or slideOut might have been called,
|
||||||
|
// changing the visiblity
|
||||||
|
this.layout.slideX = this.getSlide();
|
||||||
|
this._updateTranslation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThumbnailsSlider = new Lang.Class({
|
||||||
|
Name: 'ThumbnailsSlider',
|
||||||
|
Extends: SlidingControl,
|
||||||
|
|
||||||
|
_init: function(thumbnailsBox) {
|
||||||
|
this.parent({ slideDirection: SlideDirection.RIGHT });
|
||||||
|
|
||||||
|
this._thumbnailsBox = thumbnailsBox;
|
||||||
|
|
||||||
|
// SlideLayout reads the actor's expand flags to decide
|
||||||
|
// whether to allocate the natural size to its child, or the whole
|
||||||
|
// available allocation
|
||||||
|
this._thumbnailsBox.actor.y_expand = true;
|
||||||
|
|
||||||
|
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
|
||||||
|
this.actor.reactive = true;
|
||||||
|
this.actor.track_hover = true;
|
||||||
|
this.actor.add_actor(this._thumbnailsBox.actor);
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
|
||||||
|
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
|
||||||
|
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getAlwaysZoomOut: function() {
|
||||||
|
// Always show the pager when hover, during a drag, or if workspaces are
|
||||||
|
// actually used, e.g. there are windows on more than one
|
||||||
|
let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
|
||||||
|
|
||||||
|
if (!alwaysZoomOut) {
|
||||||
|
let monitors = Main.layoutManager.monitors;
|
||||||
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
|
/* Look for any monitor to the right of the primary, if there is
|
||||||
|
* one, we always keep zoom out, otherwise its hard to reach
|
||||||
|
* the thumbnail area without passing into the next monitor. */
|
||||||
|
for (let i = 0; i < monitors.length; i++) {
|
||||||
|
if (monitors[i].x >= primary.x + primary.width) {
|
||||||
|
alwaysZoomOut = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return alwaysZoomOut;
|
||||||
|
},
|
||||||
|
|
||||||
|
getNonExpandedWidth: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
return child.get_theme_node().get_length('visible-width');
|
||||||
|
},
|
||||||
|
|
||||||
|
getSlide: function() {
|
||||||
|
if (!this.visible)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||||
|
if (alwaysZoomOut)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
let preferredHeight = child.get_preferred_height(-1)[1];
|
||||||
|
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
|
||||||
|
|
||||||
|
return this.getNonExpandedWidth() / expandedWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
getVisibleWidth: function() {
|
||||||
|
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||||
|
if (alwaysZoomOut)
|
||||||
|
return this.parent();
|
||||||
|
else
|
||||||
|
return this.getNonExpandedWidth();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DashSlider = new Lang.Class({
|
||||||
|
Name: 'DashSlider',
|
||||||
|
Extends: SlidingControl,
|
||||||
|
|
||||||
|
_init: function(dash) {
|
||||||
|
this.parent({ slideDirection: SlideDirection.LEFT });
|
||||||
|
|
||||||
|
this._dash = dash;
|
||||||
|
|
||||||
|
// SlideLayout reads the actor's expand flags to decide
|
||||||
|
// whether to allocate the natural size to its child, or the whole
|
||||||
|
// available allocation
|
||||||
|
this._dash.actor.x_expand = true;
|
||||||
|
this._dash.actor.y_expand = true;
|
||||||
|
|
||||||
|
this.actor.x_align = Clutter.ActorAlign.START;
|
||||||
|
this.actor.y_expand = true;
|
||||||
|
|
||||||
|
this.actor.add_actor(this._dash.actor);
|
||||||
|
|
||||||
|
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
|
||||||
|
},
|
||||||
|
|
||||||
|
getSlide: function() {
|
||||||
|
if (this.visible || this.inDrag)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragBegin: function() {
|
||||||
|
this.fadeHalf();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragEnd: function() {
|
||||||
|
this.fadeIn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DashSpacer = new Lang.Class({
|
||||||
|
Name: 'DashSpacer',
|
||||||
|
Extends: St.Widget,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
|
||||||
|
this._bindConstraint = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
setDashActor: function(dashActor) {
|
||||||
|
if (this._bindConstraint) {
|
||||||
|
this.remove_constraint(this._bindConstraint);
|
||||||
|
this._bindConstraint = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashActor) {
|
||||||
|
this._bindConstraint = new Clutter.BindConstraint({ source: dashActor,
|
||||||
|
coordinate: Clutter.BindCoordinate.SIZE });
|
||||||
|
this.add_constraint(this._bindConstraint);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_width: function(forHeight) {
|
||||||
|
let box = this.get_allocation_box();
|
||||||
|
let minWidth = this.parent(forHeight)[0];
|
||||||
|
let natWidth = box.x2 - box.x1;
|
||||||
|
return [minWidth, natWidth];
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_height: function(forWidth) {
|
||||||
|
let box = this.get_allocation_box();
|
||||||
|
let minHeight = this.parent(forWidth)[0];
|
||||||
|
let natHeight = box.y2 - box.y1;
|
||||||
|
return [minHeight, natHeight];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const MessagesIndicator = new Lang.Class({
|
||||||
|
Name: 'MessagesIndicator',
|
||||||
|
|
||||||
|
_init: function(viewSelector) {
|
||||||
|
this._count = 0;
|
||||||
|
this._sources = [];
|
||||||
|
this._viewSelector = viewSelector;
|
||||||
|
|
||||||
|
this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
|
||||||
|
reactive: true,
|
||||||
|
track_hover: true,
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
x_align: Clutter.ActorAlign.CENTER });
|
||||||
|
|
||||||
|
this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
|
||||||
|
icon_size: 16 });
|
||||||
|
this._container.add_actor(this._icon);
|
||||||
|
|
||||||
|
this._label = new St.Label();
|
||||||
|
this._container.add_actor(this._label);
|
||||||
|
|
||||||
|
this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
y_align: Clutter.ActorAlign.END,
|
||||||
|
visible: false });
|
||||||
|
|
||||||
|
this._container.connect('notify::hover', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._highlight.visible = this._container.hover;
|
||||||
|
}));
|
||||||
|
|
||||||
|
let clickAction = new Clutter.ClickAction();
|
||||||
|
this._container.add_action(clickAction);
|
||||||
|
clickAction.connect('clicked', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
Main.messageTray.openTray();
|
||||||
|
}));
|
||||||
|
|
||||||
|
Main.messageTray.connect('showing', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._highlight.visible = false;
|
||||||
|
this._container.hover = false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
let layout = new Clutter.BinLayout();
|
||||||
|
this.actor = new St.Widget({ layout_manager: layout,
|
||||||
|
style_class: 'messages-indicator',
|
||||||
|
y_expand: true,
|
||||||
|
y_align: Clutter.ActorAlign.END,
|
||||||
|
visible: false });
|
||||||
|
this.actor.add_actor(this._container);
|
||||||
|
this.actor.add_actor(this._highlight);
|
||||||
|
|
||||||
|
Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
|
||||||
|
Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
|
||||||
|
|
||||||
|
let sources = Main.messageTray.getSources();
|
||||||
|
sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
|
||||||
|
|
||||||
|
this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceAdded: function(tray, source) {
|
||||||
|
if (source.trayIcon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (source.isTransient)
|
||||||
|
return;
|
||||||
|
|
||||||
|
source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||||
|
this._sources.push(source);
|
||||||
|
this._updateCount();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceRemoved: function(tray, source) {
|
||||||
|
this._sources.splice(this._sources.indexOf(source), 1);
|
||||||
|
this._updateCount();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCount: function() {
|
||||||
|
let count = 0;
|
||||||
|
let hasChats = false;
|
||||||
|
this._sources.forEach(Lang.bind(this,
|
||||||
|
function(source) {
|
||||||
|
count += source.indicatorCount;
|
||||||
|
hasChats |= source.isChat;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._count = count;
|
||||||
|
this._label.text = ngettext("%d new message",
|
||||||
|
"%d new messages",
|
||||||
|
count).format(count);
|
||||||
|
|
||||||
|
this._icon.visible = hasChats;
|
||||||
|
this._updateVisibility();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateVisibility: function() {
|
||||||
|
let activePage = this._viewSelector.getActivePage();
|
||||||
|
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
|
||||||
|
|
||||||
|
this.actor.visible = visible;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ControlsManager = new Lang.Class({
|
||||||
|
Name: 'ControlsManager',
|
||||||
|
|
||||||
|
_init: function(searchEntry) {
|
||||||
|
this.dash = new Dash.Dash();
|
||||||
|
this._dashSlider = new DashSlider(this.dash);
|
||||||
|
this._dashSpacer = new DashSpacer();
|
||||||
|
this._dashSpacer.setDashActor(this._dashSlider.actor);
|
||||||
|
|
||||||
|
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||||
|
this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
|
||||||
|
|
||||||
|
this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
|
||||||
|
this.dash.showAppsButton);
|
||||||
|
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
|
||||||
|
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
|
||||||
|
|
||||||
|
this._indicator = new MessagesIndicator(this.viewSelector);
|
||||||
|
this.indicatorActor = this._indicator.actor;
|
||||||
|
|
||||||
|
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||||
|
reactive: true,
|
||||||
|
x_expand: true, y_expand: true,
|
||||||
|
clip_to_allocation: true });
|
||||||
|
this._group = new St.BoxLayout({ name: 'overview-group',
|
||||||
|
x_expand: true, y_expand: true });
|
||||||
|
this.actor.add_actor(this._group);
|
||||||
|
|
||||||
|
this.actor.add_actor(this._dashSlider.actor);
|
||||||
|
|
||||||
|
this._group.add_actor(this._dashSpacer);
|
||||||
|
this._group.add(this.viewSelector.actor, { x_fill: true,
|
||||||
|
expand: true });
|
||||||
|
this._group.add_actor(this._thumbnailsSlider.actor);
|
||||||
|
|
||||||
|
this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
|
||||||
|
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
|
||||||
|
Main.overview.connect('item-drag-begin', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
let activePage = this.viewSelector.getActivePage();
|
||||||
|
if (activePage != ViewSelector.ViewPage.WINDOWS)
|
||||||
|
this.viewSelector.fadeHalf();
|
||||||
|
}));
|
||||||
|
Main.overview.connect('item-drag-end', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.viewSelector.fadeIn();
|
||||||
|
}));
|
||||||
|
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.viewSelector.fadeIn();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateWorkspacesGeometry: function() {
|
||||||
|
let [x, y] = this.actor.get_transformed_position();
|
||||||
|
let [width, height] = this.actor.get_transformed_size();
|
||||||
|
let geometry = { x: x, y: y, width: width, height: height };
|
||||||
|
|
||||||
|
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||||
|
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
|
||||||
|
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
|
||||||
|
|
||||||
|
geometry.width -= dashWidth;
|
||||||
|
geometry.width -= thumbnailsWidth;
|
||||||
|
|
||||||
|
if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
|
||||||
|
geometry.x += dashWidth;
|
||||||
|
else
|
||||||
|
geometry.x += thumbnailsWidth;
|
||||||
|
|
||||||
|
this.viewSelector.setWorkspacesFullGeometry(geometry);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setVisibility: function() {
|
||||||
|
// Ignore the case when we're leaving the overview, since
|
||||||
|
// actors will be made visible again when entering the overview
|
||||||
|
// next time, and animating them while doing so is just
|
||||||
|
// unnecessary noise
|
||||||
|
if (!Main.overview.visible ||
|
||||||
|
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let activePage = this.viewSelector.getActivePage();
|
||||||
|
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
|
||||||
|
activePage == ViewSelector.ViewPage.APPS);
|
||||||
|
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||||
|
|
||||||
|
if (dashVisible)
|
||||||
|
this._dashSlider.slideIn();
|
||||||
|
else
|
||||||
|
this._dashSlider.slideOut();
|
||||||
|
|
||||||
|
if (thumbnailsVisible)
|
||||||
|
this._thumbnailsSlider.slideIn();
|
||||||
|
else
|
||||||
|
this._thumbnailsSlider.slideOut();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSpacerVisibility: function() {
|
||||||
|
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let activePage = this.viewSelector.getActivePage();
|
||||||
|
this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPageEmpty: function() {
|
||||||
|
this._dashSlider.pageEmpty();
|
||||||
|
this._thumbnailsSlider.pageEmpty();
|
||||||
|
|
||||||
|
this._updateSpacerVisibility();
|
||||||
|
}
|
||||||
|
});
|
601
js/ui/panel.js
@ -14,13 +14,15 @@ const St = imports.gi.St;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Atk = imports.gi.Atk;
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
|
const Config = imports.misc.config;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Layout = imports.ui.layout;
|
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const DateMenu = imports.ui.dateMenu;
|
const RemoteMenu = imports.ui.remoteMenu;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
@ -28,7 +30,6 @@ const PANEL_ICON_SIZE = 24;
|
|||||||
|
|
||||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||||
|
|
||||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
|
||||||
const SPINNER_ANIMATION_TIME = 0.2;
|
const SPINNER_ANIMATION_TIME = 0.2;
|
||||||
|
|
||||||
// To make sure the panel corners blend nicely with the panel,
|
// To make sure the panel corners blend nicely with the panel,
|
||||||
@ -74,57 +75,6 @@ function _unpremultiply(color) {
|
|||||||
blue: blue, alpha: color.alpha });
|
blue: blue, alpha: color.alpha });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const AnimatedIcon = new Lang.Class({
|
|
||||||
Name: 'AnimatedIcon',
|
|
||||||
|
|
||||||
_init: function(name, size) {
|
|
||||||
this.actor = new St.Bin({ visible: false });
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
||||||
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibleNotify));
|
|
||||||
|
|
||||||
this._timeoutId = 0;
|
|
||||||
this._frame = 0;
|
|
||||||
this._animations = St.TextureCache.get_default().load_sliced_image (global.datadir + '/theme/' + name, size, size);
|
|
||||||
this.actor.set_child(this._animations);
|
|
||||||
},
|
|
||||||
|
|
||||||
_disconnectTimeout: function() {
|
|
||||||
if (this._timeoutId > 0) {
|
|
||||||
Mainloop.source_remove(this._timeoutId);
|
|
||||||
this._timeoutId = 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onVisibleNotify: function() {
|
|
||||||
if (this.actor.visible)
|
|
||||||
this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update));
|
|
||||||
else
|
|
||||||
this._disconnectTimeout();
|
|
||||||
},
|
|
||||||
|
|
||||||
_showFrame: function(frame) {
|
|
||||||
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
|
||||||
if (oldFrameActor)
|
|
||||||
oldFrameActor.hide();
|
|
||||||
|
|
||||||
this._frame = (frame % this._animations.get_n_children());
|
|
||||||
|
|
||||||
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
|
||||||
if (newFrameActor)
|
|
||||||
newFrameActor.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
_update: function() {
|
|
||||||
this._showFrame(this._frame + 1);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
this._disconnectTimeout();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const TextShadower = new Lang.Class({
|
const TextShadower = new Lang.Class({
|
||||||
Name: 'TextShadower',
|
Name: 'TextShadower',
|
||||||
|
|
||||||
@ -178,7 +128,7 @@ const TextShadower = new Lang.Class({
|
|||||||
let child = children[i];
|
let child = children[i];
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
// The order of the labels here is arbitrary, except
|
// The order of the labels here is arbitrary, except
|
||||||
// we know the "real" label is at the end because Clutter.Group
|
// we know the "real" label is at the end because Clutter.Actor
|
||||||
// sorts by Z order
|
// sorts by Z order
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0: // top
|
case 0: // top
|
||||||
@ -221,19 +171,20 @@ const AppMenuButton = new Lang.Class({
|
|||||||
Name: 'AppMenuButton',
|
Name: 'AppMenuButton',
|
||||||
Extends: PanelMenu.Button,
|
Extends: PanelMenu.Button,
|
||||||
|
|
||||||
_init: function(menuManager) {
|
_init: function(panel) {
|
||||||
this.parent(0.0, null, true);
|
this.parent(0.0, null, true);
|
||||||
|
|
||||||
this.actor.accessible_role = Atk.Role.MENU;
|
this.actor.accessible_role = Atk.Role.MENU;
|
||||||
|
|
||||||
this._startingApps = [];
|
this._startingApps = [];
|
||||||
|
|
||||||
this._menuManager = menuManager;
|
this._menuManager = panel.menuManager;
|
||||||
this._targetApp = null;
|
this._targetApp = null;
|
||||||
this._appMenuNotifyId = 0;
|
this._appMenuNotifyId = 0;
|
||||||
this._actionGroupNotifyId = 0;
|
this._actionGroupNotifyId = 0;
|
||||||
|
|
||||||
let bin = new St.Bin({ name: 'appMenu' });
|
let bin = new St.Bin({ name: 'appMenu' });
|
||||||
|
bin.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
this.actor.add_actor(bin);
|
this.actor.add_actor(bin);
|
||||||
|
|
||||||
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
|
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
|
||||||
@ -246,6 +197,10 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._container.connect('get-preferred-height', Lang.bind(this, this._getContentPreferredHeight));
|
this._container.connect('get-preferred-height', Lang.bind(this, this._getContentPreferredHeight));
|
||||||
this._container.connect('allocate', Lang.bind(this, this._contentAllocate));
|
this._container.connect('allocate', Lang.bind(this, this._contentAllocate));
|
||||||
|
|
||||||
|
let textureCache = St.TextureCache.get_default();
|
||||||
|
textureCache.connect('icon-theme-changed',
|
||||||
|
Lang.bind(this, this._onIconThemeChanged));
|
||||||
|
|
||||||
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
||||||
this._iconBox.connect('style-changed',
|
this._iconBox.connect('style-changed',
|
||||||
Lang.bind(this, this._onIconBoxStyleChanged));
|
Lang.bind(this, this._onIconBoxStyleChanged));
|
||||||
@ -260,26 +215,25 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._visible = !Main.overview.visible;
|
this._visible = !Main.overview.visible;
|
||||||
if (!this._visible)
|
if (!this._visible)
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||||
this.show();
|
this.show();
|
||||||
}));
|
}));
|
||||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
|
||||||
this.hide();
|
this.hide();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._stop = true;
|
this._stop = true;
|
||||||
|
|
||||||
this._spinner = new AnimatedIcon('process-working.svg',
|
this._spinner = null;
|
||||||
PANEL_ICON_SIZE);
|
|
||||||
this._container.add_actor(this._spinner.actor);
|
|
||||||
this._spinner.actor.lower_bottom();
|
|
||||||
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
let appSys = Shell.AppSystem.get_default();
|
let appSys = Shell.AppSystem.get_default();
|
||||||
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
|
this._focusAppNotifyId =
|
||||||
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
|
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
|
||||||
|
this._appStateChangedSignalId =
|
||||||
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
|
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
|
||||||
|
this._switchWorkspaceNotifyId =
|
||||||
|
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
|
||||||
|
|
||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
@ -325,12 +279,33 @@ const AppMenuButton = new Lang.Class({
|
|||||||
onCompleteScope: this });
|
onCompleteScope: this });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onStyleChanged: function(actor) {
|
||||||
|
let node = actor.get_theme_node();
|
||||||
|
let [success, icon] = node.lookup_url('spinner-image', false);
|
||||||
|
if (!success || this._spinnerIcon == icon)
|
||||||
|
return;
|
||||||
|
this._spinnerIcon = icon;
|
||||||
|
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||||
|
this._container.add_actor(this._spinner.actor);
|
||||||
|
this._spinner.actor.hide();
|
||||||
|
this._spinner.actor.lower_bottom();
|
||||||
|
},
|
||||||
|
|
||||||
_onIconBoxStyleChanged: function() {
|
_onIconBoxStyleChanged: function() {
|
||||||
let node = this._iconBox.get_theme_node();
|
let node = this._iconBox.get_theme_node();
|
||||||
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
|
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
|
||||||
this._updateIconBoxClip();
|
this._updateIconBoxClip();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onIconThemeChanged: function() {
|
||||||
|
if (this._iconBox.child == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._iconBox.child.destroy();
|
||||||
|
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||||
|
this._iconBox.set_child(icon);
|
||||||
|
},
|
||||||
|
|
||||||
_updateIconBoxClip: function() {
|
_updateIconBoxClip: function() {
|
||||||
let allocation = this._iconBox.allocation;
|
let allocation = this._iconBox.allocation;
|
||||||
if (this._iconBottomClip > 0)
|
if (this._iconBottomClip > 0)
|
||||||
@ -347,12 +322,17 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
this._stop = true;
|
this._stop = true;
|
||||||
this.actor.reactive = true;
|
this.actor.reactive = true;
|
||||||
|
|
||||||
|
if (this._spinner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
Tweener.addTween(this._spinner.actor,
|
Tweener.addTween(this._spinner.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: SPINNER_ANIMATION_TIME,
|
time: SPINNER_ANIMATION_TIME,
|
||||||
transition: "easeOutQuad",
|
transition: "easeOutQuad",
|
||||||
onCompleteScope: this,
|
onCompleteScope: this,
|
||||||
onComplete: function() {
|
onComplete: function() {
|
||||||
|
this._spinner.stop();
|
||||||
this._spinner.actor.opacity = 255;
|
this._spinner.actor.opacity = 255;
|
||||||
this._spinner.actor.hide();
|
this._spinner.actor.hide();
|
||||||
}
|
}
|
||||||
@ -362,6 +342,11 @@ const AppMenuButton = new Lang.Class({
|
|||||||
startAnimation: function() {
|
startAnimation: function() {
|
||||||
this._stop = false;
|
this._stop = false;
|
||||||
this.actor.reactive = false;
|
this.actor.reactive = false;
|
||||||
|
|
||||||
|
if (this._spinner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._spinner.play();
|
||||||
this._spinner.actor.show();
|
this._spinner.actor.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -423,6 +408,9 @@ const AppMenuButton = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this._label.actor.allocate(childBox, flags);
|
this._label.actor.allocate(childBox, flags);
|
||||||
|
|
||||||
|
if (this._spinner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (direction == Clutter.TextDirection.LTR) {
|
if (direction == Clutter.TextDirection.LTR) {
|
||||||
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
|
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
|
||||||
childBox.x2 = childBox.x1 + this._spinner.actor.width;
|
childBox.x2 = childBox.x1 + this._spinner.actor.width;
|
||||||
@ -506,14 +494,20 @@ const AppMenuButton = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetApp == this._targetApp) {
|
if (targetApp == this._targetApp) {
|
||||||
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
|
if (targetApp &&
|
||||||
|
targetApp.get_state() != Shell.AppState.STARTING &&
|
||||||
|
targetApp.get_state() != Shell.AppState.BUSY) {
|
||||||
this.stopAnimation();
|
this.stopAnimation();
|
||||||
this._maybeSetMenu();
|
this._maybeSetMenu();
|
||||||
|
} else if (targetApp &&
|
||||||
|
targetApp.get_state() == Shell.AppState.BUSY) {
|
||||||
|
this.startAnimation();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._spinner.actor.hide();
|
if (this._spinner)
|
||||||
|
this._spinner.actor.hide();
|
||||||
if (this._iconBox.child != null)
|
if (this._iconBox.child != null)
|
||||||
this._iconBox.child.destroy();
|
this._iconBox.child.destroy();
|
||||||
this._iconBox.hide();
|
this._iconBox.hide();
|
||||||
@ -540,7 +534,8 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._iconBox.set_child(icon);
|
this._iconBox.set_child(icon);
|
||||||
this._iconBox.show();
|
this._iconBox.show();
|
||||||
|
|
||||||
if (targetApp.get_state() == Shell.AppState.STARTING)
|
if (targetApp.get_state() == Shell.AppState.STARTING ||
|
||||||
|
targetApp.get_state() == Shell.AppState.BUSY)
|
||||||
this.startAnimation();
|
this.startAnimation();
|
||||||
else
|
else
|
||||||
this._maybeSetMenu();
|
this._maybeSetMenu();
|
||||||
@ -552,17 +547,23 @@ const AppMenuButton = new Lang.Class({
|
|||||||
let menu;
|
let menu;
|
||||||
|
|
||||||
if (this._targetApp.action_group && this._targetApp.menu) {
|
if (this._targetApp.action_group && this._targetApp.menu) {
|
||||||
if (this.menu instanceof PopupMenu.RemoteMenu &&
|
if (this.menu instanceof RemoteMenu.RemoteMenu &&
|
||||||
this.menu.actionGroup == this._targetApp.action_group)
|
this.menu.actionGroup == this._targetApp.action_group)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
menu = new RemoteMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||||
|
menu.connect('activate', Lang.bind(this, function() {
|
||||||
|
let win = this._targetApp.get_windows()[0];
|
||||||
|
win.check_alive(global.get_current_time());
|
||||||
|
}));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this.menu && !(this.menu instanceof PopupMenu.RemoteMenu))
|
if (this.menu.isDummyQuitMenu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// fallback to older menu
|
// fallback to older menu
|
||||||
menu = new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.TOP, 0);
|
menu = new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.TOP, 0);
|
||||||
|
menu.isDummyQuitMenu = true;
|
||||||
menu.addAction(_("Quit"), Lang.bind(this, function() {
|
menu.addAction(_("Quit"), Lang.bind(this, function() {
|
||||||
this._targetApp.request_quit();
|
this._targetApp.request_quit();
|
||||||
}));
|
}));
|
||||||
@ -570,93 +571,71 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
this.setMenu(menu);
|
this.setMenu(menu);
|
||||||
this._menuManager.addMenu(menu);
|
this._menuManager.addMenu(menu);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._appStateChangedSignalId > 0) {
|
||||||
|
let appSys = Shell.AppSystem.get_default();
|
||||||
|
appSys.disconnect(this._appStateChangedSignalId);
|
||||||
|
this._appStateChangedSignalId = 0;
|
||||||
|
}
|
||||||
|
if (this._focusAppNotifyId > 0) {
|
||||||
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
tracker.disconnect(this._focusAppNotifyId);
|
||||||
|
this._focusAppNotifyId = 0;
|
||||||
|
}
|
||||||
|
if (this._overviewHidingId > 0) {
|
||||||
|
Main.overview.disconnect(this._overviewHidingId);
|
||||||
|
this._overviewHidingId = 0;
|
||||||
|
}
|
||||||
|
if (this._overviewShowingId > 0) {
|
||||||
|
Main.overview.disconnect(this._overviewShowingId);
|
||||||
|
this._overviewShowingId = 0;
|
||||||
|
}
|
||||||
|
if (this._switchWorkspaceNotifyId > 0) {
|
||||||
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||||
|
this._switchWorkspaceNotifyId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Signals.addSignalMethods(AppMenuButton.prototype);
|
Signals.addSignalMethods(AppMenuButton.prototype);
|
||||||
|
|
||||||
// Activities button. Because everything else in the top bar is a
|
|
||||||
// PanelMenu.Button, it simplifies some things to make this be one too.
|
|
||||||
// We just hack it up to not actually have a menu attached to it.
|
|
||||||
const ActivitiesButton = new Lang.Class({
|
const ActivitiesButton = new Lang.Class({
|
||||||
Name: 'ActivitiesButton',
|
Name: 'ActivitiesButton',
|
||||||
Extends: PanelMenu.Button,
|
Extends: PanelMenu.Button,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent(0.0);
|
this.parent(0.0, null, true);
|
||||||
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
||||||
|
|
||||||
let container = new Shell.GenericContainer();
|
|
||||||
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
|
||||||
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
|
|
||||||
container.connect('allocate', Lang.bind(this, this._containerAllocate));
|
|
||||||
this.actor.add_actor(container);
|
|
||||||
this.actor.name = 'panelActivities';
|
this.actor.name = 'panelActivities';
|
||||||
|
|
||||||
/* Translators: If there is no suitable word for "Activities"
|
/* Translators: If there is no suitable word for "Activities"
|
||||||
in your language, you can use the word for "Overview". */
|
in your language, you can use the word for "Overview". */
|
||||||
this._label = new St.Label({ text: _("Activities") });
|
this._label = new St.Label({ text: _("Activities") });
|
||||||
container.add_actor(this._label);
|
this.actor.add_actor(this._label);
|
||||||
|
|
||||||
this.actor.label_actor = this._label;
|
this.actor.label_actor = this._label;
|
||||||
|
|
||||||
this._hotCorner = new Layout.HotCorner();
|
|
||||||
container.add_actor(this._hotCorner.actor);
|
|
||||||
|
|
||||||
// Hack up our menu...
|
|
||||||
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
|
|
||||||
this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
|
|
||||||
this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
|
|
||||||
|
|
||||||
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||||
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||||
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||||
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||||
this.actor.add_style_pseudo_class('overview');
|
this.actor.add_style_pseudo_class('overview');
|
||||||
this._escapeMenuGrab();
|
|
||||||
this.actor.add_accessible_state (Atk.StateType.CHECKED);
|
this.actor.add_accessible_state (Atk.StateType.CHECKED);
|
||||||
}));
|
}));
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||||
this.actor.remove_style_pseudo_class('overview');
|
this.actor.remove_style_pseudo_class('overview');
|
||||||
this._escapeMenuGrab();
|
|
||||||
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
|
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._xdndTimeOut = 0;
|
this._xdndTimeOut = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
|
|
||||||
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
|
|
||||||
},
|
|
||||||
|
|
||||||
_containerGetPreferredHeight: function(actor, forWidth, alloc) {
|
|
||||||
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
|
|
||||||
},
|
|
||||||
|
|
||||||
_containerAllocate: function(actor, box, flags) {
|
|
||||||
this._label.allocate(box, flags);
|
|
||||||
|
|
||||||
// The hot corner needs to be outside any padding/alignment
|
|
||||||
// that has been imposed on us
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
|
||||||
let hotBox = new Clutter.ActorBox();
|
|
||||||
let ok, x, y;
|
|
||||||
if (actor.get_text_direction() == Clutter.TextDirection.LTR) {
|
|
||||||
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
|
|
||||||
} else {
|
|
||||||
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
|
|
||||||
// hotCorner.actor has northeast gravity, so we don't need
|
|
||||||
// to adjust x for its width
|
|
||||||
}
|
|
||||||
|
|
||||||
hotBox.x1 = Math.round(x);
|
|
||||||
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
|
|
||||||
hotBox.y1 = Math.round(y);
|
|
||||||
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
|
|
||||||
this._hotCorner.actor.allocate(hotBox, flags);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDragOver: function(source, actor, x, y, time) {
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
if (source != Main.xdndHandler)
|
if (source != Main.xdndHandler)
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
@ -664,65 +643,36 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
if (this._xdndTimeOut != 0)
|
if (this._xdndTimeOut != 0)
|
||||||
Mainloop.source_remove(this._xdndTimeOut);
|
Mainloop.source_remove(this._xdndTimeOut);
|
||||||
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||||
Lang.bind(this, this._xdndShowOverview, actor));
|
Lang.bind(this, this._xdndToggleOverview, actor));
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_escapeMenuGrab: function() {
|
|
||||||
if (this.menu.isOpen)
|
|
||||||
this.menu.close();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCapturedEvent: function(actor, event) {
|
_onCapturedEvent: function(actor, event) {
|
||||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||||
if (!this._hotCorner.shouldToggleOverviewOnClick())
|
if (!Main.overview.shouldToggleByCornerOrButton())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMenuOpenRequest: function() {
|
|
||||||
this.menu.isOpen = true;
|
|
||||||
this.menu.emit('open-state-changed', true);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMenuCloseRequest: function() {
|
|
||||||
this.menu.isOpen = false;
|
|
||||||
this.menu.emit('open-state-changed', false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMenuToggleRequest: function() {
|
|
||||||
this.menu.isOpen = !this.menu.isOpen;
|
|
||||||
this.menu.emit('open-state-changed', this.menu.isOpen);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onButtonRelease: function() {
|
_onButtonRelease: function() {
|
||||||
if (this.menu.isOpen) {
|
Main.overview.toggle();
|
||||||
this.menu.close();
|
|
||||||
Main.overview.toggle();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyRelease: function(actor, event) {
|
_onKeyRelease: function(actor, event) {
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
||||||
if (this.menu.isOpen)
|
|
||||||
this.menu.close();
|
|
||||||
Main.overview.toggle();
|
Main.overview.toggle();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_xdndShowOverview: function(actor) {
|
_xdndToggleOverview: function(actor) {
|
||||||
let [x, y, mask] = global.get_pointer();
|
let [x, y, mask] = global.get_pointer();
|
||||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||||
|
|
||||||
if (pickedActor == this.actor) {
|
if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton())
|
||||||
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
Main.overview.toggle();
|
||||||
Main.overview.showTemporarily();
|
|
||||||
Main.overview.beginItemDrag(actor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mainloop.source_remove(this._xdndTimeOut);
|
Mainloop.source_remove(this._xdndTimeOut);
|
||||||
this._xdndTimeOut = 0;
|
this._xdndTimeOut = 0;
|
||||||
@ -732,12 +682,9 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
const PanelCorner = new Lang.Class({
|
const PanelCorner = new Lang.Class({
|
||||||
Name: 'PanelCorner',
|
Name: 'PanelCorner',
|
||||||
|
|
||||||
_init: function(box, side) {
|
_init: function(side) {
|
||||||
this._side = side;
|
this._side = side;
|
||||||
|
|
||||||
this._box = box;
|
|
||||||
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
|
|
||||||
|
|
||||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||||
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||||
@ -753,10 +700,11 @@ const PanelCorner = new Lang.Class({
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Start at the back and work backward
|
// Start at the back and work backward
|
||||||
let index = children.length - 1;
|
let index;
|
||||||
while (!children[index].visible && index >= 0)
|
for (index = children.length - 1; index >= 0; index--) {
|
||||||
index--;
|
if (children[index].visible)
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -777,10 +725,11 @@ const PanelCorner = new Lang.Class({
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Start at the front and work forward
|
// Start at the front and work forward
|
||||||
let index = 0;
|
let index;
|
||||||
while (!children[index].visible && index < children.length)
|
for (index = 0; index < children.length; index++) {
|
||||||
index++;
|
if (children[index].visible)
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (index == children.length)
|
if (index == children.length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -791,12 +740,12 @@ const PanelCorner = new Lang.Class({
|
|||||||
return children[index];
|
return children[index];
|
||||||
},
|
},
|
||||||
|
|
||||||
_boxStyleChanged: function() {
|
setStyleParent: function(box) {
|
||||||
let side = this._side;
|
let side = this._side;
|
||||||
|
|
||||||
let rtlAwareContainer = this._box instanceof St.BoxLayout;
|
let rtlAwareContainer = box instanceof St.BoxLayout;
|
||||||
if (rtlAwareContainer &&
|
if (rtlAwareContainer &&
|
||||||
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
|
box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||||
if (this._side == St.Side.LEFT)
|
if (this._side == St.Side.LEFT)
|
||||||
side = St.Side.RIGHT;
|
side = St.Side.RIGHT;
|
||||||
else if (this._side == St.Side.RIGHT)
|
else if (this._side == St.Side.RIGHT)
|
||||||
@ -805,9 +754,9 @@ const PanelCorner = new Lang.Class({
|
|||||||
|
|
||||||
let button;
|
let button;
|
||||||
if (side == St.Side.LEFT)
|
if (side == St.Side.LEFT)
|
||||||
button = this._findLeftmostButton(this._box);
|
button = this._findLeftmostButton(box);
|
||||||
else if (side == St.Side.RIGHT)
|
else if (side == St.Side.RIGHT)
|
||||||
button = this._findRightmostButton(this._box);
|
button = this._findRightmostButton(box);
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
if (this._button && this._buttonStyleChangedSignalId) {
|
if (this._button && this._buttonStyleChangedSignalId) {
|
||||||
@ -834,7 +783,7 @@ const PanelCorner = new Lang.Class({
|
|||||||
|
|
||||||
// The corner doesn't support theme transitions, so override
|
// The corner doesn't support theme transitions, so override
|
||||||
// the .panel-button default
|
// the .panel-button default
|
||||||
button.style = 'transition-duration: 0';
|
button.style = 'transition-duration: 0ms';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -847,10 +796,13 @@ const PanelCorner = new Lang.Class({
|
|||||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||||
let borderColor = node.get_color('-panel-corner-border-color');
|
let borderColor = node.get_color('-panel-corner-border-color');
|
||||||
|
|
||||||
|
let overlap = borderColor.alpha != 0;
|
||||||
|
let offsetY = overlap ? 0 : borderWidth;
|
||||||
|
|
||||||
let cr = this.actor.get_context();
|
let cr = this.actor.get_context();
|
||||||
cr.setOperator(Cairo.Operator.SOURCE);
|
cr.setOperator(Cairo.Operator.SOURCE);
|
||||||
|
|
||||||
cr.moveTo(0, 0);
|
cr.moveTo(0, offsetY);
|
||||||
if (this._side == St.Side.LEFT)
|
if (this._side == St.Side.LEFT)
|
||||||
cr.arc(cornerRadius,
|
cr.arc(cornerRadius,
|
||||||
borderWidth + cornerRadius,
|
borderWidth + cornerRadius,
|
||||||
@ -859,7 +811,7 @@ const PanelCorner = new Lang.Class({
|
|||||||
cr.arc(0,
|
cr.arc(0,
|
||||||
borderWidth + cornerRadius,
|
borderWidth + cornerRadius,
|
||||||
cornerRadius, 3 * Math.PI / 2, 2 * Math.PI);
|
cornerRadius, 3 * Math.PI / 2, 2 * Math.PI);
|
||||||
cr.lineTo(cornerRadius, 0);
|
cr.lineTo(cornerRadius, offsetY);
|
||||||
cr.closePath();
|
cr.closePath();
|
||||||
|
|
||||||
let savedPath = cr.copyPath();
|
let savedPath = cr.copyPath();
|
||||||
@ -869,14 +821,18 @@ const PanelCorner = new Lang.Class({
|
|||||||
Clutter.cairo_set_source_color(cr, over);
|
Clutter.cairo_set_source_color(cr, over);
|
||||||
cr.fill();
|
cr.fill();
|
||||||
|
|
||||||
let offset = borderWidth;
|
if (overlap) {
|
||||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
let offset = borderWidth;
|
||||||
|
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||||
|
|
||||||
cr.save();
|
cr.save();
|
||||||
cr.translate(xOffsetDirection * offset, - offset);
|
cr.translate(xOffsetDirection * offset, - offset);
|
||||||
cr.appendPath(savedPath);
|
cr.appendPath(savedPath);
|
||||||
cr.fill();
|
cr.fill();
|
||||||
cr.restore();
|
cr.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.$dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
_styleChanged: function() {
|
_styleChanged: function() {
|
||||||
@ -890,6 +846,30 @@ const PanelCorner = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||||
|
'activities': ActivitiesButton,
|
||||||
|
'appMenu': AppMenuButton,
|
||||||
|
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
||||||
|
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||||
|
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
|
||||||
|
'volume': imports.ui.status.volume.Indicator,
|
||||||
|
'battery': imports.ui.status.power.Indicator,
|
||||||
|
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||||
|
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||||
|
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||||
|
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Config.HAVE_BLUETOOTH)
|
||||||
|
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
|
||||||
|
imports.ui.status.bluetooth.Indicator;
|
||||||
|
|
||||||
|
try {
|
||||||
|
PANEL_ITEM_IMPLEMENTATIONS['network'] =
|
||||||
|
imports.ui.status.network.NMApplet;
|
||||||
|
} catch(e) {
|
||||||
|
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||||
|
}
|
||||||
|
|
||||||
const Panel = new Lang.Class({
|
const Panel = new Lang.Class({
|
||||||
Name: 'Panel',
|
Name: 'Panel',
|
||||||
@ -899,16 +879,11 @@ const Panel = new Lang.Class({
|
|||||||
reactive: true });
|
reactive: true });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
|
||||||
this._statusArea = {};
|
this._sessionStyle = null;
|
||||||
|
|
||||||
Main.overview.connect('shown', Lang.bind(this, function () {
|
this.statusArea = {};
|
||||||
this.actor.add_style_class_name('in-overview');
|
|
||||||
}));
|
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
|
||||||
this.actor.remove_style_class_name('in-overview');
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._menus = new PopupMenu.PopupMenuManager(this);
|
this.menuManager = new PopupMenu.PopupMenuManager(this, { keybindingMode: Shell.KeyBindingMode.TOPBAR_POPUP });
|
||||||
|
|
||||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||||
this.actor.add_actor(this._leftBox);
|
this.actor.add_actor(this._leftBox);
|
||||||
@ -917,17 +892,10 @@ const Panel = new Lang.Class({
|
|||||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||||
this.actor.add_actor(this._rightBox);
|
this.actor.add_actor(this._rightBox);
|
||||||
|
|
||||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
|
||||||
else
|
|
||||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
|
||||||
|
|
||||||
this.actor.add_actor(this._leftCorner.actor);
|
this.actor.add_actor(this._leftCorner.actor);
|
||||||
|
|
||||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||||
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
|
||||||
else
|
|
||||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
|
||||||
this.actor.add_actor(this._rightCorner.actor);
|
this.actor.add_actor(this._rightCorner.actor);
|
||||||
|
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
@ -935,34 +903,19 @@ const Panel = new Lang.Class({
|
|||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||||
|
|
||||||
/* Button on the left side of the panel. */
|
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||||
if (Main.sessionMode.hasOverview) {
|
this.actor.add_style_pseudo_class('overview');
|
||||||
this._activitiesButton = new ActivitiesButton();
|
}));
|
||||||
this._activities = this._activitiesButton.actor;
|
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||||
this._leftBox.add(this._activities);
|
this.actor.remove_style_pseudo_class('overview');
|
||||||
|
}));
|
||||||
// The activities button has a pretend menu, so as to integrate
|
|
||||||
// more cleanly with the rest of the panel
|
|
||||||
this._menus.addMenu(this._activitiesButton.menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Main.sessionMode.hasAppMenu) {
|
|
||||||
this._appMenu = new AppMenuButton(this._menus);
|
|
||||||
this._leftBox.add(this._appMenu.actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* center */
|
|
||||||
this._dateMenu = new DateMenu.DateMenuButton();
|
|
||||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
|
||||||
this._menus.addMenu(this._dateMenu.menu);
|
|
||||||
|
|
||||||
/* right */
|
|
||||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
|
||||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
|
||||||
|
|
||||||
Main.layoutManager.panelBox.add(this.actor);
|
Main.layoutManager.panelBox.add(this.actor);
|
||||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
||||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
|
||||||
|
this._updatePanel();
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
@ -1022,16 +975,19 @@ const Panel = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this._rightBox.allocate(childBox, flags);
|
this._rightBox.allocate(childBox, flags);
|
||||||
|
|
||||||
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
let cornerMinWidth, cornerMinHeight;
|
||||||
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
|
let cornerWidth, cornerHeight;
|
||||||
|
|
||||||
|
[cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
||||||
|
[cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
|
||||||
childBox.x1 = 0;
|
childBox.x1 = 0;
|
||||||
childBox.x2 = cornerWidth;
|
childBox.x2 = cornerWidth;
|
||||||
childBox.y1 = allocHeight;
|
childBox.y1 = allocHeight;
|
||||||
childBox.y2 = allocHeight + cornerHeight;
|
childBox.y2 = allocHeight + cornerHeight;
|
||||||
this._leftCorner.actor.allocate(childBox, flags);
|
this._leftCorner.actor.allocate(childBox, flags);
|
||||||
|
|
||||||
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
[cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
||||||
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
|
[cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
|
||||||
childBox.x1 = allocWidth - cornerWidth;
|
childBox.x1 = allocWidth - cornerWidth;
|
||||||
childBox.x2 = allocWidth;
|
childBox.x2 = allocWidth;
|
||||||
childBox.y1 = allocHeight;
|
childBox.y1 = allocHeight;
|
||||||
@ -1040,6 +996,9 @@ const Panel = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onButtonPress: function(actor, event) {
|
_onButtonPress: function(actor, event) {
|
||||||
|
if (Main.modalCount > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (event.get_source() != actor)
|
if (event.get_source() != actor)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1078,89 +1037,143 @@ const Panel = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
openAppMenu: function() {
|
toggleAppMenu: function() {
|
||||||
let menu = this._appMenu.menu;
|
let indicator = this.statusArea.appMenu;
|
||||||
if (!this._appMenu.actor.reactive || menu.isOpen)
|
if (!indicator) // appMenu not supported by current session mode
|
||||||
return;
|
return;
|
||||||
|
|
||||||
menu.open();
|
let menu = indicator.menu;
|
||||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
if (!indicator.actor.reactive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
menu.toggle();
|
||||||
|
if (menu.isOpen)
|
||||||
|
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
startStatusArea: function() {
|
set boxOpacity(value) {
|
||||||
for (let i = 0; i < Main.sessionMode.statusArea.order.length; i++) {
|
let isReactive = value > 0;
|
||||||
let role = Main.sessionMode.statusArea.order[i];
|
|
||||||
let constructor = Main.sessionMode.statusArea.implementation[role];
|
this._leftBox.opacity = value;
|
||||||
|
this._leftBox.reactive = isReactive;
|
||||||
|
this._centerBox.opacity = value;
|
||||||
|
this._centerBox.reactive = isReactive;
|
||||||
|
this._rightBox.opacity = value;
|
||||||
|
this._rightBox.reactive = isReactive;
|
||||||
|
},
|
||||||
|
|
||||||
|
get boxOpacity() {
|
||||||
|
return this._leftBox.opacity;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePanel: function() {
|
||||||
|
let panel = Main.sessionMode.panel;
|
||||||
|
this._hideIndicators();
|
||||||
|
this._updateBox(panel.left, this._leftBox);
|
||||||
|
this._updateBox(panel.center, this._centerBox);
|
||||||
|
this._updateBox(panel.right, this._rightBox);
|
||||||
|
|
||||||
|
if (this._sessionStyle)
|
||||||
|
this._removeStyleClassName(this._sessionStyle);
|
||||||
|
|
||||||
|
this._sessionStyle = Main.sessionMode.panelStyle;
|
||||||
|
if (this._sessionStyle)
|
||||||
|
this._addStyleClassName(this._sessionStyle);
|
||||||
|
|
||||||
|
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||||
|
this._leftCorner.setStyleParent(this._rightBox);
|
||||||
|
this._rightCorner.setStyleParent(this._leftBox);
|
||||||
|
} else {
|
||||||
|
this._leftCorner.setStyleParent(this._leftBox);
|
||||||
|
this._rightCorner.setStyleParent(this._rightBox);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideIndicators: function() {
|
||||||
|
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
|
||||||
|
let indicator = this.statusArea[role];
|
||||||
|
if (!indicator)
|
||||||
|
continue;
|
||||||
|
if (indicator.menu)
|
||||||
|
indicator.menu.close();
|
||||||
|
indicator.container.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureIndicator: function(role) {
|
||||||
|
let indicator = this.statusArea[role];
|
||||||
|
if (!indicator) {
|
||||||
|
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||||
if (!constructor) {
|
if (!constructor) {
|
||||||
// This icon is not implemented (this is a bug)
|
// This icon is not implemented (this is a bug)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
indicator = new constructor(this);
|
||||||
|
this.statusArea[role] = indicator;
|
||||||
|
}
|
||||||
|
return indicator;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBox: function(elements, box) {
|
||||||
|
let nChildren = box.get_n_children();
|
||||||
|
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
let role = elements[i];
|
||||||
|
let indicator = this._ensureIndicator(role);
|
||||||
|
if (indicator == null)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
let indicator = new constructor();
|
this._addToPanelBox(role, indicator, i + nChildren, box);
|
||||||
this.addToStatusArea(role, indicator, i);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_insertStatusItem: function(actor, position) {
|
_addToPanelBox: function(role, indicator, position, box) {
|
||||||
let children = this._rightBox.get_children();
|
let container = indicator.container;
|
||||||
let i;
|
container.show();
|
||||||
for (i = children.length - 1; i >= 0; i--) {
|
|
||||||
let rolePosition = children[i]._rolePosition;
|
let parent = container.get_parent();
|
||||||
if (position > rolePosition) {
|
if (parent)
|
||||||
this._rightBox.insert_child_at_index(actor, i + 1);
|
parent.remove_actor(container);
|
||||||
break;
|
|
||||||
}
|
box.insert_child_at_index(container, position);
|
||||||
}
|
if (indicator.menu)
|
||||||
if (i == -1) {
|
this.menuManager.addMenu(indicator.menu);
|
||||||
// If we didn't find a position, we must be first
|
this.statusArea[role] = indicator;
|
||||||
this._rightBox.insert_child_at_index(actor, 0);
|
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||||
}
|
delete this.statusArea[role];
|
||||||
actor._rolePosition = position;
|
emitter.disconnect(destroyId);
|
||||||
|
container.destroy();
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
addToStatusArea: function(role, indicator, position) {
|
addToStatusArea: function(role, indicator, position, box) {
|
||||||
if (this._statusArea[role])
|
if (this.statusArea[role])
|
||||||
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
|
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
|
||||||
|
|
||||||
if (!(indicator instanceof PanelMenu.Button))
|
if (!(indicator instanceof PanelMenu.Button))
|
||||||
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
||||||
|
|
||||||
if (!position)
|
position = position || 0;
|
||||||
position = 0;
|
let boxes = {
|
||||||
this._insertStatusItem(indicator.actor, position);
|
left: this._leftBox,
|
||||||
if (indicator.menu)
|
center: this._centerBox,
|
||||||
this._menus.addMenu(indicator.menu);
|
right: this._rightBox
|
||||||
|
};
|
||||||
this._statusArea[role] = indicator;
|
let boxContainer = boxes[box] || this._rightBox;
|
||||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
this.statusArea[role] = indicator;
|
||||||
this._statusArea[role] = null;
|
this._addToPanelBox(role, indicator, position, boxContainer);
|
||||||
emitter.disconnect(destroyId);
|
|
||||||
}));
|
|
||||||
|
|
||||||
return indicator;
|
return indicator;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon, role) {
|
_addStyleClassName: function(className) {
|
||||||
if (Main.sessionMode.statusArea.implementation[role]) {
|
this.actor.add_style_class_name(className);
|
||||||
// This icon is legacy, and replaced by a Shell version
|
this._rightCorner.actor.add_style_class_name(className);
|
||||||
// Hide it
|
this._leftCorner.actor.add_style_class_name(className);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Main.sessionMode.statusArea.order.indexOf(role) == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
icon.height = PANEL_ICON_SIZE;
|
|
||||||
let buttonBox = new PanelMenu.ButtonBox();
|
|
||||||
let box = buttonBox.actor;
|
|
||||||
box.add_actor(icon);
|
|
||||||
|
|
||||||
this._insertStatusItem(box, Main.sessionMode.statusArea.order.indexOf(role));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconRemoved: function(o, icon) {
|
_removeStyleClassName: function(className) {
|
||||||
let box = icon.get_parent();
|
this.actor.remove_style_class_name(className);
|
||||||
if (box && box._delegate instanceof PanelMenu.ButtonBox)
|
this._rightCorner.actor.remove_style_class_name(className);
|
||||||
box.destroy();
|
this._leftCorner.actor.remove_style_class_name(className);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
@ -20,6 +21,10 @@ const ButtonBox = new Lang.Class({
|
|||||||
this.actor = new Shell.GenericContainer(params);
|
this.actor = new Shell.GenericContainer(params);
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
|
||||||
|
this.container = new St.Bin({ y_fill: true,
|
||||||
|
x_fill: true,
|
||||||
|
child: this.actor });
|
||||||
|
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
@ -107,13 +112,19 @@ const Button = new Lang.Class({
|
|||||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||||
|
|
||||||
if (dontCreateMenu)
|
if (dontCreateMenu)
|
||||||
this.menu = null;
|
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
||||||
else
|
else
|
||||||
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
||||||
|
|
||||||
this.setName(nameText);
|
this.setName(nameText);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setSensitive: function(sensitive) {
|
||||||
|
this.actor.reactive = sensitive;
|
||||||
|
this.actor.can_focus = sensitive;
|
||||||
|
this.actor.track_hover = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
setName: function(text) {
|
setName: function(text) {
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
// This is the easiest way to provide a accessible name to
|
// This is the easiest way to provide a accessible name to
|
||||||
@ -173,10 +184,12 @@ const Button = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onMenuKeyPress: function(actor, event) {
|
_onMenuKeyPress: function(actor, event) {
|
||||||
|
if (global.focus_manager.navigate_from_event(event))
|
||||||
|
return true;
|
||||||
|
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||||
let focusManager = St.FocusManager.get_for_stage(global.stage);
|
let group = global.focus_manager.get_group(this.actor);
|
||||||
let group = focusManager.get_group(this.actor);
|
|
||||||
if (group) {
|
if (group) {
|
||||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||||
group.navigate_focus(this.actor, direction, false);
|
group.navigate_focus(this.actor, direction, false);
|
||||||
@ -195,16 +208,15 @@ const Button = new Lang.Class({
|
|||||||
// Setting the max-height won't do any good if the minimum height of the
|
// Setting the max-height won't do any good if the minimum height of the
|
||||||
// menu is higher then the screen; it's useful if part of the menu is
|
// menu is higher then the screen; it's useful if part of the menu is
|
||||||
// scrollable so the minimum height is smaller than the natural height
|
// scrollable so the minimum height is smaller than the natural height
|
||||||
let monitor = Main.layoutManager.primaryMonitor;
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||||
this.menu.actor.style = ('max-height: ' +
|
this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;');
|
||||||
Math.round(monitor.height - Main.panel.actor.height) +
|
|
||||||
'px;');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this.actor._delegate = null;
|
this.actor._delegate = null;
|
||||||
|
|
||||||
this.menu.destroy();
|
if (this.menu)
|
||||||
|
this.menu.destroy();
|
||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
|
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
@ -224,19 +236,39 @@ const SystemStatusButton = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(iconName, nameText) {
|
_init: function(iconName, nameText) {
|
||||||
this.parent(0.0, nameText);
|
this.parent(0.0, nameText);
|
||||||
|
|
||||||
this._iconActor = new St.Icon({ icon_name: iconName,
|
|
||||||
icon_type: St.IconType.SYMBOLIC,
|
|
||||||
style_class: 'system-status-icon' });
|
|
||||||
this.actor.add_actor(this._iconActor);
|
|
||||||
this.actor.add_style_class_name('panel-status-button');
|
this.actor.add_style_class_name('panel-status-button');
|
||||||
|
|
||||||
|
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||||
|
this.actor.add_actor(this._box);
|
||||||
|
|
||||||
|
if (iconName)
|
||||||
|
this.setIcon(iconName);
|
||||||
|
},
|
||||||
|
|
||||||
|
get icons() {
|
||||||
|
return this._box.get_children();
|
||||||
|
},
|
||||||
|
|
||||||
|
addIcon: function(gicon) {
|
||||||
|
let icon = new St.Icon({ gicon: gicon,
|
||||||
|
style_class: 'system-status-icon' });
|
||||||
|
this._box.add_actor(icon);
|
||||||
|
|
||||||
|
this.emit('icons-changed');
|
||||||
|
|
||||||
|
return icon;
|
||||||
},
|
},
|
||||||
|
|
||||||
setIcon: function(iconName) {
|
setIcon: function(iconName) {
|
||||||
this._iconActor.icon_name = iconName;
|
if (!this.mainIcon)
|
||||||
|
this.mainIcon = this.addIcon(null);
|
||||||
|
this.mainIcon.icon_name = iconName;
|
||||||
},
|
},
|
||||||
|
|
||||||
setGIcon: function(gicon) {
|
setGIcon: function(gicon) {
|
||||||
this._iconActor.gicon = gicon;
|
if (this.mainIcon)
|
||||||
|
this.mainIcon.gicon = gicon;
|
||||||
|
else
|
||||||
|
this.mainIcon = this.addIcon(gicon);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,430 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const GLib = imports.gi.GLib;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const Signals = imports.signals;
|
|
||||||
const St = imports.gi.St;
|
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const Params = imports.misc.params;
|
|
||||||
const Search = imports.ui.search;
|
|
||||||
const Util = imports.misc.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a place object, which is most normally a bookmark entry,
|
|
||||||
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
|
|
||||||
*
|
|
||||||
* @name: String title
|
|
||||||
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
|
|
||||||
* @launch: A JavaScript callback to launch the entry
|
|
||||||
*/
|
|
||||||
const PlaceInfo = new Lang.Class({
|
|
||||||
Name: 'PlaceInfo',
|
|
||||||
|
|
||||||
_init: function(id, name, iconFactory, launch) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this._lowerName = name.toLowerCase();
|
|
||||||
this.iconFactory = iconFactory;
|
|
||||||
this.launch = launch;
|
|
||||||
},
|
|
||||||
|
|
||||||
matchTerms: function(terms) {
|
|
||||||
let mtype = Search.MatchType.NONE;
|
|
||||||
for (let i = 0; i < terms.length; i++) {
|
|
||||||
let term = terms[i];
|
|
||||||
let idx = this._lowerName.indexOf(term);
|
|
||||||
if (idx == 0) {
|
|
||||||
mtype = Search.MatchType.PREFIX;
|
|
||||||
} else if (idx > 0) {
|
|
||||||
if (mtype == Search.MatchType.NONE)
|
|
||||||
mtype = Search.MatchType.SUBSTRING;
|
|
||||||
} else {
|
|
||||||
return Search.MatchType.NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mtype;
|
|
||||||
},
|
|
||||||
|
|
||||||
isRemovable: function() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
|
||||||
function _makeLaunchContext(params)
|
|
||||||
{
|
|
||||||
params = Params.parse(params, { workspace: -1,
|
|
||||||
timestamp: 0 });
|
|
||||||
|
|
||||||
let launchContext = global.create_app_launch_context();
|
|
||||||
if (params.workspace != -1)
|
|
||||||
launchContext.set_desktop(params.workspace);
|
|
||||||
if (params.timestamp != 0)
|
|
||||||
launchContext.set_timestamp(params.timestamp);
|
|
||||||
|
|
||||||
return launchContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlaceDeviceInfo = new Lang.Class({
|
|
||||||
Name: 'PlaceDeviceInfo',
|
|
||||||
Extends: PlaceInfo,
|
|
||||||
|
|
||||||
_init: function(mount) {
|
|
||||||
this._mount = mount;
|
|
||||||
this.name = mount.get_name();
|
|
||||||
this._lowerName = this.name.toLowerCase();
|
|
||||||
this.id = 'mount:' + mount.get_root().get_uri();
|
|
||||||
},
|
|
||||||
|
|
||||||
iconFactory: function(size) {
|
|
||||||
let icon = this._mount.get_icon();
|
|
||||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
|
||||||
},
|
|
||||||
|
|
||||||
launch: function(params) {
|
|
||||||
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
|
||||||
_makeLaunchContext(params));
|
|
||||||
},
|
|
||||||
|
|
||||||
isRemovable: function() {
|
|
||||||
return this._mount.can_unmount();
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function() {
|
|
||||||
if (!this.isRemovable())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._mount.can_eject())
|
|
||||||
this._mount.eject(0, null, Lang.bind(this, this._removeFinish));
|
|
||||||
else
|
|
||||||
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish));
|
|
||||||
},
|
|
||||||
|
|
||||||
_removeFinish: function(o, res, data) {
|
|
||||||
try {
|
|
||||||
if (this._mount.can_eject())
|
|
||||||
this._mount.eject_finish(res);
|
|
||||||
else
|
|
||||||
this._mount.unmount_finish(res);
|
|
||||||
} catch (e) {
|
|
||||||
let message = _("Failed to unmount '%s'").format(o.get_name());
|
|
||||||
Main.overview.setMessage(message,
|
|
||||||
Lang.bind(this, this.remove),
|
|
||||||
_("Retry"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const PlacesManager = new Lang.Class({
|
|
||||||
Name: 'PlacesManager',
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this._defaultPlaces = [];
|
|
||||||
this._mounts = [];
|
|
||||||
this._bookmarks = [];
|
|
||||||
|
|
||||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
|
||||||
let homeUri = homeFile.get_uri();
|
|
||||||
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
|
||||||
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
|
||||||
this._home = new PlaceInfo('special:home', homeLabel,
|
|
||||||
function(size) {
|
|
||||||
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
|
|
||||||
},
|
|
||||||
function(params) {
|
|
||||||
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
|
|
||||||
});
|
|
||||||
|
|
||||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
|
||||||
let desktopFile = Gio.file_new_for_path (desktopPath);
|
|
||||||
let desktopUri = desktopFile.get_uri();
|
|
||||||
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
|
||||||
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
|
||||||
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
|
|
||||||
function(size) {
|
|
||||||
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
|
|
||||||
},
|
|
||||||
function(params) {
|
|
||||||
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
|
|
||||||
});
|
|
||||||
|
|
||||||
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
|
||||||
function (size) {
|
|
||||||
// do NOT use St.Icon here, it crashes the shell
|
|
||||||
// see wanda.js for details
|
|
||||||
return St.TextureCache.get_default().load_icon_name(null,
|
|
||||||
'applications-internet',
|
|
||||||
St.IconType.FULLCOLOR,
|
|
||||||
size);
|
|
||||||
},
|
|
||||||
function (params) {
|
|
||||||
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
|
|
||||||
// launch it with the workspace from params. It's probably pretty rare
|
|
||||||
// and odd to drag this place onto a workspace in any case
|
|
||||||
|
|
||||||
Util.spawn(['nautilus-connect-server']);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._defaultPlaces.push(this._home);
|
|
||||||
this._defaultPlaces.push(this._desktopMenu);
|
|
||||||
this._defaultPlaces.push(this._connect);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
|
||||||
*/
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
|
||||||
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._updateDevices();
|
|
||||||
|
|
||||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']);
|
|
||||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
|
||||||
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
|
||||||
this._bookmarkTimeoutId = 0;
|
|
||||||
this._monitor.connect('changed', Lang.bind(this, function () {
|
|
||||||
if (this._bookmarkTimeoutId > 0)
|
|
||||||
return;
|
|
||||||
/* Defensive event compression */
|
|
||||||
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
|
||||||
this._bookmarkTimeoutId = 0;
|
|
||||||
this._reloadBookmarks();
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._reloadBookmarks();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDevices: function() {
|
|
||||||
this._mounts = [];
|
|
||||||
|
|
||||||
/* first go through all connected drives */
|
|
||||||
let drives = this._volumeMonitor.get_connected_drives();
|
|
||||||
for (let i = 0; i < drives.length; i++) {
|
|
||||||
let volumes = drives[i].get_volumes();
|
|
||||||
for(let j = 0; j < volumes.length; j++) {
|
|
||||||
let mount = volumes[j].get_mount();
|
|
||||||
if(mount != null) {
|
|
||||||
this._addMount(mount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add all volumes that is not associated with a drive */
|
|
||||||
let volumes = this._volumeMonitor.get_volumes();
|
|
||||||
for(let i = 0; i < volumes.length; i++) {
|
|
||||||
if(volumes[i].get_drive() != null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let mount = volumes[i].get_mount();
|
|
||||||
if(mount != null) {
|
|
||||||
this._addMount(mount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
|
||||||
let mounts = this._volumeMonitor.get_mounts();
|
|
||||||
for(let i = 0; i < mounts.length; i++) {
|
|
||||||
if(mounts[i].is_shadowed())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(mounts[i].get_volume())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
this._addMount(mounts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We emit two signals, one for a generic 'all places' update
|
|
||||||
* and the other for one specific to mounts. We do this because
|
|
||||||
* clients like PlaceDisplay may only care about places in general
|
|
||||||
* being updated while clients like DashPlaceDisplay care which
|
|
||||||
* specific type of place got updated.
|
|
||||||
*/
|
|
||||||
this.emit('mounts-updated');
|
|
||||||
this.emit('places-updated');
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
_reloadBookmarks: function() {
|
|
||||||
|
|
||||||
this._bookmarks = [];
|
|
||||||
|
|
||||||
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
|
||||||
return;
|
|
||||||
|
|
||||||
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
|
|
||||||
|
|
||||||
let bookmarks = bookmarksContent.split('\n');
|
|
||||||
|
|
||||||
let bookmarksToLabel = {};
|
|
||||||
let bookmarksOrder = [];
|
|
||||||
for (let i = 0; i < bookmarks.length; i++) {
|
|
||||||
let bookmarkLine = bookmarks[i];
|
|
||||||
let components = bookmarkLine.split(' ');
|
|
||||||
let bookmark = components[0];
|
|
||||||
if (bookmark in bookmarksToLabel)
|
|
||||||
continue;
|
|
||||||
let label = null;
|
|
||||||
if (components.length > 1)
|
|
||||||
label = components.slice(1).join(' ');
|
|
||||||
bookmarksToLabel[bookmark] = label;
|
|
||||||
bookmarksOrder.push(bookmark);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < bookmarksOrder.length; i++) {
|
|
||||||
let bookmark = bookmarksOrder[i];
|
|
||||||
let label = bookmarksToLabel[bookmark];
|
|
||||||
let file = Gio.file_new_for_uri(bookmark);
|
|
||||||
if (!file.query_exists(null))
|
|
||||||
continue;
|
|
||||||
if (label == null)
|
|
||||||
label = Shell.util_get_label_for_uri(bookmark);
|
|
||||||
if (label == null)
|
|
||||||
continue;
|
|
||||||
let icon = Shell.util_get_icon_for_uri(bookmark);
|
|
||||||
|
|
||||||
let item = new PlaceInfo('bookmark:' + bookmark, label,
|
|
||||||
function(size) {
|
|
||||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
|
||||||
},
|
|
||||||
function(params) {
|
|
||||||
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
|
|
||||||
});
|
|
||||||
this._bookmarks.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See comment in _updateDevices for explanation why there are two signals. */
|
|
||||||
this.emit('bookmarks-updated');
|
|
||||||
this.emit('places-updated');
|
|
||||||
},
|
|
||||||
|
|
||||||
_addMount: function(mount) {
|
|
||||||
let devItem = new PlaceDeviceInfo(mount);
|
|
||||||
this._mounts.push(devItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
getAllPlaces: function () {
|
|
||||||
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultPlaces: function () {
|
|
||||||
return this._defaultPlaces;
|
|
||||||
},
|
|
||||||
|
|
||||||
getBookmarks: function () {
|
|
||||||
return this._bookmarks;
|
|
||||||
},
|
|
||||||
|
|
||||||
getMounts: function () {
|
|
||||||
return this._mounts;
|
|
||||||
},
|
|
||||||
|
|
||||||
_lookupIndexById: function(sourceArray, id) {
|
|
||||||
for (let i = 0; i < sourceArray.length; i++) {
|
|
||||||
let place = sourceArray[i];
|
|
||||||
if (place.id == id)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
lookupPlaceById: function(id) {
|
|
||||||
let colonIdx = id.indexOf(':');
|
|
||||||
let type = id.substring(0, colonIdx);
|
|
||||||
let sourceArray = null;
|
|
||||||
if (type == 'special')
|
|
||||||
sourceArray = this._defaultPlaces;
|
|
||||||
else if (type == 'mount')
|
|
||||||
sourceArray = this._mounts;
|
|
||||||
else if (type == 'bookmark')
|
|
||||||
sourceArray = this._bookmarks;
|
|
||||||
return sourceArray[this._lookupIndexById(sourceArray, id)];
|
|
||||||
},
|
|
||||||
|
|
||||||
_removeById: function(sourceArray, id) {
|
|
||||||
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Signals.addSignalMethods(PlacesManager.prototype);
|
|
||||||
|
|
||||||
const PlaceSearchProvider = new Lang.Class({
|
|
||||||
Name: 'PlaceSearchProvider',
|
|
||||||
Extends: Search.SearchProvider,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.parent(_("PLACES & DEVICES"));
|
|
||||||
this.placesManager = new PlacesManager();
|
|
||||||
},
|
|
||||||
|
|
||||||
getResultMetas: function(resultIds, callback) {
|
|
||||||
let metas = [];
|
|
||||||
for (let i = 0; i < resultIds.length; i++) {
|
|
||||||
let placeInfo = this.placesManager.lookupPlaceById(resultIds[i]);
|
|
||||||
if (!placeInfo)
|
|
||||||
metas.push(null);
|
|
||||||
else
|
|
||||||
metas.push({ 'id': resultIds[i],
|
|
||||||
'name': placeInfo.name,
|
|
||||||
'createIcon': function(size) {
|
|
||||||
return placeInfo.iconFactory(size);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
callback(metas);
|
|
||||||
},
|
|
||||||
|
|
||||||
activateResult: function(id, params) {
|
|
||||||
let placeInfo = this.placesManager.lookupPlaceById(id);
|
|
||||||
placeInfo.launch(params);
|
|
||||||
},
|
|
||||||
|
|
||||||
_compareResultMeta: function (idA, idB) {
|
|
||||||
let infoA = this.placesManager.lookupPlaceById(idA);
|
|
||||||
let infoB = this.placesManager.lookupPlaceById(idB);
|
|
||||||
return infoA.name.localeCompare(infoB.name);
|
|
||||||
},
|
|
||||||
|
|
||||||
_searchPlaces: function(places, terms) {
|
|
||||||
let prefixResults = [];
|
|
||||||
let substringResults = [];
|
|
||||||
|
|
||||||
terms = terms.map(String.toLowerCase);
|
|
||||||
|
|
||||||
for (let i = 0; i < places.length; i++) {
|
|
||||||
let place = places[i];
|
|
||||||
let mtype = place.matchTerms(terms);
|
|
||||||
if (mtype == Search.MatchType.PREFIX)
|
|
||||||
prefixResults.push(place.id);
|
|
||||||
else if (mtype == Search.MatchType.SUBSTRING)
|
|
||||||
substringResults.push(place.id);
|
|
||||||
}
|
|
||||||
prefixResults.sort(Lang.bind(this, this._compareResultMeta));
|
|
||||||
substringResults.sort(Lang.bind(this, this._compareResultMeta));
|
|
||||||
|
|
||||||
this.searchSystem.pushResults(this, prefixResults.concat(substringResults));
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
|
||||||
let places = this.placesManager.getAllPlaces();
|
|
||||||
this._searchPlaces(places, terms);
|
|
||||||
},
|
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
|
||||||
let places = previousResults.map(Lang.bind(this, function(id) {
|
|
||||||
return this.placesManager.lookupPlaceById(id);
|
|
||||||
}));
|
|
||||||
this._searchPlaces(places, terms);
|
|
||||||
}
|
|
||||||
});
|
|
130
js/ui/pointerWatcher.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
// We stop polling if the user is idle for more than this amount of time
|
||||||
|
const IDLE_TIME = 1000;
|
||||||
|
|
||||||
|
// This file implements a reasonably efficient system for tracking the position
|
||||||
|
// of the mouse pointer. We simply query the pointer from the X server in a loop,
|
||||||
|
// but we turn off the polling when the user is idle.
|
||||||
|
|
||||||
|
let _pointerWatcher = null;
|
||||||
|
function getPointerWatcher() {
|
||||||
|
if (_pointerWatcher == null)
|
||||||
|
_pointerWatcher = new PointerWatcher();
|
||||||
|
|
||||||
|
return _pointerWatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PointerWatch = new Lang.Class({
|
||||||
|
Name: 'PointerWatch',
|
||||||
|
|
||||||
|
_init: function(watcher, interval, callback) {
|
||||||
|
this.watcher = watcher;
|
||||||
|
this.interval = interval;
|
||||||
|
this.callback = callback;
|
||||||
|
},
|
||||||
|
|
||||||
|
// remove:
|
||||||
|
// remove this watch. This function may safely be called
|
||||||
|
// while the callback is executing.
|
||||||
|
remove: function() {
|
||||||
|
this.watcher._removeWatch(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const PointerWatcher = new Lang.Class({
|
||||||
|
Name: 'PointerWatcher',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||||
|
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||||
|
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
|
||||||
|
this._watches = [];
|
||||||
|
this.pointerX = null;
|
||||||
|
this.pointerY = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
// addWatch:
|
||||||
|
// @interval: hint as to the time resolution needed. When the user is
|
||||||
|
// not idle, the position of the pointer will be queried at least
|
||||||
|
// once every this many milliseconds.
|
||||||
|
// @callback: function to call when the pointer position changes - takes
|
||||||
|
// two arguments, X and Y.
|
||||||
|
//
|
||||||
|
// Set up a watch on the position of the mouse pointer. Returns a
|
||||||
|
// PointerWatch object which has a remove() method to remove the watch.
|
||||||
|
addWatch: function(interval, callback) {
|
||||||
|
// Avoid unreliably calling the watch for the current position
|
||||||
|
this._updatePointer();
|
||||||
|
|
||||||
|
let watch = new PointerWatch(this, interval, callback);
|
||||||
|
this._watches.push(watch);
|
||||||
|
this._updateTimeout();
|
||||||
|
return watch;
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeWatch: function(watch) {
|
||||||
|
for (let i = 0; i < this._watches.length; i++) {
|
||||||
|
if (this._watches[i] == watch) {
|
||||||
|
this._watches.splice(i, 1);
|
||||||
|
this._updateTimeout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onIdleMonitorBecameActive: function(monitor) {
|
||||||
|
this._idle = false;
|
||||||
|
this._updatePointer();
|
||||||
|
this._updateTimeout();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onIdleMonitorBecameIdle: function(monitor) {
|
||||||
|
this._idle = true;
|
||||||
|
this._idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||||
|
this._updateTimeout();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateTimeout: function() {
|
||||||
|
if (this._timeoutId) {
|
||||||
|
Mainloop.source_remove(this._timeoutId);
|
||||||
|
this._timeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._idle || this._watches.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let minInterval = this._watches[0].interval;
|
||||||
|
for (let i = 1; i < this._watches.length; i++)
|
||||||
|
minInterval = Math.min(this._watches[i].interval, minInterval);
|
||||||
|
|
||||||
|
this._timeoutId = Mainloop.timeout_add(minInterval,
|
||||||
|
Lang.bind(this, this._onTimeout));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onTimeout: function() {
|
||||||
|
this._updatePointer();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePointer: function() {
|
||||||
|
let [x, y, mods] = global.get_pointer();
|
||||||
|
if (this.pointerX == x && this.pointerY == y)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.pointerX = x;
|
||||||
|
this.pointerY = y;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._watches.length;) {
|
||||||
|
let watch = this._watches[i];
|
||||||
|
watch.callback(x, y);
|
||||||
|
if (watch == this._watches[i]) // guard against self-removal
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
1020
js/ui/popupMenu.js
199
js/ui/remoteMenu.js
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const ShellMenu = imports.gi.ShellMenu;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
function stripMnemonics(label) {
|
||||||
|
if (!label)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
// remove all underscores that are not followed by another underscore
|
||||||
|
return label.replace(/_([^_])/, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
function _insertItem(menu, trackerItem, position) {
|
||||||
|
let mapper;
|
||||||
|
|
||||||
|
if (trackerItem.get_is_separator())
|
||||||
|
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
|
||||||
|
else if (trackerItem.get_has_submenu())
|
||||||
|
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
|
||||||
|
else
|
||||||
|
mapper = new RemoteMenuItemMapper(trackerItem);
|
||||||
|
|
||||||
|
let item = mapper.menuItem;
|
||||||
|
menu.addMenuItem(item, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _removeItem(menu, position) {
|
||||||
|
let items = menu._getMenuItems();
|
||||||
|
items[position].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoteMenuSeparatorItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuSeparatorItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._updateLabel();
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RequestSubMenu = new Lang.Class({
|
||||||
|
Name: 'RequestSubMenu',
|
||||||
|
Extends: PopupMenu.PopupSubMenuMenuItem,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent('');
|
||||||
|
this._requestOpen = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setOpenState: function(open) {
|
||||||
|
this.emit('request-open', open);
|
||||||
|
this._requestOpen = open;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getOpenState: function() {
|
||||||
|
return this._requestOpen;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenuSubmenuItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuSubmenuItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
this.menuItem = new RequestSubMenu();
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._updateLabel();
|
||||||
|
|
||||||
|
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
|
||||||
|
_insertItem.bind(null, this.menuItem.menu),
|
||||||
|
_removeItem.bind(null, this.menuItem.menu));
|
||||||
|
|
||||||
|
this.menuItem.connect('request-open', Lang.bind(this, function(menu, open) {
|
||||||
|
this._trackerItem.request_submenu_shown(open);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._trackerItem.connect('notify::submenu-shown', Lang.bind(this, function() {
|
||||||
|
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._tracker.destroy();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenuItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
|
||||||
|
this.menuItem = new PopupMenu.PopupBaseMenuItem();
|
||||||
|
this._label = new St.Label();
|
||||||
|
this.menuItem.addActor(this._label);
|
||||||
|
this.menuItem.actor.label_actor = this._label;
|
||||||
|
|
||||||
|
this.menuItem.connect('activate', Lang.bind(this, function() {
|
||||||
|
this._trackerItem.activated();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._trackerItem.connect('notify::sensitive', Lang.bind(this, this._updateSensitivity));
|
||||||
|
this._trackerItem.connect('notify::role', Lang.bind(this, this._updateRole));
|
||||||
|
this._trackerItem.connect('notify::toggled', Lang.bind(this, this._updateDecoration));
|
||||||
|
|
||||||
|
this._updateLabel();
|
||||||
|
this._updateSensitivity();
|
||||||
|
this._updateRole();
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this._label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function() {
|
||||||
|
this.menuItem.setSensitive(this._trackerItem.sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDecoration: function() {
|
||||||
|
let ornamentForRole = {};
|
||||||
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
|
||||||
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
|
||||||
|
|
||||||
|
let ornament = PopupMenu.Ornament.NONE;
|
||||||
|
if (this._trackerItem.toggled)
|
||||||
|
ornament = ornamentForRole[this._trackerItem.role];
|
||||||
|
|
||||||
|
this.menuItem.setOrnament(ornament);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateRole: function() {
|
||||||
|
let a11yRoles = {};
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
|
||||||
|
|
||||||
|
let a11yRole = a11yRoles[this._trackerItem.role];
|
||||||
|
this.menuItem.actor.accessible_role = a11yRole;
|
||||||
|
|
||||||
|
this._updateDecoration();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenu = new Lang.Class({
|
||||||
|
Name: 'RemoteMenu',
|
||||||
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
|
_init: function(sourceActor, model, actionGroup) {
|
||||||
|
this.parent(sourceActor, 0.0, St.Side.TOP);
|
||||||
|
|
||||||
|
this._model = model;
|
||||||
|
this._actionGroup = actionGroup;
|
||||||
|
this._tracker = Shell.MenuTracker.new(this._actionGroup,
|
||||||
|
this._model,
|
||||||
|
null, /* action namespace */
|
||||||
|
_insertItem.bind(null, this),
|
||||||
|
_removeItem.bind(null, this));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._tracker.destroy();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
});
|