Compare commits
1075 Commits
Author | SHA1 | Date | |
---|---|---|---|
803406d601 | |||
1bccc9a2d6 | |||
cf6b44b4bd | |||
5858b2266a | |||
1878c84eef | |||
931d364079 | |||
3fd65055f9 | |||
6ed0c16dd0 | |||
dd250ec994 | |||
5f13468b35 | |||
054ab6a1ed | |||
0b8ae22aa3 | |||
25e1b2548c | |||
5b21bca4f4 | |||
5a08886ed6 | |||
3679a8cdc4 | |||
6ebabd50c6 | |||
0c22a21a24 | |||
70a0c4211c | |||
a21af541c4 | |||
e07ba91486 | |||
a22e9ce9cd | |||
d4ce51b1b7 | |||
830005069c | |||
8c49267658 | |||
d0bab1f7ac | |||
f00826f3fb | |||
717c0ea19f | |||
3cd8dd0f32 | |||
aedd616346 | |||
b3de3ff00b | |||
b66dff8aed | |||
ff814df03a | |||
c9a528025c | |||
5f3ec8a6f1 | |||
becd29c50a | |||
a029a35050 | |||
d6a78d61d1 | |||
9be46bd212 | |||
4df3afced0 | |||
90f8e1df80 | |||
ea2ac89e61 | |||
695bfb9616 | |||
5dbdde8c15 | |||
854bfc17ed | |||
c8b192bcae | |||
f07306897f | |||
44d731fff5 | |||
52f939f64c | |||
f1d12c18d8 | |||
373ebb50eb | |||
b0b5be63b9 | |||
68f439425b | |||
cae4d92191 | |||
f336295475 | |||
06d0e7d74a | |||
5e0e3edc7b | |||
02a51bfa65 | |||
dbd04df311 | |||
e6adcd99c7 | |||
3171819c36 | |||
e4976b535d | |||
06d1602f17 | |||
603d0dfbc3 | |||
0b6c5b4620 | |||
d7894dbc44 | |||
d25d8126ea | |||
2402d3e115 | |||
ac4284d348 | |||
f9ef80749a | |||
22f0d3076e | |||
127b6dca1c | |||
8cfff33291 | |||
21f534c7f0 | |||
9faa1db942 | |||
2fc9bffb23 | |||
f16eae45a2 | |||
622e73c34c | |||
eb1c3d7045 | |||
fc26895f70 | |||
ca224b379f | |||
42b609b852 | |||
a24169e2aa | |||
144d1dca88 | |||
38ab6ba3ec | |||
bc6a0626c1 | |||
497f4f4a3c | |||
8c8c89a5f1 | |||
d658a5148a | |||
2c3e0dab0f | |||
e2400e35bd | |||
fc4562491b | |||
6e3bedb888 | |||
2b8332df5e | |||
795e7d4b59 | |||
522d46a525 | |||
dcecf5e350 | |||
a61c929450 | |||
5992e9f524 | |||
ee4cd05ebd | |||
af5e65fd79 | |||
284cbf3ea7 | |||
c3e68955e3 | |||
ffa28e7ee7 | |||
78d81f909c | |||
34551bf48b | |||
e02cd53ac5 | |||
b7f5154bd3 | |||
d80247918c | |||
bb8c2d3e13 | |||
6e0e37519d | |||
5c19ae5878 | |||
8713473c64 | |||
a4b4f2f6fd | |||
03f22135b5 | |||
5b79f657eb | |||
4fc0c513af | |||
f5c058a036 | |||
0dac0ad516 | |||
5d4d1e26fd | |||
2f0039e7dd | |||
9202309bed | |||
7e0e224e0e | |||
35825cf0c7 | |||
b2d79b6362 | |||
f5e1dc86cd | |||
8c51f0076f | |||
8347152730 | |||
3d77dd8f51 | |||
82e6935281 | |||
61070e6ec0 | |||
128697d6a7 | |||
424fa01eca | |||
775187b2e4 | |||
99b5e10acf | |||
208ed6e90d | |||
de8d4c4cd8 | |||
3ae591434d | |||
61fb62f969 | |||
c4584c20dc | |||
c6eb95ff3a | |||
ab68360d53 | |||
58a733dc93 | |||
db8f6b4848 | |||
e72d388a67 | |||
b5dd4d1456 | |||
a7562b4148 | |||
358f64d66b | |||
2a7f9f70b8 | |||
c91085caf7 | |||
21ddbf0b8f | |||
2ad2853278 | |||
e16f63a8e4 | |||
c39ffa111f | |||
5182129196 | |||
02bad8e92b | |||
500ea13155 | |||
ce8e7481e7 | |||
528f2e3cdc | |||
408211ba73 | |||
e02467eada | |||
3e93a1bdd6 | |||
a69a9ba51a | |||
1c0e0191e0 | |||
f233122d4d | |||
72bfa91259 | |||
ef195f0185 | |||
47da6b139e | |||
2705434955 | |||
a7e030d0f9 | |||
8a44170f83 | |||
2ea6ae05e5 | |||
5c0eba7d3b | |||
f81887772a | |||
9aa3d864dc | |||
2425b11df6 | |||
1cb644529f | |||
6cff0fd994 | |||
437256e0f1 | |||
841e0132e9 | |||
1545596c7e | |||
38406e070c | |||
9b07ce1d0d | |||
bf8d30603e | |||
bd6e7f14d1 | |||
5226d8b086 | |||
bd92331780 | |||
387f5143f0 | |||
b7867fe44c | |||
ddea54a539 | |||
84da49c715 | |||
513dff45c1 | |||
3d697baa26 | |||
9eda10d091 | |||
0f82da8735 | |||
296b61b61c | |||
974a896dbc | |||
093fd54e2b | |||
5ae3e5aeb7 | |||
af28a219be | |||
1883df2927 | |||
3274f270e3 | |||
c97626e516 | |||
9c483dd9a6 | |||
8fd6e93fbe | |||
8416ba25de | |||
63f6ff9151 | |||
59c2ace98c | |||
cf4465027a | |||
a23b293fe2 | |||
c039a3ddda | |||
ebe071bd50 | |||
2edfd458b7 | |||
3b750aa815 | |||
d96f97e55a | |||
55ac2f46ba | |||
262c04ab91 | |||
c80e7784fb | |||
763567377f | |||
ea9dcbf8b7 | |||
2b383bf73e | |||
b8dcbfebb2 | |||
a257aa35d5 | |||
941513b280 | |||
a9bb0558bc | |||
58834b80d7 | |||
b125a0e371 | |||
980ac0da8a | |||
f15fb0bd73 | |||
7d25592165 | |||
9ee36f2b71 | |||
f3e226103f | |||
a03e5662e2 | |||
16bb3efeb9 | |||
e01d867e69 | |||
1d4dfd7d5f | |||
da05d1d864 | |||
6499424eba | |||
824a602963 | |||
0092d8fff3 | |||
5fbe454836 | |||
23a59d387b | |||
6b2a68e553 | |||
82950ecea0 | |||
2da4df219c | |||
8b5597fd8d | |||
82ae06ad55 | |||
c594b66a2a | |||
13e3318e4d | |||
4aa4bb4a6a | |||
4a74df86e2 | |||
30ebc9c241 | |||
e0f6cf538d | |||
ba71382c72 | |||
87eb0d7e2f | |||
da199242b2 | |||
5e3d378667 | |||
a6d2735373 | |||
2ea435a928 | |||
75ba75900c | |||
7bacf4e344 | |||
258993d8ad | |||
ff19e411a2 | |||
e86c2826ef | |||
64741e41df | |||
fceac27412 | |||
0a394799da | |||
b83f2c0e33 | |||
225f825faf | |||
ed054f4f40 | |||
a1244a9b26 | |||
3a3714f4ff | |||
be483c4137 | |||
3492121c3f | |||
364f1453c1 | |||
9392e50cf1 | |||
f60a6ab465 | |||
f3a92d558d | |||
d387d4a60d | |||
4503ca4ca0 | |||
eeb10c0ce3 | |||
c69fcc2ef8 | |||
7d67d88ae8 | |||
310622b14e | |||
00065b84e1 | |||
4f7a8902f8 | |||
d20a6e0988 | |||
96fa7b7e77 | |||
61e4d40852 | |||
cc826f77fb | |||
1de288c5bb | |||
fe986faa2d | |||
f11b404622 | |||
508668107b | |||
674454621f | |||
2c907bd422 | |||
4eb6c0c4ee | |||
dd2eff2b6f | |||
d8960b396b | |||
4c29604e1e | |||
ede0bf2d88 | |||
893bfdf85f | |||
ac7a4c27fd | |||
4fe0233139 | |||
f9f9c7fe5b | |||
f1b90f174a | |||
3dd74c86c1 | |||
0edbdc529e | |||
b08523b170 | |||
cd2f8d8177 | |||
a1e8c79d38 | |||
34fc454764 | |||
e98a434ede | |||
02cdc065e7 | |||
77eb8f98c0 | |||
68679e1e7e | |||
05f2dbf205 | |||
45779bc7be | |||
1bf27b1f18 | |||
3ecdfaffd2 | |||
ee8fd1e613 | |||
ccfd5e35d6 | |||
b2eab65a81 | |||
00814dd159 | |||
57f9ffcaa7 | |||
ce83f378a5 | |||
30c7545ff3 | |||
a13357c2a8 | |||
75dc5c16c8 | |||
acd5d70209 | |||
669d20bcf0 | |||
793a8005a5 | |||
23b81fb241 | |||
73ba9d33eb | |||
75e2a7228c | |||
91d70f2487 | |||
779b1ae8e5 | |||
346ffd14d7 | |||
558b51f555 | |||
ef18b7ea25 | |||
0044e225aa | |||
c25c143b24 | |||
b87da87252 | |||
f9258bb5e3 | |||
3d747b00e6 | |||
450345b4d0 | |||
7563e1ebcd | |||
f96cc4dd69 | |||
c65a9c4d2e | |||
c01bd37edc | |||
021cecbce2 | |||
2a0cb7ff05 | |||
0085a94706 | |||
5db38194dc | |||
2a26143149 | |||
62f22557aa | |||
516ea5a02e | |||
9154471aba | |||
fbf6746acf | |||
a36686a6aa | |||
fd837d74d1 | |||
fd57334395 | |||
85100cb65f | |||
c70f6278d6 | |||
e573441bac | |||
5ed9571b37 | |||
9d203ddc0f | |||
508a13ae72 | |||
c0b50cbdf2 | |||
06f78549bd | |||
67afd7a6d8 | |||
ffa8c2f2b1 | |||
3803a880e8 | |||
965aedb0bb | |||
80911535a7 | |||
ca401d5036 | |||
a1c091d98d | |||
e1e08f0a68 | |||
1a4f629554 | |||
4113be770b | |||
cf3f4850b8 | |||
14f374096a | |||
6fef5c37f7 | |||
6e7455aa1e | |||
03bf6fa399 | |||
f777e761c0 | |||
31201d9618 | |||
d95d78ac15 | |||
bef4f17c49 | |||
f4b7ab0cb6 | |||
a180dde01c | |||
385c918e2e | |||
d7401c8646 | |||
a456d5eb19 | |||
6c08799c7b | |||
8d7bb6496c | |||
f0496a2d3c | |||
6664553b7e | |||
2a950ca3b3 | |||
463cd6382c | |||
78db025b10 | |||
f3265c28a9 | |||
a6e5e459d3 | |||
9ba399bf18 | |||
a52c91e9e5 | |||
9a7b47c23f | |||
18f7d20006 | |||
3f0ee88657 | |||
c634718dfa | |||
df6b31de05 | |||
b8e29ae8c7 | |||
cdba8e5cea | |||
4fccdaafb7 | |||
df0b465e76 | |||
aacdd4fd5e | |||
5f68c3a324 | |||
8ceae3b054 | |||
3fc5afaff1 | |||
8b7464c648 | |||
3e602b1765 | |||
5858028411 | |||
2c682ace81 | |||
45f3106814 | |||
aa947f9948 | |||
0c72d1fcbd | |||
6d22670307 | |||
0b9e68e305 | |||
da8155cbe5 | |||
5b7a052e18 | |||
6f26e39082 | |||
489b96a310 | |||
e65d90d624 | |||
9c74e22313 | |||
48a1fce151 | |||
83e7f6f496 | |||
3c5c3a6597 | |||
1c3ea1649f | |||
48a54e8ac4 | |||
113a854048 | |||
882f5fa79e | |||
9acdb8012c | |||
731d64e0e4 | |||
ccf1bd9f27 | |||
c164a8fe03 | |||
1a39666f7c | |||
f2731d4d6a | |||
3e63fb7abe | |||
8b4249ef26 | |||
bf0be6ef12 | |||
294702d3f1 | |||
58f3b7c748 | |||
e25502aeb2 | |||
ffe4eaf00d | |||
2f88a7a1e1 | |||
cd7d564125 | |||
ede81017ec | |||
a539e6236a | |||
90b7710834 | |||
f8cc8f1dc1 | |||
f8e5e3e435 | |||
508e751ffd | |||
207c847762 | |||
3c980566d3 | |||
50b59e0ca6 | |||
14c52bb00a | |||
9720b32987 | |||
674ae262c8 | |||
db297e7fdb | |||
d57c146514 | |||
ec5a4328e3 | |||
e4ee72c481 | |||
31f1e9ff0a | |||
d6c049a8c9 | |||
44047ac881 | |||
464d5d8a13 | |||
29811a85dc | |||
36ee4e6c3b | |||
2036e4c85c | |||
f24034de84 | |||
522ff86081 | |||
669e3c8ed9 | |||
ce850f464c | |||
03eaa61cef | |||
e10e953d24 | |||
2b47bb3d82 | |||
73d819116c | |||
c8dd984663 | |||
6087eb6d0e | |||
97b43d1d36 | |||
57ebadbaf8 | |||
cad7bb1151 | |||
9a376d47c5 | |||
b79adc05f4 | |||
f765c5e319 | |||
edc445c0c9 | |||
ab6b0f3f7f | |||
5e7902e733 | |||
f9f821aa55 | |||
9ed4b2a5ae | |||
0130ced790 | |||
8dab07af82 | |||
f6cd3fa5ed | |||
faae1a028e | |||
f5e32184fe | |||
18c7138237 | |||
3f0fbae7e2 | |||
7f1a258ff9 | |||
35b38d5cb2 | |||
09e8a437d4 | |||
d2bedcc182 | |||
84eda6e459 | |||
f5e7530fc7 | |||
f983b34784 | |||
36bbe64898 | |||
627a393ed6 | |||
a025b151ef | |||
18b6f13395 | |||
051413550f | |||
3e10574736 | |||
9a3041004b | |||
87f71b8ce1 | |||
825146f1e3 | |||
52995416fd | |||
6c43d0247a | |||
9aa98d9f0c | |||
c3a29d6df1 | |||
82f84416a9 | |||
9dd3162dbe | |||
7ef519756a | |||
3bbe74d1c1 | |||
409f6718b8 | |||
9c0e179080 | |||
b3b278d41f | |||
0f466dbafb | |||
fbb4a9a3a6 | |||
8ddae5cd71 | |||
b0915c7b60 | |||
831bb4e334 | |||
4e025506fa | |||
abccf451bf | |||
14954117c0 | |||
629f408fe5 | |||
86c6ab3c01 | |||
4a6ff94701 | |||
e480b08d58 | |||
caf53861d1 | |||
d0480648ba | |||
eb8cfe799f | |||
b9f2541880 | |||
bde9b08bfe | |||
8a4c862633 | |||
785c90f4b8 | |||
dd6a11e4c7 | |||
64e9503adb | |||
36c885bf34 | |||
ad7cde805d | |||
2c2c67f4dc | |||
cc4f8dfab0 | |||
0fb13608c5 | |||
09dbe17da0 | |||
fdd347c9aa | |||
572095515b | |||
f2d4aa0822 | |||
030a22d795 | |||
c70afcdb44 | |||
526d6c03b8 | |||
261b55300d | |||
e13bfd9a17 | |||
e096d18bac | |||
9460f0e4f3 | |||
e6591f52ac | |||
07e3d1fd5c | |||
1fbc6b24c8 | |||
982777be94 | |||
6610a34ad0 | |||
bfa8a0441a | |||
7723622ec7 | |||
6bcc8c70ef | |||
d114d5f95a | |||
b5c734da42 | |||
2077bb94c1 | |||
65a4ee7fb4 | |||
debf293298 | |||
a0df3e7d1e | |||
0d67c2d164 | |||
682bd7b622 | |||
fa0e54edbb | |||
5a0b209663 | |||
7e8859fd0e | |||
444fa2e0ab | |||
a31455b921 | |||
ac0213a516 | |||
45a6e2c305 | |||
11cbd396c0 | |||
a343445cd2 | |||
be3d62487c | |||
58905bd01a | |||
08506eac2d | |||
02c6b0374d | |||
0722c06275 | |||
17a4044d97 | |||
27a7194634 | |||
d73f560bcc | |||
e92f43b83e | |||
fed79ce4e6 | |||
fc45cf03bf | |||
efde11a0f3 | |||
fb951ff9b5 | |||
f5865e895e | |||
b8c2d4c6c7 | |||
778ad49ab4 | |||
fe7dd1305f | |||
378a3df5ea | |||
e63b81d69c | |||
c2fa2cdd8a | |||
6f215427f8 | |||
67ed4e0570 | |||
8a15178557 | |||
f3ecfab378 | |||
804563d5b2 | |||
c3e5d983b9 | |||
d21edcfed5 | |||
9b69a45eee | |||
7424ee755a | |||
ff664fd1d8 | |||
fd3f03580d | |||
a09150846a | |||
f2a9c55637 | |||
eaa3f83e46 | |||
e786cc1454 | |||
cd0c632fcb | |||
d5f248cb82 | |||
6a800abe06 | |||
fe265554a7 | |||
7305466765 | |||
9ac55a98f1 | |||
a1149fb6ad | |||
dfc4cc4aaf | |||
ef7541291b | |||
248a3e6b7e | |||
6b1e381750 | |||
feaf6108f9 | |||
9ad104585d | |||
7c44af3616 | |||
0599bf41b0 | |||
eac303f84c | |||
7bdd1c625c | |||
0003760fd9 | |||
eafb8c8e38 | |||
60c8105559 | |||
54626c6f7e | |||
cca528a630 | |||
530193a3a2 | |||
52e3149040 | |||
ad297ea9dc | |||
2015fc97dc | |||
35889a0f7d | |||
dcd84a4b53 | |||
01374989b1 | |||
f300462003 | |||
1e4da1b99c | |||
e1de6cb98d | |||
59a18c4ead | |||
b881e4b62a | |||
7060ae077b | |||
a7b0910566 | |||
60706f72d4 | |||
2e77f6b34b | |||
50d5030949 | |||
03dbb0f931 | |||
249619fabd | |||
60d1f7797c | |||
dc4b8c876e | |||
1724723e63 | |||
02455b1e28 | |||
47a9b97f8b | |||
0aa29daa72 | |||
182b1c1941 | |||
bbc8010de3 | |||
15baa56584 | |||
a72683707f | |||
f4baa4ddf8 | |||
f9eb36434f | |||
bc5e16bcea | |||
86e04048ff | |||
d7c0ff5e89 | |||
51e1efa277 | |||
67f636cc68 | |||
fcdfebd0e7 | |||
069ec3b910 | |||
1092f55b54 | |||
8a8abf12f9 | |||
f044e29526 | |||
4f703019ca | |||
0e6baec350 | |||
08690d658f | |||
674325e96c | |||
f8aa486ad1 | |||
be78f0f36a | |||
9917f05be8 | |||
d23228522c | |||
8c9896561e | |||
23cdb2125e | |||
43fc598bd5 | |||
2105d2f952 | |||
d18ec919dd | |||
da3e5f9746 | |||
594a227bc1 | |||
7277744dc0 | |||
e4718d3f7f | |||
5afd04781c | |||
a0868bac6b | |||
265b1f0292 | |||
2c12f3a509 | |||
0141a2be9e | |||
6660342d2f | |||
92667e3b7f | |||
a0632e3e02 | |||
c6d2946ce6 | |||
86304418a9 | |||
2f228e21da | |||
8521556723 | |||
9f5ac0e6d5 | |||
cc38dd1d49 | |||
66a5c0c094 | |||
8e802fd32f | |||
e87656af16 | |||
8327dbd611 | |||
e1b575dddc | |||
018025dada | |||
49fe0335ee | |||
bbe417121c | |||
0068098996 | |||
36d9cc329d | |||
1f2e53dd15 | |||
048a14f1f3 | |||
fd6ef482f0 | |||
d8926b96e2 | |||
934ec3c3fe | |||
216e996f66 | |||
a4b5583995 | |||
b00a1d6b7f | |||
d24abab4a8 | |||
23a9fb0314 | |||
5e26d0c90c | |||
aeb971c33a | |||
8897385714 | |||
e2a17fa8b4 | |||
14da6b68dc | |||
9a01a7ae07 | |||
c3bf4a325d | |||
2d71456944 | |||
9934529a0b | |||
ef6e8f5bb2 | |||
2ce7a3baa6 | |||
c0d224e200 | |||
4b2e5a4375 | |||
fbecbca9a0 | |||
c312891774 | |||
486d4dfb68 | |||
169b00aa64 | |||
f2c1a416bf | |||
28ef88911c | |||
e2161f385d | |||
f0c0687b43 | |||
e76e874093 | |||
d2a0cfb6d4 | |||
e5270cb6ec | |||
ee360d8749 | |||
bb4e6fd2da | |||
651600ac19 | |||
67f4cf3cff | |||
9b76e9f90a | |||
ba919b9c6a | |||
4da2862f8f | |||
b1de1ada25 | |||
1002bbc212 | |||
15e42c4d5f | |||
bb61dd4b44 | |||
2d4ba30ba2 | |||
54f46e8486 | |||
0ee762263a | |||
826682cc07 | |||
8b6e566728 | |||
e4a20e51e2 | |||
173ff91640 | |||
438d0d4feb | |||
8f275a79a9 | |||
a504dd866b | |||
dfc51ef243 | |||
a25d2dc256 | |||
b2ae945cf2 | |||
681861c8c7 | |||
6d40cb98e7 | |||
81d7ab1e49 | |||
2212b4ea71 | |||
5650355da5 | |||
20a9206919 | |||
3533b2a8bc | |||
b8d2238ff0 | |||
96c8870820 | |||
177e9316b0 | |||
27b7e6ef4b | |||
32d686d90f | |||
f812e9be7d | |||
c7185d597b | |||
6380526c12 | |||
fa4f6c4561 | |||
a5b7eaec1a | |||
d165295c83 | |||
5f5874f8ca | |||
ab5e950cd0 | |||
3e760f5ca6 | |||
9e8a24467e | |||
d84d1f79a0 | |||
024ec36cc0 | |||
51b1258866 | |||
ff1b76f4c7 | |||
0389ae5299 | |||
35ab60b712 | |||
0ea15f70c3 | |||
f1c7a36dde | |||
cc9093f118 | |||
775a2ea051 | |||
cb3d5c2b51 | |||
dc1c31d704 | |||
777616d8b0 | |||
8aeebbbf78 | |||
dade67ba5a | |||
921a9071a1 | |||
90a08ba0b6 | |||
4e52ed9df7 | |||
01b51cd081 | |||
d1efc274e5 | |||
d48d787c1e | |||
e4ad31a5dd | |||
b61cb92053 | |||
e72450f5d8 | |||
d9211b8e20 | |||
c16b83fc3e | |||
8b5a44e119 | |||
b0be6b8678 | |||
bb73547acf | |||
d8fc58e174 | |||
b9fdffbb62 | |||
e48a83fcfc | |||
4c7eae7ef2 | |||
75745fc23f | |||
82479db084 | |||
9becb3985d | |||
4cb7df67b7 | |||
e0a12f55b9 | |||
b18240370d | |||
4253df6463 | |||
d8b43865a2 | |||
3eb8b9ef68 | |||
f96077bd0f | |||
741846d1ff | |||
f36d7bd078 | |||
bd51c92d65 | |||
2aed6ade79 | |||
83e5ea4827 | |||
5915348396 | |||
567dc70c9b | |||
8323891294 | |||
5bf8695295 | |||
7012437929 | |||
83ad98d640 | |||
1816f763d2 | |||
1a01f07b2c | |||
23d48fb334 | |||
dada0420b1 | |||
17a336c795 | |||
8c347965d7 | |||
e1816cd228 | |||
3614da6845 | |||
8aa1765f24 | |||
8eb0782f25 | |||
5f6aba7f4b | |||
986b14fb1b | |||
7c03b88b74 | |||
1f277ebcb9 | |||
8c54bc68e0 | |||
6e39be5b19 | |||
cf71ea016f | |||
4affa5528a | |||
8536f9312c | |||
a0e8456cb5 | |||
77074b5646 | |||
cd68ed8297 | |||
eaef067b3e | |||
e23dfe533d | |||
39ecd3b5a8 | |||
f5e07fbeba | |||
ef3077a7fe | |||
4ff489d24b | |||
0ebba112d7 | |||
e7fa83ea1a | |||
b58f08bda1 | |||
3ec764d584 | |||
2c6443fd29 | |||
bb2d7f7e7e | |||
39a57eea4d | |||
d8505934e8 | |||
8f424e7d96 | |||
39fd7b9a05 | |||
60f6715228 | |||
1c36ade125 | |||
ebf132770e | |||
8f902a9379 | |||
6bb905895c | |||
99af774c98 | |||
42a214aa24 | |||
6c3dd2d4e0 | |||
eadc39356f | |||
8eb236ae41 | |||
707cc9e315 | |||
fab25e18da | |||
e01076a48b | |||
38a2f26e44 | |||
88cde2a97d | |||
b4bfe9a0c6 | |||
55df98298a | |||
a101f46544 | |||
afc2253e5d | |||
db4076b697 | |||
313ee70cf7 | |||
61c5f259ec | |||
874cf0ba15 | |||
f1370ef578 | |||
8f136e46d4 | |||
8a732d3c13 | |||
ad3e24e0bb | |||
f85d68aa70 | |||
6c5861ab3d | |||
be52ad999f | |||
5a47ad837b | |||
11e4f5c094 | |||
79c04c93e4 | |||
f015f4574f | |||
3b914ed4f7 | |||
bb7a99968f | |||
fdd6fc976c | |||
c60eb857bf | |||
147d192fa3 | |||
39b823dcec | |||
99ef24ba44 | |||
e34e183282 | |||
4ab275080c | |||
5a3b9a034e | |||
08d753fd29 | |||
7d058901ef | |||
0023059fa3 | |||
58da16c786 | |||
4a38359635 | |||
714bc5f103 | |||
d56411729b | |||
b3a96f2f6c | |||
5f5dc8327f | |||
c9bcb411fc | |||
86c6716786 | |||
46fe11a541 | |||
c2104dbf85 | |||
e23a03d639 | |||
ad92584b72 | |||
bf57397cb8 | |||
19cef181ad | |||
1a255d988b | |||
890a809022 | |||
530e8273ff | |||
62e9978c52 | |||
8d7db75fc5 | |||
6a504f5c62 | |||
b376a5dcdd | |||
dfd00d5483 | |||
13dbc3f15d | |||
22ac75556c | |||
8e665da103 | |||
7846fdbd2c | |||
99d61062e1 | |||
b37496d086 | |||
db2b83edf3 | |||
971179a10a | |||
ea855442be | |||
0a70909f10 | |||
abc43c9ece | |||
5bdaffd36e | |||
4660015bb1 | |||
634e5ff9e0 | |||
c79d24b60e | |||
509464fd61 | |||
d5d9f6283d | |||
560835dd0c | |||
8e40aa96ba | |||
4e7261532c | |||
3836cab769 | |||
79fc1a2383 | |||
c749facedb | |||
83648cf7d0 | |||
16914ddda3 | |||
167610c580 | |||
945caed602 | |||
7d2690ac5a | |||
a23f87de5e | |||
d6bb3de2fd | |||
c02b66bad1 | |||
880b240ecb | |||
078aafb10c | |||
4e7000988d | |||
d6d1fec4bd | |||
27318a8967 | |||
07664e7d2f | |||
6da0ebe35f | |||
af4e84383d | |||
1c1f63a7d7 | |||
8dab692f93 | |||
4de20272e5 | |||
ad75739fc5 | |||
1c5b73bfdf | |||
c045ab6171 | |||
1d23539576 | |||
71ae7e69c5 | |||
4c91db4661 | |||
09c38fda51 | |||
2b65d24ac1 | |||
2d3aff3f04 | |||
01056d72ab | |||
39d59b6eb6 | |||
cd5318baa7 | |||
67eade6482 | |||
30c6e541f1 | |||
e8a023a78f | |||
87ae45a12f | |||
ec66b32df6 | |||
815eaa1d4d | |||
8ecf901dd4 | |||
53b3d2a6c2 | |||
a3c1c09cc1 | |||
71f59de797 | |||
8032e672e9 | |||
f6c84d6185 | |||
e189a34fc0 | |||
aedc2428be | |||
faf00036e2 | |||
d903e831f3 | |||
4eff643d59 | |||
8c72c93aae | |||
d6eea0e380 | |||
1ef5281c55 | |||
77413feb57 | |||
830b4559c0 | |||
4a709792cd | |||
f2d0fcabd1 | |||
9c9da8a176 | |||
b81be42d08 | |||
485cd0f278 | |||
08d2e617e1 | |||
06586eb95d | |||
b00f122fcf | |||
11b3ed276b | |||
dfd887066f | |||
025985eb07 | |||
65a5baa902 | |||
e404369c0f | |||
88c7039ebe | |||
efc0ec4740 | |||
7d1382afd3 | |||
e850d9e29b | |||
464f552dd2 | |||
053e54f944 | |||
f84addcadc | |||
de27e4e1b2 | |||
33c5f5726e | |||
26330fde7e | |||
39cfe488a4 |
11
.gitignore
vendored
@ -16,11 +16,8 @@ config.log
|
||||
config.status
|
||||
config
|
||||
configure
|
||||
data/50-gnome-shell-*.xml
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-wayland.desktop
|
||||
data/gnome-shell-wayland.desktop.in
|
||||
data/org.gnome.Shell.desktop
|
||||
data/org.gnome.Shell.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
data/gnome-shell-theme.gresource
|
||||
@ -28,10 +25,9 @@ data/gschemas.compiled
|
||||
data/perf-background.xml
|
||||
data/org.gnome.shell.gschema.xml
|
||||
data/org.gnome.shell.gschema.valid
|
||||
data/org.gnome.shell.evolution.calendar.gschema.xml
|
||||
data/org.gnome.shell.evolution.calendar.gschema.valid
|
||||
data/org.gnome.Shell.PortalHelper.desktop
|
||||
data/org.gnome.Shell.PortalHelper.service
|
||||
data/theme/.sass-cache
|
||||
docs/reference/*/*.args
|
||||
docs/reference/*/*.bak
|
||||
docs/reference/*/*.hierarchy
|
||||
@ -75,7 +71,6 @@ src/*-marshal.[ch]
|
||||
src/Makefile
|
||||
src/Makefile.in
|
||||
src/calendar-server/evolution-calendar.desktop
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||
src/gnome-shell
|
||||
src/gnome-shell-calendar-server
|
||||
|
2
.gitmodules
vendored
@ -3,4 +3,4 @@
|
||||
url = git://git.gnome.org/libgnome-volume-control
|
||||
[submodule "data/theme/gnome-shell-sass"]
|
||||
path = data/theme/gnome-shell-sass
|
||||
url = https://git.gnome.org/browse/gnome-shell-sass
|
||||
url = git://git.gnome.org/gnome-shell-sass
|
||||
|
590
NEWS
@ -1,3 +1,593 @@
|
||||
3.22.2
|
||||
======
|
||||
* Fix OSD popup glitches [Cosimo; #772723, #772287]
|
||||
* Request periodic scans while WiFi list is open [Dan; #767918]
|
||||
* Always show primary network icon when connected [Florian; #773890]
|
||||
* Misc. bug fixes [Carlos, Florian, Ray; #773875, #740043, #773893]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Carlos Garcia Campos, Florian Müllner, Ray Strode, Dan Williams
|
||||
|
||||
Translations:
|
||||
Kjartan Maraas [nb], Fabio Tomat [fur], Philip Chimento [zh_CN],
|
||||
YunQiang Su [zh_CN], Piotr Drąg [pl], Stas Solovey [ru]
|
||||
|
||||
3.22.1
|
||||
======
|
||||
* Fix hidden network indicator on startup [Florian; #772249]
|
||||
* Fix order of windows with modal dialogs in window switcher [Florian; #747153]
|
||||
* Fix feedback loop between StClipboard and X11 bridge [Carlos; #760745]
|
||||
* Reliably match windows from Flatpak apps [Florian; #772615]
|
||||
* Misc. bug fixes [Philip; #742249]
|
||||
|
||||
Contributors:
|
||||
Philip Chimento, Carlos Garnacho, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Inaki Larranaga Murgoitio [eu], Khaled Hosny [ar], BM [uz@cyrillic],
|
||||
Milo Casagrande [it], Cheng-Chia Tseng [zh_TW], gogo [hr]
|
||||
|
||||
3.22.0
|
||||
======
|
||||
* Misc. bug fixes [Florian, Rui; #771391, #771536] #771656]
|
||||
|
||||
Contributors:
|
||||
Rui Matos, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Ask Hjorth Larsen [da], GNOME Translation Robot [gd], Alexandre Franke [fr],
|
||||
Daniel Korostil [uk], Jordi Mas [ca], Khaled Hosny [ar], David King [en_GB]
|
||||
|
||||
3.21.92
|
||||
=======
|
||||
* Adjust screen capture to work with multiple stage views [Jonas; #770128]
|
||||
* Improve handling of cycle shortcuts [Florian; #771063]
|
||||
* Fix windows not getting undimmed in some cases [Rui; #770163, #752524]
|
||||
* Disable extension version check by default [Florian; #770887]
|
||||
* Misc. bug fixes [Rui, Florian, Michael; #770382, #770888, #770328]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Michael Catanzaro, Fran Dieguez, Olivier Fourdan, Rui Matos,
|
||||
Florian Müllner
|
||||
|
||||
Translations:
|
||||
Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Aurimas Černius [lt],
|
||||
Muhammet Kara [tr], Trần Ngọc Quân [vi], A S Alam [pa], Yosef Or Boczko [he],
|
||||
Anders Jonsson [sv], Tiago Santos [pt], Hannie Dumoleyn [nl],
|
||||
Rūdolfs Mazurs [lv], Claude Paroz [fr], Arash Mousavi [fa],
|
||||
Fran Dieguez [gl], Stas Solovey [ru], Tom Tryfonidis [el]
|
||||
|
||||
3.21.91
|
||||
=======
|
||||
Translations:
|
||||
Mario Blättermann [de], Jiri Grönroos [fi], Dušan Kazik [sk],
|
||||
Andika Triwidada [id], Daniel Mustieles [es], Fabio Tomat [fur],
|
||||
Enrico Nicoletto [pt_BR], Matej Urbančič [sl], Мирослав Николић [sr, sr@latin]
|
||||
|
||||
3.21.90.1
|
||||
=========
|
||||
Contributors:
|
||||
Piotr Drąg
|
||||
|
||||
Translations:
|
||||
Marek Černocký [cs], Balázs Úr [hu]
|
||||
|
||||
3.21.90
|
||||
=======
|
||||
* Improve on-screen keyboard on wayland [Carlos; #765009]
|
||||
* Misc. bug fixes [Florian; #769156, #769216, #769074]
|
||||
|
||||
Contributors:
|
||||
Carlos Garnacho, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Fabio Tomat [fur], Tiago Santos [pt], Daniel Mustieles [es],
|
||||
Bernd Homuth [de], Aurimas Černius [lt], Balázs Úr [hu],
|
||||
Yosef Or Boczko [he], Jiri Grönroos [fi], Marek Cernocky [cs],
|
||||
Muhammet Kara [tr], Enrico Nicoletto [pt_BR], Andika Triwidada [id]
|
||||
|
||||
3.21.4
|
||||
======
|
||||
* overview: Fix switching workspaces when scrolling on non-primary monitors
|
||||
[Florian; #766883, #768316]
|
||||
* Fix crash when using screen recorder under wayland [Rui; #767001]
|
||||
* Update theme on video memory purge errors [Rui; #739178]
|
||||
* Free old backgrounds immediately [Hyungwon; #766353]
|
||||
* Add support for system upgrades to end session dialog [Kalev; #763611]
|
||||
* Fix maximized windows flickering to the wrong size on restart [Owen; #761566]
|
||||
* Hide ignored events in calendar as well [Florian; #768538]
|
||||
* calendar: Only hide dismissed occurrence of recurring event [Florian; #748226]
|
||||
* Provide org.freedesktop.impl.portal.access implementation [Florian; #768669]
|
||||
* Misc. bug fixes and cleanups [Rui, Florian, Marinus, Jonas; #767954, #768317,
|
||||
#746867, #762206, #768956, #768979]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Piotr Drąg, Hyungwon Hwang, Kalev Lember, Rui Matos,
|
||||
Florian Müllner, Marinus Schraal, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Andika Triwidada [id], Daniel Mustieles [es], Bruce Cowan [en_GB],
|
||||
Dušan Kazik [sk], Piotr Drąg [pl], Chao-Hsiung Liao [zh_HK]
|
||||
|
||||
3.21.3
|
||||
======
|
||||
* Do not disable suspend action when locked [Florian; #725960]
|
||||
* Remember input sources MRU list [Cosimo; #766826]
|
||||
* networkAgent: Handle VPN service aliases [David; #658484]
|
||||
* Plug a memory leak [Hans; #710230]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Florian Müllner, Hans Petter Jansson, David Woodhouse
|
||||
|
||||
Translations:
|
||||
Tiago Santos [pt], Cédric Valmary [oc], Muhammet Kara [tr],
|
||||
Daniel Mustieles [es], Rafael Fontenelle [pt_BR]
|
||||
|
||||
3.21.2
|
||||
======
|
||||
* Fix sorting of hidden apps in app switcher [Florian; #766238]
|
||||
* Set logind's LockedHint property when locked [Victor; #764773]
|
||||
* Allocate framebuffers early to fix a crash on NVIDIA [Martin; #764898]
|
||||
* Fix cycle-windows/cycle-group keybindings [Florian; #730739]
|
||||
* Switch to shared desktop schema for calendar settings [Iain; #766318]
|
||||
* Misc. bug fixes [Florian, Cosimo, Michele; #766325, #758471, #757556,
|
||||
#757019, #766598]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Michele Gaio, Iain Lane, Florian Müllner, Martin Szulecki,
|
||||
Victor Toso
|
||||
|
||||
Translations:
|
||||
Tiago Santos [pt], Kjartan Maraas [nb], Jiro Matsuzawa [ja],
|
||||
Cédric Valmary [oc], Sveinn í Felli [is]
|
||||
|
||||
3.21.1
|
||||
======
|
||||
* Save screencasts in HOME if XDG_VIDEO_DIR doesn't exist [Florian; #765015]
|
||||
* Don't show orientation lock when g-s-d won't rotate [Florian; #765267]
|
||||
* Misc. bug fixes [Heiher, Florian, Marek, Rui; #722752, #765061, #763068,
|
||||
#765607, #757676, #760439]
|
||||
|
||||
Contributors:
|
||||
Heiher, Marek Chalupa, Rui Matos, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Arash Mousavi [fa], Kristjan SCHMIDT [eo], GNOME Translation Robot [gd]
|
||||
|
||||
3.20.1
|
||||
======
|
||||
* Plug a memory leak [Aaron; #735705]
|
||||
|
||||
Contributors:
|
||||
Aaron Plattner
|
||||
|
||||
Translations:
|
||||
Daniel Korostil [uk], Matej Urbančič [sl], Inaki Larranaga Murgoitio [eu],
|
||||
Cheng-Chia Tseng [zh_TW], Fabio Tomat [fur], Trần Ngọc Quân [vi],
|
||||
YunQiang Su [zh_CN], Marek Černocký [cs], Arash Mousavi [fa],
|
||||
Alexander Shopov [bg], Khaled Hosny [ar]
|
||||
|
||||
3.20.0
|
||||
======
|
||||
|
||||
Translations:
|
||||
Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Milo Casagrande [it],
|
||||
Anders Jonsson [sv], Muhammet Kara [tr], Alexandre Franke [fr],
|
||||
Rūdolfs Mazurs [lv], Ask Hjorth Larsen [da], Jiro Matsuzawa [ja]
|
||||
|
||||
3.19.92
|
||||
=======
|
||||
* Update location dialog according to latest mockups [Zeeshan; #762480]
|
||||
* Fix deleting chat notifications in calendar [Florian; #747991]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Florian Müllner
|
||||
|
||||
Translations:
|
||||
Rūdolfs Mazurs [lv], Changwoo Ryu [ko], Matej Urbančič [sl],
|
||||
Justin van Steijn [nl], Fabio Tomat [fur], Kris Thomsen [da],
|
||||
Marek Černocký [cs], Piotr Drąg [pl], Dušan Kazik [sk],
|
||||
Мирослав Николић [sr, sr@latin], Balázs Úr [hu], Yosef Or Boczko [he],
|
||||
Daniel Mustieles [es], Fran Dieguez [gl], Bernd Homuth [de],
|
||||
Tom Tryfonidis [el], Jiri Grönroos [fi], Gil Forcada [ca],
|
||||
Artur Morais [pt_BR], Aurimas Černius [lt], Stas Solovey [ru]
|
||||
|
||||
3.19.91
|
||||
=======
|
||||
* location: Ask user only once [Zeeshan; #762559]
|
||||
* Fix jiggling when auto-hiding legacy tray [Florian; #747957]
|
||||
* Misc. bug fixes [Florian, Michael, Ting-Wei; #762475, #762507, #755659]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Michael Catanzaro, Ting-Wei Lan, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Мирослав Николић [sr, sr@latin], Piotr Drąg [pl], A S Alam [pa],
|
||||
Artur de Aquino Morais [pt_BR], Daniel Mustieles [es],
|
||||
Chao-Hsiung Liao [zh_TW], Daniel Korostil [uk], Fran Dieguez [gl],
|
||||
Tom Tryfonidis [el], Bernd Homuth [de], Sebastian Rasmussen [sv],
|
||||
Jordi Mas [ca], Piotr Drąg [ga], Cédric Valmary [oc], Gábor Kelemen [hu],
|
||||
Baurzhan Muftakhidinov [kk], Friedel Wolff [af], Marek Černocký [cs],
|
||||
Mingye Wang (Arthur2e5) [zh_CN], Aron Xu [zh_CN], Khaled Hosny [ar],
|
||||
Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he]
|
||||
|
||||
3.19.90
|
||||
=======
|
||||
* Correctly identify VPN secret requests [Lubomir; #760999]
|
||||
* Improve week number presentation [Jakub; #683245]
|
||||
* Add audio device selection dialog [Florian; #760284]
|
||||
* Add media controls to the time and date drop down [Florian; #756491]
|
||||
* Fix IBus candidate popup position under wayland [Rui; #753476]
|
||||
* Ask user to grant applications access to location [Zeeshan; #762119]
|
||||
* Misc. bug fixes [Mario, Jakub, Florian; #761208, #761772, #762270]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Michael Catanzaro, Rui Matos, Florian Müllner,
|
||||
Lubomir Rintel, Mario Sanchez Prada, Jakub Steiner
|
||||
|
||||
Translations:
|
||||
Alexander Shopov [bg], Balázs Meskó [hu], Fabio Tomat [fur],
|
||||
Dušan Kazik [sk], Piotr Drąg [pl], Alexandre Franke [fr],
|
||||
Mario Blättermann [de], Milo Casagrande [it], Jordi Mas [ca]
|
||||
|
||||
3.19.4
|
||||
======
|
||||
* gdm: Do not allow bypassing disabled Sign In button [Michael; #746180]
|
||||
* Style week numbers in calendar [Jakub; #683245]
|
||||
* Misc. bug fixes [Christophe, Jakub, Rui; #759708, #760577, #760945]
|
||||
|
||||
Contributors:
|
||||
Michael Catanzaro, Marek Černocký, Christophe Fergeau, Rui Matos,
|
||||
Jakub Steiner
|
||||
|
||||
Translations:
|
||||
Aurimas Černius [lt], Enrico Nicoletto [pt_BR], Andika Triwidada [id],
|
||||
Mario Blättermann [de], Marek Černocký [cs], Kjartan Maraas [nb],
|
||||
Muhammet Kara [tr], Stas Solovey [ru]
|
||||
|
||||
3.19.3
|
||||
======
|
||||
* Fix thumbnail scaling in window switcher on HiDPI [Florian; #758676]
|
||||
* Update animated backgrounds on timezone changes [Florian; #758939]
|
||||
* loginDialog: Update user list on user changes [Michael; #758568]
|
||||
* Fix touch interaction on wayland [Carlos; #756748]
|
||||
|
||||
Contributors:
|
||||
Michael Catanzaro, Carlos Garnacho, Kalev Lember, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Daniel Korostil [uk], Muhammet Kara [tr], Dušan Kazik [sk],
|
||||
Baurzhan Muftakhidinov [kk], Marek Černocký [cs]
|
||||
|
||||
3.19.2
|
||||
======
|
||||
* Make gnome-shell DBus activatable [Ray; #741666]
|
||||
* Fix browser plugin crash in Firefox [Carlos; #737932, #757940]
|
||||
* Optionally show battery percentage in system status area [Bastien; #735771]
|
||||
* Misc. bug fixes [Kalev, Florian, Bastien; #757418, #757668, #757779, #757816,
|
||||
#745626, #758220]
|
||||
|
||||
Contributors:
|
||||
Michael Biebl, Michael Catanzaro, Piotr Drąg, Carlos Garcia Campos,
|
||||
Kalev Lember, Florian Müllner, Bastien Nocera, Ray Strode
|
||||
|
||||
Translations:
|
||||
Pedro Albuquerque [pt], liushuyu [zh_CN], Yosef Or Boczko [he],
|
||||
Jiri Grönroos [fi], Kjartan Maraas [nb], GNOME Translation Robot [gd],
|
||||
Daniel Mustieles [es], Marek Černocký [cs], Kristjan SCHMIDT [eo],
|
||||
Stas Solovey [ru]
|
||||
|
||||
3.19.1
|
||||
======
|
||||
* Respect text-scaling factor under wayland [Owen; #756447]
|
||||
* Show the Bluetooth submenu when there were setup devices [Bastien; #723848]
|
||||
* Misc. bug fixes [Florian, Cosimo, Rui, Ray, Owen, Jakub, Bastien;
|
||||
#756697, #756714, #756605, #754814, #738942, #756983, #756925,
|
||||
#757011, #673235, #757150]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Rui Matos, Florian Müllner, Bastien Nocera, Jakub Steiner,
|
||||
Ray Strode, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Kjartan Maraas [nb], Khaled Hosny [ar], Balázs Meskó [hu],
|
||||
Daniel Șerbănescu [ro], Marek Černocký [cs]
|
||||
|
||||
3.18.1
|
||||
======
|
||||
* Fix screen freezes when a notification is pushed [Carlos; #755425]
|
||||
* Fix overzealous ellipsization in system status menu [Adel, Florian; #708472]
|
||||
* Hide app menu when disabled by setting [Florian; #745919]
|
||||
* Fix lightbox effect when animations are disabled [Rui; #755827]
|
||||
* Do not mark hotplug notifications as critical [Florian; #657923]
|
||||
* Fix icons getting cut off in dash [Florian; #745649]
|
||||
* Animate fullscreen/unfullscreen operations [Cosimo; #707248]
|
||||
* Misc. bug fixes [Florian, Owen; #748919, #674799, #754581]
|
||||
|
||||
Contributors:
|
||||
Emmanuele Bassi, Michael Catanzaro, Cosimo Cecchi, Matthias Clasen,
|
||||
Adel Gadllah, Carlos Garnacho, Ekaterina Gerasimova, Rui Matos,
|
||||
Florian Müllner, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Марко Костић [sr], Милош Поповић [sr@latin], Khaled Hosny [ar],
|
||||
Trần Ngọc Quân [vi], Petr Kovar [cs], Alexandre Franke [fr],
|
||||
Fran Dieguez [gl], Anders Jonsson [sv], Piotr Drąg [pl], Dušan Kazik [sk],
|
||||
Milo Casagrande [it], Changwoo Ryu [ko], Stas Solovey [ru],
|
||||
Rafael Fontenelle [pt_BR], Tom Tryfonidis [el], Aurimas Černius [lt],
|
||||
Seán de Búrca [ga], Christian Kirbach [de], Jiri Grönroos [fi],
|
||||
Pedro Albuquerque [pt], Baurzhan Muftakhidinov [kk], Daniel Mustieles [es],
|
||||
Marek Černocký [cs], Ask Hjorth Larsen [da], Inaki Larranaga Murgoitio [eu]
|
||||
|
||||
3.18.0
|
||||
======
|
||||
|
||||
Translations:
|
||||
Sendy Aditya Suryana [id], Kris Thomsen [da], Seán de Búrca [ga],
|
||||
Andika Triwidada [id], Enrico Nicoletto [pt_BR], Anders Jonsson [sv],
|
||||
Rūdolfs Mazurs [lv]
|
||||
|
||||
3.17.92
|
||||
=======
|
||||
* Fix race when loading multiple background animations [Josselin; #741453]
|
||||
|
||||
Contributors:
|
||||
Michael Biebl, Josselin Mouette, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Baurzhan Muftakhidinov [kk], Changwoo Ryu [ko], Christian Kirbach [de],
|
||||
Kjartan Maraas [nb], Jiri Grönroos [fi], Arash Mousavi [fa],
|
||||
Jiro Matsuzawa [ja], Marek Černocký [cs], Milo Casagrande [it]
|
||||
|
||||
3.17.91
|
||||
=======
|
||||
* Fix login screen spinner causing wakeups while VT-switched away
|
||||
[Ray, Rui; #753891]
|
||||
* Fix scrolling of user list on login screen [Florian; #754525]
|
||||
|
||||
Contributors:
|
||||
Piotr Drąg, Rui Matos, Florian Müllner, Ray Strode
|
||||
|
||||
Translations:
|
||||
Dušan Kazik [sk], Jordi Mas [ca], Aurimas Černius [lt], Stas Solovey [ru],
|
||||
Piotr Drąg [pl], Pedro Albuquerque [pt], Daniel Mustieles [es],
|
||||
Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr], Fran Dieguez [gl],
|
||||
Hannie Dumoleyn [nl], Yosef Or Boczko [he], Tom Tryfonidis [el],
|
||||
A S Alam [pa], Balázs Úr [hu], Alexandre Franke [fr], Frédéric Péters [fr]
|
||||
|
||||
3.17.90
|
||||
=======
|
||||
* Avoid caret/focus viewport changes during pointer movement [Rui; #752138]
|
||||
* Match GTK+'s modal dialogs for system modal dialogs [Carlos; #746108]
|
||||
* Refine message list style [Florian; #749958]
|
||||
* Fix type-ahead behavior for backspace and compose key [Rui; #753319, #753320]
|
||||
* Refine the system status menu [Florian; #751377]
|
||||
* Misc. bug fixes and cleanups [Bastien, Ray, Florian, Jakub; #752779, #752739,
|
||||
#741366, #651503, #753064, #753181, #752881]
|
||||
|
||||
Contributors:
|
||||
Rui Matos, Florian Müllner, Bastien Nocera, Carlos Soriano, Jakub Steiner,
|
||||
Ray Strode, Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Marek Černocký [cs], Kjartan Maraas [nb], Jordi Mas [ca], Muhammet Kara [tr],
|
||||
Enrico Nicoletto [pt_BR]
|
||||
|
||||
3.17.4
|
||||
======
|
||||
* Fix fuzziness of app menu icon [Jakub; #747932]
|
||||
* Implement 4 finger swipe gesture for touchpads [Carlos; #752250]
|
||||
* Misc. bug fixes [Florian, Alexandre, Piotr, Ray, Mario; #751921, #659969,
|
||||
#752438, #752675]
|
||||
|
||||
Contributors:
|
||||
Piotr Drąg, Alexandre Franke, Carlos Garnacho, Florian Müllner,
|
||||
Mario Sanchez Prada, Jakub Steiner, Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Benjamin Steinwender [de], Pedro Albuquerque [pt], Fabio Tomat [fur],
|
||||
Matej Urbančič [sl], Daniel Mustieles [es], Yosef Or Boczko [he],
|
||||
Daniel Martinez [an]
|
||||
|
||||
3.17.3
|
||||
======
|
||||
* Handle touch events in OSK on wayland [Rui; #750287]
|
||||
* Reinstate left/right movement to window menu [Ron; #751344]
|
||||
* Allow extensions to disable "Window is ready" notification [Adel; #748846]
|
||||
* Misc. bug fixes [Watson, Michael, Ray, Rui, Florian, Cosimo; #750465,
|
||||
#751016, #751517, #750714, #751541, #751599]
|
||||
|
||||
Contributors:
|
||||
Michael Biebl, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
|
||||
Ray Strode, Wim Taymans, Ron Yorston, Watson Yuuma Sato
|
||||
|
||||
Translations:
|
||||
Sebastian Rasmussen [sv], Dimitris Spingos [el], Muhammet Kara [tr],
|
||||
Stas Solovey [ru], Benjamin Steinwender [de], Balázs Úr [hu],
|
||||
Victor Ibragimov [tg], Dušan Kazik [sk], Pedro Albuquerque [pt]
|
||||
|
||||
3.17.2
|
||||
======
|
||||
* Remove StTable widget [Florian; #703833]
|
||||
* Increase visibility of expanders in alt-tab popup [Jakub; #745058]
|
||||
* Ensure suspend inhibitors are released when VT switched away [Rui; #749228]
|
||||
* Use iio-sensor-proxy directly for orientation lock [Bastien; #749671]
|
||||
* Misc. bug fixes [Florian, Lan, Carlos; #749279, #749383, #749529, #749490,
|
||||
#749742]
|
||||
|
||||
Contributors:
|
||||
Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Bastien Nocera,
|
||||
Jakub Steiner
|
||||
|
||||
Translations:
|
||||
Yosef Or Boczko [he], sun [zh_CN], Felipe Braga [pt_BR],
|
||||
Victor Ibragimov [tg], Gábor Kelemen [hu], Cédric Valmary [oc],
|
||||
Dušan Kazik [sk], Kjartan Maraas [nb], Bruno Ramalhete [pt],
|
||||
Matej Urbančič [sl], Daniel Mustieles [es]
|
||||
|
||||
3.17.1
|
||||
======
|
||||
* Add Display Settings entry to background menu [Meet; #697346]
|
||||
* Add window menu option to move to different monitor [Isaac; #633994]
|
||||
* Improve switch style in default/highContrast themes [Jakub; #746294, #747912]
|
||||
* Make event highlight in calendar more prominent [Jakub; #747715]
|
||||
* Fix keyboard focus when focusing a notification banner [Florian; #747205]
|
||||
* Move notification banners below the dateMenu [Meet, Florian; #745910]
|
||||
* Misc. bug fixes [Mario, Rui; #748338, #748541]
|
||||
|
||||
Contributors:
|
||||
Isaac Ge, Rui Matos, Florian Müllner, Meet Parikh, Mario Sanchez Prada,
|
||||
Jakub Steiner, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Sveinn í Felli [is], Marek Černocký [cs], laurent Soleil [oc]
|
||||
|
||||
3.16.1
|
||||
======
|
||||
* gdm: Move long session chooser menus to the side [Florian; #734352]
|
||||
* Work around background corruption with NVIDIA driver [Rui; #739178]
|
||||
* Don't allow move-to-workspace for always-sticky windows [Florian; #746782]
|
||||
* Allow switching workspaces with PgUp/PgDown in overview [Devyani; #742581]
|
||||
* Bump time PAM messages are displayed [Sarvjeet; #720885]
|
||||
* Fix "stutter" when moving window past the last workspace [Shivam; #712778]
|
||||
* Fix blurred text on login screen [Clément; #746912]
|
||||
* keyboard: Restore whole MRU list after password mode [Rui; #746605]
|
||||
* Pass event timestamps when activating remote actions [Owen; #747323]
|
||||
* Fix hung login screen when password is typed too quickly [Shivam; #737586]
|
||||
* Make on-screen keyboard work for shell chrome on wayland [Rui; #747274]
|
||||
* Implement reexec_self() for FreeBSD [Ting-Wei; #747788]
|
||||
* Allow to dismiss resident notifications [Florian; #746860]
|
||||
* Temporarily reveal legacy tray when icons are added [Florian; #746025]
|
||||
* Make concealed tray smaller to minimize overlap with apps [Florian; #746787]
|
||||
* Misc. bug fixes [Florian, Rui, Giovanni; #746323, #746579, #746902, #746364,
|
||||
#746509, #747636]
|
||||
|
||||
Contributors:
|
||||
Sarvjeet, Giovanni Campagna, Adel Gadllah, Clément Guérin, Devyani Kota,
|
||||
Ting-Wei Lan, Rui Matos, Shivam Mishra, Florian Müllner, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Khaled Hosny [ar], Dušan Kazik [sk], Yuri Myasoedov [ru], Stas Solovey [ru],
|
||||
Hannie Dumoleyn [nl], Rūdolfs Mazurs [lv]
|
||||
|
||||
3.16.0
|
||||
======
|
||||
* Revert erroneous login dialog changes [Ray; #740142]
|
||||
* Improve accessibility of legacy tray [Florian; #746487]
|
||||
* Fix legacy status icons leaking into other monitors [Florian; #745824]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Ray Strode
|
||||
|
||||
Translations:
|
||||
Daniel Martinez [an], Sebastian Rasmussen [sv], Fran Dieguez [gl],
|
||||
Andika Triwidada [id], Jordi Mas [ca], Kjartan Maraas [nb],
|
||||
Inaki Larranaga Murgoitio [eu], Muhammet Kara [tr], Khaled Hosny [ar],
|
||||
Bernd Homuth [de], Jiro Matsuzawa [ja]
|
||||
|
||||
3.15.92
|
||||
=======
|
||||
* gdm: Fix user list accessibility [Florian; #729603]
|
||||
* Handle multiline questions in mount operations [Ross; #745713]
|
||||
* Improve classic theme [Jakub; #745686, #745687]
|
||||
* Fix ordering of calendar events [Florian; #745988]
|
||||
* Pick first input source for new windows when per-window [Rui; #746037]
|
||||
* networkAgent: Show a notification for non-user-initiated password requests
|
||||
[Giovanni; #660293]
|
||||
* Fix dismissing calendar events [Florian; #744927]
|
||||
* Add legacy tray to ctrl-alt-tab popup [Florian; #746022]
|
||||
* Manage on-screen-keyboard visibility in gnome-shell [Carlos; #745977]
|
||||
* Add pointer barriers to legacy tray [Cosimo; #746026]
|
||||
* Use fallback when app icon cannot be resolved [Cosimo; #746219]
|
||||
* Fix handling of removed smartcard at startup [Ray; #740143]
|
||||
* gdm: Don't pick a random session for the user [Jasper; #740142]
|
||||
* Make menu selection behavior consistent with GTK [Florian; #745246]
|
||||
* gdm: Fix empty user list on user switching [Ray; #719418]
|
||||
* Misc bug fixes [Florian, Giovanni, Clément, Rui; #745666, #746019, #745861,
|
||||
#746027, #746223, #737502, #746343, #746288]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Piotr Drąg, Adel Gadllah, Carlos Garnacho,
|
||||
Clément Guérin, Ross Lagerwall, Rui Matos, Florian Müllner, Jakub Steiner,
|
||||
Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Piotr Drąg [pl], Changwoo Ryu [ko], Milo Casagrande [it],
|
||||
Baurzhan Muftakhidinov [kk], Мирослав Николић [sr, sr@latin], Balázs Úr [hu],
|
||||
IWAI, Masaharu [ja], Daniel Korostil [uk], Aurimas Černius [lt],
|
||||
Matej Urbančič [sl], Daniel Mustieles [es], Kjartan Maraas [nb],
|
||||
Victor Ibragimov [tg], Claude Paroz [fr], Jordi Mas [ca], Bernd Homuth [de],
|
||||
Muhammet Kara [tr], Frédéric Péters [fr], Jiri Grönroos [fi],
|
||||
Alexander Shopov [bg], Stas Solovey [ru], Trần Ngọc Quân [vi],
|
||||
Samir Ribic [bs], Dušan Kazik [sk], Enrico Nicoletto [pt_BR],
|
||||
Marek Černocký [cs], A S Alam [pa], Ask Hjorth Larsen [da],
|
||||
Tom Tryfonidis [el], Alexandre Franke [fr], Yosef Or Boczko [he],
|
||||
Chao-Hsiung Liao [zh_TW]
|
||||
|
||||
3.15.91
|
||||
=======
|
||||
* Don't disable all shortcuts while non-panel menus are open [Florian; #745039]
|
||||
* Do not wake up the screen for disabled notifications [Florian; #744114]
|
||||
* Add unminimize animation [Florian; #702662]
|
||||
* Change default shortcut for viewing notifications [Florian; #687986]
|
||||
* Add shortcut to dismiss notifications from list [Florian; #745279]
|
||||
* Use Polari instead of Empathy for IRC conversations [Giovanni; #745431]
|
||||
* Add a hideable bottom tray for legacy status icons [Florian; #745162]
|
||||
* Improve accessibility of calendar/message list [Florian; #706903, #745393]
|
||||
* Fix window thumbnail scaling in overview [Sebastian; #744883]
|
||||
* Stop handling non-chat notifications for Empathy [Giovanni; #745503]
|
||||
* Fix mouse interaction with ibus candidate window [Rui; #745167, #745245]
|
||||
* Misc bug fixes and cleanups [Giovanni, Florian, Miguel, Cosimo, Jakub,
|
||||
Michele, Raul, Matthias, Rui; #677412, #744880, #744900, #744894, #744907,
|
||||
#745061, #741111, #744912, #745175, #745246, #744959, #744575, #745570,
|
||||
#652742, #659187, #745412, #745521, #745521, #741665, #745574, #745627,
|
||||
#745494, #745111, #745132, #745110]
|
||||
|
||||
Contributors:
|
||||
Michele, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||
Raul Gutierrez Segales, Sebastian Keller, David Liang, Rui Matos,
|
||||
Florian Müllner, Yosef Or Boczko, Jakub Steiner, Jasper St. Pierre,
|
||||
Rico Tzschichholz, Miguel Vaello Martínez
|
||||
|
||||
Translations:
|
||||
Dušan Kazik [sk], Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr],
|
||||
Balázs Úr [hu], Daniel Mustieles [es], Frédéric Péters [fr],
|
||||
Rafael Ferreira [pt_BR], Aurimas Černius [lt], Milo Casagrande [it],
|
||||
Yosef Or Boczko [he], Stas Solovey [ru], Baurzhan Muftakhidinov [kk],
|
||||
Daniel Korostil [uk], Fran Dieguez [gl], Kjartan Maraas [nb],
|
||||
Tom Tryfonidis [el], Cheng-Chia Tseng [zh_TW], Friedel Wolff [af],
|
||||
Alexandre Franke [fr], Efstathios Iosifidis [el], Aron Xu [zh_CN],
|
||||
Marek Černocký [cs], Enrico Nicoletto [pt_BR]
|
||||
|
||||
3.15.90
|
||||
=======
|
||||
* gdm: Fix reactivity of first user in user list [Ray; #743370]
|
||||
* Prevent DND to dash when favorites are locked down [David; #741325]
|
||||
* Prevent DND in app picker when favorites are locked down [Murray; #741325]
|
||||
* Implement Display panel's OSD monitor labels in the shell [Rui; #743744]
|
||||
* Remove GSystem dependency [Colin; #744457]
|
||||
* Do not duplicate "New Window" action in dash [Florian; #744446]
|
||||
* Fix 'draw-cursor' option of screen recorder [Alexander; #744599]
|
||||
* Fix screencast timestamps [Sebastian; #744642]
|
||||
* Allow keybindings for switching to VT8-VT12 [Ray; #744800]
|
||||
* Implement notification redesign [Florian; #744498, #744815, #744817, #744850]
|
||||
* Fix struts computation of chrome on non-primary monitors [Sylvain; #744183]
|
||||
* Visual refresh based on GTK+ theme redesign [Jakub, Carlos; #737785, #744680]
|
||||
* Misc. bug fixes and cleanups [Ray, Bastien, Cosimo, Rui, Florian; #743371,
|
||||
#736182, #744013, #743993, #744665]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Murray Cumming, Sebastian Dröge, David King,
|
||||
Alexander Larsson, Rui Matos, Florian Müllner, Bastien Nocera,
|
||||
Sylvain Pasche, Carlos Soriano, Jakub Steiner, Jasper St. Pierre, Ray Strode,
|
||||
Colin Walters
|
||||
|
||||
Translations:
|
||||
Daniel Martinez [an], Sveinn í Felli [is], GNOME Translation Robot [de],
|
||||
Yosef Or Boczko [he], Kristjan SCHMIDT [eo], Dušan Kazik [sk],
|
||||
Andika Triwidada [id], Marek Černocký [cs], Muhammet Kara [tr],
|
||||
Matej Urbančič [sl], Milo Casagrande [it], Anders Jonsson [sv]
|
||||
|
||||
3.15.4
|
||||
======
|
||||
* Add mode parameter to AcceleratorActivated signal [Florian; #711682]
|
||||
|
@ -4,8 +4,6 @@
|
||||
srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
PKG_NAME="gnome-shell"
|
||||
|
||||
(test -f $srcdir/configure.ac \
|
||||
&& test -d $srcdir/src) || {
|
||||
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
|
||||
@ -26,4 +24,4 @@ which gnome-autogen.sh || {
|
||||
echo "your OS vendor's package manager)."
|
||||
exit 1
|
||||
}
|
||||
USE_GNOME2_MACROS=1 USE_COMMON_DOC_BUILD=yes . gnome-autogen.sh
|
||||
. gnome-autogen.sh
|
||||
|
@ -3,7 +3,10 @@ mozillalibdir = $(BROWSER_PLUGIN_DIR)
|
||||
|
||||
mozillalib_LTLIBRARIES = libgnome-shell-browser-plugin.la
|
||||
|
||||
libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined
|
||||
# Browsers can unload and reload the module while browsing, which is not supported by GObject.
|
||||
# We pass -Wl,-z,nodelete to the linker to ensure the module is never unloaded.
|
||||
# https://bugzilla.gnome.org/show_bug.cgi?id=737932
|
||||
libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined -Wl,-z,nodelete
|
||||
|
||||
libgnome_shell_browser_plugin_la_LIBADD = \
|
||||
$(BROWSER_PLUGIN_LIBS)
|
||||
|
@ -33,20 +33,16 @@
|
||||
#include <json-glib/json-glib.h>
|
||||
|
||||
#define ORIGIN "extensions.gnome.org"
|
||||
#define PLUGIN_NAME "Gnome Shell Integration"
|
||||
#define PLUGIN_DESCRIPTION "This plugin provides integration with Gnome Shell " \
|
||||
#define PLUGIN_NAME "GNOME Shell Integration"
|
||||
#define PLUGIN_DESCRIPTION "This plugin provides integration with GNOME Shell " \
|
||||
"for live extension enabling and disabling. " \
|
||||
"It can be used only by extensions.gnome.org"
|
||||
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
|
||||
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::GNOME Shell Integration Dummy Content-Type";
|
||||
|
||||
#define PLUGIN_API_VERSION 5
|
||||
|
||||
#define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation"
|
||||
|
||||
typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
} PluginData;
|
||||
|
||||
static NPNetscapeFuncs funcs;
|
||||
|
||||
static inline gchar *
|
||||
@ -145,121 +141,6 @@ check_origin_and_protocol (NPP instance)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* =============== public entry points =================== */
|
||||
|
||||
NPError
|
||||
NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
||||
{
|
||||
/* global initialization routine, called once when plugin
|
||||
is loaded */
|
||||
|
||||
g_debug ("plugin loaded");
|
||||
|
||||
memcpy (&funcs, pfuncs, sizeof (funcs));
|
||||
|
||||
plugin->size = sizeof(NPPluginFuncs);
|
||||
plugin->newp = NPP_New;
|
||||
plugin->destroy = NPP_Destroy;
|
||||
plugin->getvalue = NPP_GetValue;
|
||||
plugin->setwindow = NPP_SetWindow;
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NP_Shutdown(void)
|
||||
{
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
const char*
|
||||
NP_GetMIMEDescription(void)
|
||||
{
|
||||
return PLUGIN_MIME_STRING;
|
||||
}
|
||||
|
||||
NPError
|
||||
NP_GetValue(void *instance,
|
||||
NPPVariable variable,
|
||||
void *value)
|
||||
{
|
||||
switch (variable) {
|
||||
case NPPVpluginNameString:
|
||||
*(char**)value = PLUGIN_NAME;
|
||||
break;
|
||||
case NPPVpluginDescriptionString:
|
||||
*(char**)value = PLUGIN_DESCRIPTION;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NPP_New(NPMIMEType mimetype,
|
||||
NPP instance,
|
||||
uint16_t mode,
|
||||
int16_t argc,
|
||||
char **argn,
|
||||
char **argv,
|
||||
NPSavedData *saved)
|
||||
{
|
||||
/* instance initialization function */
|
||||
PluginData *data;
|
||||
GError *error = NULL;
|
||||
|
||||
g_debug ("plugin created");
|
||||
|
||||
if (!check_origin_and_protocol (instance))
|
||||
return NPERR_GENERIC_ERROR;
|
||||
|
||||
data = g_slice_new (PluginData);
|
||||
instance->pdata = data;
|
||||
|
||||
data->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL, /* interface info */
|
||||
"org.gnome.Shell",
|
||||
"/org/gnome/Shell",
|
||||
"org.gnome.Shell.Extensions",
|
||||
NULL, /* GCancellable */
|
||||
&error);
|
||||
if (!data->proxy)
|
||||
{
|
||||
/* ignore error if the shell is not running, otherwise warn */
|
||||
if (error->domain != G_DBUS_ERROR ||
|
||||
error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER)
|
||||
{
|
||||
g_warning ("Failed to set up Shell proxy: %s", error->message);
|
||||
}
|
||||
g_clear_error (&error);
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
g_debug ("plugin created successfully");
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NPP_Destroy(NPP instance,
|
||||
NPSavedData **saved)
|
||||
{
|
||||
/* instance finalization function */
|
||||
|
||||
PluginData *data = instance->pdata;
|
||||
|
||||
g_debug ("plugin destroyed");
|
||||
|
||||
g_object_unref (data->proxy);
|
||||
|
||||
g_slice_free (PluginData, data);
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
/* =================== scripting interface =================== */
|
||||
|
||||
typedef struct {
|
||||
@ -330,45 +211,18 @@ static NPObject *
|
||||
plugin_object_allocate (NPP instance,
|
||||
NPClass *klass)
|
||||
{
|
||||
PluginData *data = instance->pdata;
|
||||
PluginObject *obj = g_slice_new0 (PluginObject);
|
||||
PluginObject *obj = (PluginObject *) funcs.memalloc (sizeof (PluginObject));
|
||||
|
||||
memset (obj, 0, sizeof (PluginObject));
|
||||
obj->instance = instance;
|
||||
obj->proxy = g_object_ref (data->proxy);
|
||||
obj->settings = g_settings_new (SHELL_SCHEMA);
|
||||
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
|
||||
G_CALLBACK (on_shell_signal), obj);
|
||||
|
||||
obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gnome.Shell",
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
on_shell_appeared,
|
||||
NULL,
|
||||
obj,
|
||||
NULL);
|
||||
|
||||
g_debug ("plugin object created");
|
||||
|
||||
return (NPObject*)obj;
|
||||
return (NPObject*) obj;
|
||||
}
|
||||
|
||||
static void
|
||||
plugin_object_deallocate (NPObject *npobj)
|
||||
{
|
||||
PluginObject *obj = (PluginObject*)npobj;
|
||||
|
||||
g_signal_handler_disconnect (obj->proxy, obj->signal_id);
|
||||
g_object_unref (obj->proxy);
|
||||
|
||||
if (obj->listener)
|
||||
funcs.releaseobject (obj->listener);
|
||||
|
||||
if (obj->watch_name_id)
|
||||
g_bus_unwatch_name (obj->watch_name_id);
|
||||
|
||||
g_debug ("plugin object destroyed");
|
||||
|
||||
g_slice_free (PluginObject, obj);
|
||||
funcs.memfree (npobj);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
@ -1019,6 +873,149 @@ init_methods_and_properties (void)
|
||||
onextension_changed_id = funcs.getstringidentifier ("onchange");
|
||||
}
|
||||
|
||||
/* =============== public entry points =================== */
|
||||
|
||||
NPError
|
||||
NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
||||
{
|
||||
/* global initialization routine, called once when plugin
|
||||
is loaded */
|
||||
|
||||
g_debug ("plugin loaded");
|
||||
|
||||
memcpy (&funcs, pfuncs, sizeof (funcs));
|
||||
|
||||
plugin->size = sizeof(NPPluginFuncs);
|
||||
plugin->newp = NPP_New;
|
||||
plugin->destroy = NPP_Destroy;
|
||||
plugin->getvalue = NPP_GetValue;
|
||||
plugin->setwindow = NPP_SetWindow;
|
||||
plugin->event = NPP_HandleEvent;
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NP_Shutdown(void)
|
||||
{
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
const char*
|
||||
NP_GetMIMEDescription(void)
|
||||
{
|
||||
return PLUGIN_MIME_STRING;
|
||||
}
|
||||
|
||||
NPError
|
||||
NP_GetValue(void *instance,
|
||||
NPPVariable variable,
|
||||
void *value)
|
||||
{
|
||||
switch (variable) {
|
||||
case NPPVpluginNameString:
|
||||
*(char**)value = PLUGIN_NAME;
|
||||
break;
|
||||
case NPPVpluginDescriptionString:
|
||||
*(char**)value = PLUGIN_DESCRIPTION;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NPP_New(NPMIMEType mimetype,
|
||||
NPP instance,
|
||||
uint16_t mode,
|
||||
int16_t argc,
|
||||
char **argn,
|
||||
char **argv,
|
||||
NPSavedData *saved)
|
||||
{
|
||||
/* instance initialization function */
|
||||
PluginObject *obj;
|
||||
GError *error = NULL;
|
||||
|
||||
g_debug ("plugin created");
|
||||
|
||||
if (!check_origin_and_protocol (instance))
|
||||
return NPERR_GENERIC_ERROR;
|
||||
|
||||
/* set windowless mode */
|
||||
funcs.setvalue(instance, NPPVpluginWindowBool, NULL);
|
||||
|
||||
g_debug ("creating scriptable object");
|
||||
init_methods_and_properties ();
|
||||
obj = (PluginObject *) funcs.createobject (instance, &plugin_class);
|
||||
instance->pdata = obj;
|
||||
|
||||
obj->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL, /* interface info */
|
||||
"org.gnome.Shell",
|
||||
"/org/gnome/Shell",
|
||||
"org.gnome.Shell.Extensions",
|
||||
NULL, /* GCancellable */
|
||||
&error);
|
||||
if (!obj->proxy)
|
||||
{
|
||||
/* ignore error if the shell is not running, otherwise warn */
|
||||
if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
|
||||
{
|
||||
g_warning ("Failed to set up Shell proxy: %s", error->message);
|
||||
}
|
||||
g_clear_error (&error);
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
obj->settings = g_settings_new (SHELL_SCHEMA);
|
||||
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
|
||||
G_CALLBACK (on_shell_signal), obj);
|
||||
obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gnome.Shell",
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
on_shell_appeared,
|
||||
NULL,
|
||||
obj,
|
||||
NULL);
|
||||
|
||||
g_debug ("plugin created successfully");
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NPP_Destroy(NPP instance,
|
||||
NPSavedData **saved)
|
||||
{
|
||||
/* instance finalization function */
|
||||
PluginObject *obj = (PluginObject *) instance->pdata;
|
||||
|
||||
if (!obj)
|
||||
return NPERR_INVALID_INSTANCE_ERROR;
|
||||
|
||||
g_debug ("plugin destroyed");
|
||||
|
||||
g_signal_handler_disconnect (obj->proxy, obj->signal_id);
|
||||
g_object_unref (obj->proxy);
|
||||
|
||||
if (obj->listener)
|
||||
funcs.releaseobject (obj->listener);
|
||||
|
||||
if (obj->restart_listener)
|
||||
funcs.releaseobject (obj->restart_listener);
|
||||
|
||||
if (obj->watch_name_id)
|
||||
g_bus_unwatch_name (obj->watch_name_id);
|
||||
|
||||
funcs.releaseobject((NPObject *)obj);
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
NPError
|
||||
NPP_GetValue(NPP instance,
|
||||
NPPVariable variable,
|
||||
@ -1029,13 +1026,11 @@ NPP_GetValue(NPP instance,
|
||||
switch (variable) {
|
||||
case NPPVpluginScriptableNPObject:
|
||||
g_debug ("creating scriptable object");
|
||||
init_methods_and_properties ();
|
||||
if (!instance->pdata)
|
||||
return NPERR_INVALID_INSTANCE_ERROR;
|
||||
|
||||
*(NPObject**)value = funcs.createobject (instance, &plugin_class);
|
||||
break;
|
||||
|
||||
case NPPVpluginNeedsXEmbed:
|
||||
*(bool *)value = TRUE;
|
||||
funcs.retainobject (instance->pdata);
|
||||
*(NPObject**)value = instance->pdata;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1053,3 +1048,11 @@ NPP_SetWindow(NPP instance,
|
||||
{
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
int16_t
|
||||
NPP_HandleEvent(NPP instance,
|
||||
void *event)
|
||||
{
|
||||
/* Ignore the event */
|
||||
return FALSE;
|
||||
}
|
||||
|
44
configure.ac
@ -1,5 +1,6 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.15.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.22.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AX_IS_RELEASE([git-directory])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -23,13 +24,14 @@ LT_PREREQ([2.2.6])
|
||||
LT_INIT([disable-static])
|
||||
|
||||
# i18n
|
||||
IT_PROG_INTLTOOL([0.40])
|
||||
|
||||
GETTEXT_PACKAGE=gnome-shell
|
||||
AC_SUBST(GETTEXT_PACKAGE)
|
||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||
[The prefix for our gettext translation domains.])
|
||||
|
||||
AM_GNU_GETTEXT_VERSION([0.19.6])
|
||||
AM_GNU_GETTEXT([external])
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.22])
|
||||
|
||||
AC_PATH_PROG([XSLTPROC], [xsltproc])
|
||||
@ -51,7 +53,7 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules mutter-clutter-1.0)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
@ -63,8 +65,8 @@ AC_ARG_ENABLE([systemd],
|
||||
[enable_systemd=$enableval],
|
||||
[enable_systemd=auto])
|
||||
AS_IF([test x$enable_systemd != xno], [
|
||||
AC_MSG_CHECKING([for libsystemd-journal])
|
||||
PKG_CHECK_EXISTS([libsystemd-journal],
|
||||
AC_MSG_CHECKING([for libsystemd])
|
||||
PKG_CHECK_EXISTS([libsystemd],
|
||||
[have_systemd=yes
|
||||
AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
|
||||
[have_systemd=no])
|
||||
@ -73,14 +75,14 @@ AS_IF([test x$enable_systemd != xno], [
|
||||
|
||||
AC_MSG_RESULT($enable_systemd)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.15.90
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
CLUTTER_MIN_VERSION=1.21.5
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=1.49.1
|
||||
GJS_MIN_VERSION=1.39.0
|
||||
MUTTER_MIN_VERSION=3.15.4
|
||||
MUTTER_MIN_VERSION=3.22.1
|
||||
GTK_MIN_VERSION=3.15.0
|
||||
GIO_MIN_VERSION=2.37.0
|
||||
GIO_MIN_VERSION=2.45.3
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.13.90
|
||||
LIBEDATASERVER_MIN_VERSION=3.17.2
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
@ -97,8 +99,8 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
mutter-clutter-1.0 >= $CLUTTER_MIN_VERSION
|
||||
mutter-cogl-pango-1.0
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra libcanberra-gtk3
|
||||
@ -106,20 +108,19 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION
|
||||
gcr-base-3 >= $GCR_MIN_VERSION"
|
||||
if test x$have_systemd = xyes; then
|
||||
SHARED_PCS="${SHARED_PCS} libsystemd-journal"
|
||||
SHARED_PCS="${SHARED_PCS} libsystemd"
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
|
||||
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_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.8 x11)
|
||||
PKG_CHECK_MODULES(ST, mutter-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(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(TRAY, mutter-clutter-1.0 gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.13.1)
|
||||
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.21.3)
|
||||
|
||||
AC_ARG_ENABLE(browser-plugin,
|
||||
[AS_HELP_STRING([--enable-browser-plugin],
|
||||
@ -221,7 +222,7 @@ if test "$enable_man" != no; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||
|
||||
GNOME_COMPILE_WARNINGS([error])
|
||||
AX_COMPILER_FLAGS()
|
||||
case "$WARN_CFLAGS" in
|
||||
*-Werror*)
|
||||
WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations"
|
||||
@ -242,6 +243,8 @@ if test -z "$GDBUS_CODEGEN"; then
|
||||
AC_MSG_ERROR([gdbus-codegen not found])
|
||||
fi
|
||||
|
||||
AC_PATH_PROG([SASS],[sass],[])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
data/Makefile
|
||||
@ -252,7 +255,6 @@ AC_CONFIG_FILES([
|
||||
docs/reference/st/Makefile
|
||||
docs/reference/st/st-docs.sgml
|
||||
js/Makefile
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/Makefile
|
||||
src/gvc/Makefile
|
||||
browser-plugin/Makefile
|
||||
@ -268,7 +270,7 @@ Build configuration:
|
||||
Prefix: ${prefix}
|
||||
Source code location: ${srcdir}
|
||||
Compiler: ${CC}
|
||||
Compiler Warnings: $enable_compile_warnings
|
||||
Compiler Warnings: $ax_enable_compile_warnings
|
||||
|
||||
Support for NetworkManager: $have_networkmanager
|
||||
Support for GStreamer recording: $build_recorder
|
||||
|
@ -1,24 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="System"
|
||||
name="System"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-message-tray"
|
||||
_description="Show the message tray"/>
|
||||
description="Show the notification list"/>
|
||||
|
||||
<KeyListEntry name="focus-active-notification"
|
||||
_description="Focus the active notification"/>
|
||||
description="Focus the active notification"/>
|
||||
|
||||
<KeyListEntry name="toggle-overview"
|
||||
_description="Show the overview"/>
|
||||
description="Show the overview"/>
|
||||
|
||||
<KeyListEntry name="toggle-application-view"
|
||||
_description="Show all applications"/>
|
||||
description="Show all applications"/>
|
||||
|
||||
<KeyListEntry name="open-application-menu"
|
||||
_description="Open the application menu"/>
|
||||
description="Open the application menu"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
@ -1,17 +1,23 @@
|
||||
CLEANFILES =
|
||||
NULL =
|
||||
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-wayland.desktop gnome-shell-extension-prefs.desktop
|
||||
desktop_DATA = org.gnome.Shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
if HAVE_NETWORKMANAGER
|
||||
desktop_DATA += org.gnome.Shell.PortalHelper.desktop
|
||||
|
||||
portaldir = $(datadir)/xdg-desktop-portal/portals
|
||||
portal_DATA = gnome-shell.portal
|
||||
|
||||
servicedir = $(datadir)/dbus-1/services
|
||||
service_DATA = org.gnome.Shell.PortalHelper.service
|
||||
|
||||
CLEANFILES += \
|
||||
org.gnome.Shell.PortalHelper.service \
|
||||
org.gnome.Shell.PortalHelper.desktop
|
||||
org.gnome.Shell.PortalHelper.service \
|
||||
org.gnome.Shell.PortalHelper.desktop \
|
||||
org.gnome.Shell.PortalHelper.desktop.in \
|
||||
$(NULL)
|
||||
|
||||
endif
|
||||
|
||||
@ -26,14 +32,45 @@ endif
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) $(MSGFMT) --desktop --template $(builddir)/$< \
|
||||
-d $(top_srcdir)/po -o $@
|
||||
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = \
|
||||
org.gnome.Shell.Screencast.xml \
|
||||
org.gnome.Shell.Screenshot.xml \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
org.gnome.ShellSearchProvider2.xml \
|
||||
$(NULL)
|
||||
|
||||
theme_sources = \
|
||||
theme/gnome-shell-high-contrast.scss \
|
||||
theme/gnome-shell.scss \
|
||||
theme/gnome-shell-sass/_colors.scss \
|
||||
theme/gnome-shell-sass/_common.scss \
|
||||
theme/gnome-shell-sass/_drawing.scss \
|
||||
theme/gnome-shell-sass/_high-contrast-colors.scss \
|
||||
$(NULL)
|
||||
|
||||
dist_theme_files = \
|
||||
$(theme_sources) \
|
||||
theme/Gemfile \
|
||||
theme/HACKING \
|
||||
theme/README \
|
||||
theme/gnome-shell-sass/COPYING \
|
||||
theme/gnome-shell-sass/HACKING \
|
||||
theme/gnome-shell-sass/NEWS \
|
||||
theme/gnome-shell-sass/README \
|
||||
theme/gnome-shell-sass/gnome-shell-sass.doap \
|
||||
theme/parse-sass.sh \
|
||||
$(NULL)
|
||||
|
||||
%.css: %.scss $(theme_sources)
|
||||
@if test -n "$(SASS)"; then \
|
||||
if $(AM_V_P); then PS4= set -x; else echo " GEN $@"; fi; \
|
||||
$(SASS) --sourcemap=none -f -q --update $<; \
|
||||
fi
|
||||
|
||||
resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir)/theme --generate-dependencies $(srcdir)/gnome-shell-theme.gresource.xml)
|
||||
gnome-shell-theme.gresource: gnome-shell-theme.gresource.xml $(resource_files)
|
||||
@ -49,14 +86,11 @@ perf-background.xml: perf-background.xml.in
|
||||
$< > $@ || rm $@
|
||||
|
||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
keys_in_files = 50-gnome-shell-system.xml.in
|
||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||
keys_DATA = 50-gnome-shell-system.xml
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
|
||||
%.gschema.xml.in: %.gschema.xml.in.in Makefile
|
||||
%.gschema.xml: %.gschema.xml.in Makefile
|
||||
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
|
||||
$< > $@ || rm $@
|
||||
|
||||
@ -73,29 +107,29 @@ convertdir = $(datadir)/GConf/gsettings
|
||||
convert_DATA = gnome-shell-overrides.convert
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-wayland.desktop.in.in \
|
||||
org.gnome.Shell.desktop.in.in \
|
||||
gnome-shell-extension-prefs.desktop.in.in \
|
||||
$(portal_DATA) \
|
||||
$(introspection_DATA) \
|
||||
$(menu_DATA) \
|
||||
$(convert_DATA) \
|
||||
$(keys_in_files) \
|
||||
$(keys_DATA) \
|
||||
$(dist_theme_files) \
|
||||
perf-background.xml.in \
|
||||
org.gnome.Shell.PortalHelper.desktop.in \
|
||||
org.gnome.Shell.PortalHelper.desktop.in.in \
|
||||
org.gnome.Shell.PortalHelper.service.in \
|
||||
org.gnome.shell.gschema.xml.in.in \
|
||||
org.gnome.shell.gschema.xml.in \
|
||||
gnome-shell-theme.gresource.xml \
|
||||
$(resource_files)
|
||||
$(resource_files) \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES += \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-wayland.desktop.in \
|
||||
org.gnome.Shell.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(keys_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
perf-background.xml \
|
||||
gschemas.compiled \
|
||||
org.gnome.shell.gschema.valid \
|
||||
org.gnome.shell.gschema.xml.in \
|
||||
gnome-shell-theme.gresource
|
||||
gnome-shell-theme.gresource \
|
||||
$(NULL)
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell Extension Preferences
|
||||
_Comment=Configure GNOME Shell Extensions
|
||||
Name=GNOME Shell Extension Preferences
|
||||
Comment=Configure GNOME Shell Extensions
|
||||
Exec=@bindir@/gnome-shell-extension-prefs %u
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
|
@ -19,6 +19,8 @@
|
||||
<file>gnome-shell-high-contrast.css</file>
|
||||
<file>logged-in-indicator.svg</file>
|
||||
<file>more-results.svg</file>
|
||||
<file>no-events.svg</file>
|
||||
<file>no-notifications.svg</file>
|
||||
<file>noise-texture.png</file>
|
||||
<file>page-indicator-active.svg</file>
|
||||
<file>page-indicator-inactive.svg</file>
|
||||
@ -30,8 +32,10 @@
|
||||
<file>summary-counter.svg</file>
|
||||
<file>toggle-off-us.svg</file>
|
||||
<file>toggle-off-intl.svg</file>
|
||||
<file>toggle-off-hc.svg</file>
|
||||
<file>toggle-on-us.svg</file>
|
||||
<file>toggle-on-intl.svg</file>
|
||||
<file>toggle-on-hc.svg</file>
|
||||
<file>ws-switch-arrow-up.png</file>
|
||||
<file>ws-switch-arrow-down.png</file>
|
||||
</gresource>
|
||||
|
@ -1,15 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell (wayland compositor)
|
||||
_Comment=Window management and application launching
|
||||
Exec=@bindir@/gnome-shell --wayland --display-server
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;Core;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=DisplayServer
|
||||
X-GNOME-Autostart-Notify=true
|
||||
X-GNOME-AutoRestart=false
|
4
data/gnome-shell.portal
Normal file
@ -0,0 +1,4 @@
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.desktop.gnome
|
||||
Interfaces=org.freedesktop.impl.portal.Access
|
||||
UseIn=gnome
|
@ -1,9 +1,10 @@
|
||||
[Desktop Entry]
|
||||
_Name=Network Login
|
||||
Name=Network Login
|
||||
Type=Application
|
||||
Exec=gapplication launch org.gnome.Shell.PortalHelper
|
||||
DBusActivatable=true
|
||||
NoDisplay=true
|
||||
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
|
||||
Icon=network-workgroup
|
||||
StartupNotify=true
|
||||
OnlyShowIn=GNOME;
|
@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell
|
||||
_Comment=Window management and application launching
|
||||
Name=GNOME Shell
|
||||
Comment=Window management and application launching
|
||||
Exec=@bindir@/gnome-shell
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
@ -10,7 +10,7 @@ X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;Core;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
X-GNOME-Autostart-Phase=DisplayServer
|
||||
X-GNOME-Provides=panel;windowmanager;
|
||||
X-GNOME-Autostart-Notify=true
|
||||
X-GNOME-AutoRestart=false
|
@ -3,132 +3,131 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="development-tools" type="b">
|
||||
<default>true</default>
|
||||
<_summary>
|
||||
<summary>
|
||||
Enable internal tools useful for developers and testers from Alt-F2
|
||||
</_summary>
|
||||
<_description>
|
||||
</summary>
|
||||
<description>
|
||||
Allows access to internal debugging and monitoring tools
|
||||
using the Alt-F2 dialog.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="enabled-extensions" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>UUIDs of extensions to enable</_summary>
|
||||
<_description>
|
||||
<summary>UUIDs of extensions to enable</summary>
|
||||
<description>
|
||||
GNOME Shell extensions have a UUID property; this key lists extensions
|
||||
which should be loaded. Any extension that wants to be loaded needs
|
||||
to be in this list. You can also manipulate this list with the
|
||||
EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="disable-extension-version-validation" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Disables the validation of extension version compatibility</_summary>
|
||||
<_description>
|
||||
<default>true</default>
|
||||
<summary>Disables the validation of extension version compatibility</summary>
|
||||
<description>
|
||||
GNOME Shell will only load extensions that claim to support the current
|
||||
running version. Enabling this option will disable this check and try to
|
||||
load all extensions regardless of the versions they claim to support.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="favorite-apps" type="as">
|
||||
<default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
|
||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||
<_description>
|
||||
<summary>List of desktop file IDs for favorite applications</summary>
|
||||
<description>
|
||||
The applications corresponding to these identifiers
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="app-picker-view" type="u">
|
||||
<default>0</default>
|
||||
<_summary>App Picker View</_summary>
|
||||
<_description>
|
||||
<summary>App Picker View</summary>
|
||||
<description>
|
||||
Index of the currently selected view in the application picker.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
<summary>History for command (Alt-F2) dialog</summary>
|
||||
</key>
|
||||
<key name="looking-glass-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for the looking glass dialog</_summary>
|
||||
<!-- Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass -->
|
||||
<summary>History for the looking glass dialog</summary>
|
||||
</key>
|
||||
<key name="always-show-log-out" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
|
||||
<_description>
|
||||
<summary>Always show the 'Log out' menu item in the user menu.</summary>
|
||||
<description>
|
||||
This key overrides the automatic hiding of the 'Log out'
|
||||
menu item in single-user, single-session situations.
|
||||
</_description>
|
||||
</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>
|
||||
<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>
|
||||
</description>
|
||||
</key>
|
||||
<key name="had-bluetooth-devices-setup" type="b">
|
||||
<default>false</default>
|
||||
<summary>Whether the default Bluetooth adapter had set up devices associated to it</summary>
|
||||
<description>
|
||||
The shell will only show a Bluetooth menu item if a Bluetooth
|
||||
adapter is powered, or if there were devices set up associated
|
||||
with the default adapter. This will be reset if the default
|
||||
adapter is ever seen not to have devices associated to it.
|
||||
</description>
|
||||
</key>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-weekdate" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show the week date in the calendar</_summary>
|
||||
<_description>
|
||||
If true, display the ISO week date in the calendar.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="open-application-menu" type="as">
|
||||
<default>["<Super>F10"]</default>
|
||||
<_summary>Keybinding to open the application menu</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to open the application menu</summary>
|
||||
<description>
|
||||
Keybinding to open the application menu.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="toggle-application-view" type="as">
|
||||
<default>["<Super>a"]</default>
|
||||
<_summary>Keybinding to open the "Show Applications" view</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to open the "Show Applications" view</summary>
|
||||
<description>
|
||||
Keybinding to open the "Show Applications" view of the Activities
|
||||
Overview.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="toggle-overview" type="as">
|
||||
<default>["<Super>s"]</default>
|
||||
<_summary>Keybinding to open the overview</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to open the overview</summary>
|
||||
<description>
|
||||
Keybinding to open the Activities Overview.
|
||||
</_description>
|
||||
</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>
|
||||
<default>["<Super>v","<Super>m"]</default>
|
||||
<summary>Keybinding to toggle the visibility of the notification list</summary>
|
||||
<description>
|
||||
Keybinding to toggle the visibility of the notification list.
|
||||
</description>
|
||||
</key>
|
||||
<key name="focus-active-notification" type="as">
|
||||
<default>["<Super>n"]</default>
|
||||
<_summary>Keybinding to focus the active notification</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to focus the active notification</summary>
|
||||
<description>
|
||||
Keybinding to focus the active notification.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="pause-resume-tweens" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</_summary>
|
||||
<_description></_description>
|
||||
<summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -136,10 +135,10 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="keyboard-type" type="s">
|
||||
<default>'touch'</default>
|
||||
<_summary>Which keyboard to use</_summary>
|
||||
<_description>
|
||||
<summary>Which keyboard to use</summary>
|
||||
<description>
|
||||
The type of keyboard to use.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -148,11 +147,11 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>false</default>
|
||||
<_summary>Limit switcher to current workspace.</_summary>
|
||||
<_description>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
If true, only applications that have windows on the current workspace are shown in the switcher.
|
||||
Otherwise, all applications are included.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -166,20 +165,20 @@
|
||||
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>
|
||||
<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>
|
||||
</description>
|
||||
</key>
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>true</default>
|
||||
<_summary>Limit switcher to current workspace.</_summary>
|
||||
<_description>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
If true, only windows from the current workspace are shown in the switcher.
|
||||
Otherwise, all windows are included.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -187,43 +186,43 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="attach-modal-dialogs" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Attach modal dialog to the parent window</_summary>
|
||||
<_description>
|
||||
<summary>Attach modal dialog to the parent window</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running
|
||||
GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="edge-tiling" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
|
||||
<_description>
|
||||
<summary>Enable edge tiling when dropping windows on screen edges</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="dynamic-workspaces" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Workspaces are managed dynamically</_summary>
|
||||
<_description>
|
||||
<summary>Workspaces are managed dynamically</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="workspaces-only-on-primary" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Workspaces only on primary monitor</_summary>
|
||||
<_description>
|
||||
<summary>Workspaces only on primary monitor</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</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>
|
||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
1
data/theme/Gemfile
Normal file
@ -0,0 +1 @@
|
||||
gem "sass", "~> 3.4.0"
|
36
data/theme/README
Normal file
@ -0,0 +1,36 @@
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Do not edit the CSS directly, edit the source SCSS files and process them with SASS (running
|
||||
`make` should do that when you have the required software installed, as described below;
|
||||
run `/.parse-sass.sh` manually if it doesn't)
|
||||
* To be able to use the lates/adequate version of sass, install ruby, gem, sass & bundle.
|
||||
On Fedora F20, this is done with `sudo dnf install rubygems && gem install bundle && bundle install`
|
||||
from the same directory this README resides in.
|
||||
|
||||
How to tweak the theme
|
||||
----------------------
|
||||
|
||||
Adwaita is a complex theme, so to keep it maintainable it's written and processed in SASS, the
|
||||
generated CSS is then transformed into a gresource file during gtk build and used at runtime in a
|
||||
non-legible or editable form.
|
||||
|
||||
It is very likely your change will happen in the _common.scss file. That's where all the widget
|
||||
selectors are defined. Here's a rundown of the "supporting" stylesheets, that are unlikely to be the
|
||||
right place for a drive by stylesheet fix:
|
||||
|
||||
_colors.scss - global color definitions. We keep the number of defined colors to a necessary minimum,
|
||||
most colors are derived from a handful of basics. It is an exact copy of the gtk+
|
||||
counterpart. Light theme is used for the classic theme and dark is for GNOME3 shell
|
||||
default.
|
||||
|
||||
_drawing.scss - drawing helper mixings/functions to allow easier definition of widget drawing under
|
||||
specific context. This is why Adwaita isn't 15000 LOC.
|
||||
|
||||
_common.scss - actual definitions of style for each widget. This is where you are likely to add/remove
|
||||
your changes.
|
||||
|
||||
You can read about SASS at http://sass-lang.com/documentation/. Once you make your changes to the
|
||||
_common.scss file, you can either run the ./parse-sass.sh script or keep SASS watching for changes as you
|
||||
edit. This is done by running `bundle exec sass --watch --sourcemap=none .` If sass is out of date, or is
|
||||
missing, you can install it with `bundle install`.
|
@ -10,11 +10,11 @@
|
||||
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="29"
|
||||
height="29"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="calendar-today.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
@ -118,17 +118,6 @@
|
||||
fx="51"
|
||||
fy="30"
|
||||
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>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
@ -137,22 +126,23 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="20.652108"
|
||||
inkscape:cy="11.839084"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="-23.537329"
|
||||
inkscape:cy="-31.442864"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="741"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
borderlayer="true">
|
||||
inkscape:window-width="2133"
|
||||
inkscape:window-height="1241"
|
||||
inkscape:window-x="238"
|
||||
inkscape:window-y="88"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3109"
|
||||
@ -169,7 +159,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@ -177,28 +167,12 @@
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-469.08263,-532.99307)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
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"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect2996"
|
||||
width="31"
|
||||
height="3"
|
||||
x="468.08264"
|
||||
y="558.99304" />
|
||||
transform="translate(-469.08263,-537.99307)">
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="path7305"
|
||||
cx="481.57138"
|
||||
cy="559.4649"
|
||||
r="1.5" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.6 KiB |
@ -6,3 +6,9 @@
|
||||
stage {
|
||||
-st-icon-style: symbolic;
|
||||
}
|
||||
|
||||
.toggle-switch { width: 48px; }
|
||||
.toggle-switch-us, .toggle-switch-intl {
|
||||
background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg");
|
||||
&:checked { background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); }
|
||||
}
|
||||
|
119
data/theme/no-events.svg
Normal file
@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg3471"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="New document 5">
|
||||
<defs
|
||||
id="defs3473" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1461"
|
||||
inkscape:window-height="772"
|
||||
inkscape:window-x="37"
|
||||
inkscape:window-y="64"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata3476">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
transform="matrix(4,0,0,4,1.9999997,2.3636364)"
|
||||
id="g19145"
|
||||
style="fill:#bebebe;fill-opacity:1;display:inline">
|
||||
<g
|
||||
id="g19147"
|
||||
inkscape:label="status"
|
||||
style="fill:#bebebe;fill-opacity:1;display:inline"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
style="fill:#bebebe;fill-opacity:1"
|
||||
id="g19149"
|
||||
inkscape:label="devices"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
style="fill:#bebebe;fill-opacity:1"
|
||||
id="g19151"
|
||||
inkscape:label="apps"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
style="fill:#bebebe;fill-opacity:1"
|
||||
id="g19153"
|
||||
inkscape:label="places"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
style="fill:#bebebe;fill-opacity:1"
|
||||
id="g19155"
|
||||
inkscape:label="mimetypes"
|
||||
transform="translate(-541.0002,-301)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 543.0002,301 c -1.05237,0 -2,0.84508 -2,1.9375 l 0,11.125 c 0,1.09242 0.94763,1.9375 2,1.9375 l 11,0 c 1.05237,0 2,-0.84508 2,-1.9375 l 0,-11.125 c 0,-1.09242 -0.94763,-1.9375 -2,-1.9375 l -11,0 z m 0,5 3.03125,0 0,2 -3.03125,0 0,-2 z m 4.03125,0 2.96875,0 0,2 -2.96875,0 0,-2 z m 3.96875,0 3,0 0,2 -3,0 0,-2 z m -8,3 3.03125,0 0,2 -3.03125,0 0,-2 z m 4.03125,0 2.96875,0 0,2 -2.96875,0 0,-2 z m 3.96875,0 3,0 0,2 -3,0 0,-2 z m -8,3 3.03125,0 0,2 -3.03125,0 0,-2 z m 4.03125,0 2.96875,0 0,2 -2.96875,0 0,-2 z m 3.96875,0 3,0 0,2 -3,0 0,-2 z"
|
||||
id="path19157"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new;font-family:Sans;-inkscape-font-specification:Sans" />
|
||||
<rect
|
||||
height="1.9999993"
|
||||
id="rect19159"
|
||||
style="opacity:0.35;color:#000000;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
width="2.9999993"
|
||||
x="551.00018"
|
||||
y="309" />
|
||||
</g>
|
||||
<g
|
||||
id="g19161"
|
||||
inkscape:label="emblems"
|
||||
style="fill:#bebebe;fill-opacity:1;display:inline"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
id="g19163"
|
||||
inkscape:label="emotes"
|
||||
style="fill:#bebebe;fill-opacity:1;display:inline"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
id="g19165"
|
||||
inkscape:label="categories"
|
||||
style="fill:#bebebe;fill-opacity:1;display:inline"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
<g
|
||||
id="g19167"
|
||||
inkscape:label="actions"
|
||||
style="fill:#bebebe;fill-opacity:1;display:inline"
|
||||
transform="translate(-541.0002,-301)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
114
data/theme/no-notifications.svg
Normal file
@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg3393"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="New document 2">
|
||||
<defs
|
||||
id="defs3395" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="697"
|
||||
inkscape:window-height="613"
|
||||
inkscape:window-x="100"
|
||||
inkscape:window-y="77"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata3398">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
style="display:inline"
|
||||
transform="matrix(4,0,0,4,0.29733827,-0.35415646)"
|
||||
id="g19245">
|
||||
<g
|
||||
id="g19247"
|
||||
inkscape:label="status"
|
||||
style="display:inline"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19249"
|
||||
inkscape:label="devices"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19251"
|
||||
inkscape:label="apps"
|
||||
transform="translate(-323.02908,-649.02581)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 331.9377,653 c 0.0187,0.16677 0.0625,0.32822 0.0625,0.5 0,2.48528 -2.01472,4.5 -4.5,4.5 -0.11769,0 -0.22834,-0.0224 -0.34375,-0.0312 l 0,2.21875 c 0,1.00412 0.80838,1.8125 1.8125,1.8125 l 1.54511,-5e-5 2,2.04688 2.0625,-2.04688 1.61114,0 c 1.00413,0 1.8125,-0.80838 1.8125,-1.8125 l 0,-5.375 c 0,-1.00412 -0.80837,-1.8125 -1.8125,-1.8125 z"
|
||||
id="path19253"
|
||||
sodipodi:nodetypes="csscsscccssssc"
|
||||
style="opacity:0.5;color:#000000;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 327.5002,650 c -1.933,0 -3.5,1.567 -3.5,3.5 0,1.933 1.567,3.5 3.5,3.5 1.933,0 3.5,-1.567 3.5,-3.5 0,-1.933 -1.567,-3.5 -3.5,-3.5 z m -0.53125,1 1.03125,0 -0.0625,1.375 a 0.19951718,0.19951718 0 0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.125,0.125 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 l 1.15625,-0.75 0.5,0.90625 -1.21875,0.625 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0.0937 0.19951718,0.19951718 0 0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0625 0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0312 l 1.25,0.625 -0.53125,0.90625 -1.15625,-0.781 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,0 0.19951718,0.19951718 0 0 0 -0.125,0.0937 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0,0.0625 l 0.0625,1.3751 -1.03125,0 0.0937,-1.375 a 0.19951718,0.19951718 0 0 0 -0.0312,-0.0937 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0937,0.0312 l -1.1875,0.78125 -0.5,-0.90625 1.25,-0.625 a 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 -0.0312,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 l -1.25,-0.625 0.5,-0.90625 1.1875,0.75 a 0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 L 326.96895,651 z"
|
||||
id="path19255"
|
||||
style="color:#000000;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
<g
|
||||
id="g19257"
|
||||
inkscape:label="places"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19259"
|
||||
inkscape:label="mimetypes"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19261"
|
||||
inkscape:label="emblems"
|
||||
style="display:inline"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19263"
|
||||
inkscape:label="emotes"
|
||||
style="display:inline"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19265"
|
||||
inkscape:label="categories"
|
||||
style="display:inline"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
<g
|
||||
id="g19267"
|
||||
inkscape:label="actions"
|
||||
style="display:inline"
|
||||
transform="translate(-323.02908,-649.02581)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
3
data/theme/parse-sass.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
bundle exec sass --update --sourcemap=none .
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 98 KiB |
133
data/theme/toggle-off-hc.svg
Normal file
@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="48"
|
||||
height="22"
|
||||
id="svg2857"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="toggle-off-hc.svg">
|
||||
<defs
|
||||
id="defs2859">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective2865" />
|
||||
<inkscape:perspective
|
||||
id="perspective2843"
|
||||
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" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect77541-4"
|
||||
is_visible="true" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-6.1820581"
|
||||
inkscape:cy="-16.463788"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g37994"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1364"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
showborder="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid12954"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2862">
|
||||
<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"
|
||||
transform="translate(-444.64286,-781.36218)">
|
||||
<g
|
||||
transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)"
|
||||
id="g37994">
|
||||
<g
|
||||
transform="matrix(1.5323214,0,0,1.2413968,-324.76058,489.69039)"
|
||||
id="toggle-off"
|
||||
inkscape:label="#g8477">
|
||||
<circle
|
||||
cy="1033.993"
|
||||
cx="571.95966"
|
||||
id="path8444"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
r="7" />
|
||||
<rect
|
||||
ry="2.0108337"
|
||||
rx="1.9562569"
|
||||
y="1031.9885"
|
||||
x="565.0083"
|
||||
height="4.0216675"
|
||||
width="34.850178"
|
||||
id="rect8461"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.5323214,0,0,1.2413968,-324.85635,491.16456)"
|
||||
id="toggle-on"
|
||||
inkscape:label="#g8481">
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect8475"
|
||||
width="34.850178"
|
||||
height="4.0216675"
|
||||
x="565.0083"
|
||||
y="1070.9279"
|
||||
rx="1.9562569"
|
||||
ry="2.0108337" />
|
||||
<circle
|
||||
transform="scale(-1,1)"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle8463"
|
||||
cx="-591.0213"
|
||||
cy="1072.9402"
|
||||
r="9" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
113
data/theme/toggle-on-hc.svg
Normal file
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="48"
|
||||
height="22"
|
||||
id="svg2857"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="toggle-on-hc.svg">
|
||||
<defs
|
||||
id="defs2859">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective2865" />
|
||||
<inkscape:perspective
|
||||
id="perspective2843"
|
||||
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" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect77541-4"
|
||||
is_visible="true" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-222.95215"
|
||||
inkscape:cy="3.9378433"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g37994"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1364"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
showborder="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid12954"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2862">
|
||||
<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"
|
||||
transform="translate(-444.64286,-781.36218)">
|
||||
<g
|
||||
transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)"
|
||||
id="g37994">
|
||||
<g
|
||||
transform="matrix(1.5323214,0,0,1.2413968,-324.85635,441.50868)"
|
||||
id="toggle-on"
|
||||
inkscape:label="#g8481">
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect8475"
|
||||
width="34.850178"
|
||||
height="4.0216675"
|
||||
x="565.0083"
|
||||
y="1070.9279"
|
||||
rx="1.9562569"
|
||||
ry="2.0108337" />
|
||||
<circle
|
||||
transform="scale(-1,1)"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle8463"
|
||||
cx="-591.0213"
|
||||
cy="1072.9402"
|
||||
r="9" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.4 KiB |
@ -14,7 +14,7 @@
|
||||
height="22"
|
||||
id="svg2857"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="toggle-on-intl.svg">
|
||||
<defs
|
||||
id="defs2859">
|
||||
@ -52,7 +52,7 @@
|
||||
<stop
|
||||
id="stop77465"
|
||||
offset="1"
|
||||
style="stop-color:#204a87;stop-opacity:1" />
|
||||
style="stop-color:#205b9a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
@ -88,14 +88,14 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="17.255148"
|
||||
inkscape:cy="8.9252639"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="37.410841"
|
||||
inkscape:cy="16.009314"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g37994"
|
||||
showgrid="true"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1375"
|
||||
inkscape:window-height="1376"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
@ -14,7 +14,7 @@
|
||||
height="22"
|
||||
id="svg2857"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="toggle-on-us.svg">
|
||||
<defs
|
||||
id="defs2859">
|
||||
@ -32,28 +32,6 @@
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient77461"
|
||||
id="linearGradient77551"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
|
||||
x1="1164.7644"
|
||||
y1="962.93695"
|
||||
x2="1164.7644"
|
||||
y2="970.51404" />
|
||||
<linearGradient
|
||||
id="linearGradient77461"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop77463"
|
||||
offset="0"
|
||||
style="stop-color:#182f4c;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop77465"
|
||||
offset="1"
|
||||
style="stop-color:#204a87;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient76469-7-7-4"
|
||||
@ -80,6 +58,38 @@
|
||||
effect="spiro"
|
||||
id="path-effect77541-4"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
id="linearGradient77461-1"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop77463-1"
|
||||
offset="0"
|
||||
style="stop-color:#182f4c;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop77465-4"
|
||||
offset="1"
|
||||
style="stop-color:#205b9a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient77461-1"
|
||||
id="linearGradient77551-6-5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8527367,0,0,0.80554422,-969.41608,-778.00299)"
|
||||
x1="1164.7644"
|
||||
y1="962.93695"
|
||||
x2="1164.7644"
|
||||
y2="970.51404" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient77461-1"
|
||||
id="linearGradient11198"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.3066667,0,0,1,-1066.3709,794.25325)"
|
||||
x1="1322.5831"
|
||||
y1="-312.51855"
|
||||
x2="1322.5831"
|
||||
y2="-306.53461" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
@ -89,13 +99,13 @@
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="33.380898"
|
||||
inkscape:cy="6.9658271"
|
||||
inkscape:cx="-26.798898"
|
||||
inkscape:cy="5.3753009"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g37994"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1375"
|
||||
inkscape:window-height="1376"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
@ -152,7 +162,7 @@
|
||||
height="25"
|
||||
width="98"
|
||||
id="rect38000"
|
||||
style="color:#000000;fill:url(#linearGradient77551);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;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;enable-background:accumulate" />
|
||||
style="color:#000000;fill:url(#linearGradient11198);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;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;enable-background:accumulate;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(2.0625,-2)"
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.7 KiB |
@ -113,7 +113,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la -rpath $(MUTTER_TYPELIB_DIR)
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -78,7 +78,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=
|
||||
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la
|
||||
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la -rpath $(MUTTER_TYPELIB_DIR)
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -35,7 +35,6 @@
|
||||
<xi:include href="xml/st-bin.xml"/>
|
||||
<xi:include href="xml/st-box-layout.xml"/>
|
||||
<xi:include href="xml/st-scroll-view.xml"/>
|
||||
<xi:include href="xml/st-table.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="styling">
|
||||
|
@ -23,11 +23,6 @@ const GnomeShellIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const customCss = '.prefs-button { \
|
||||
padding: 8px; \
|
||||
border-radius: 20px; \
|
||||
}';
|
||||
|
||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||||
|
||||
function stripPrefix(string, prefix) {
|
||||
@ -54,7 +49,6 @@ const Application = new Lang.Class({
|
||||
this._startupUuid = null;
|
||||
this._loaded = false;
|
||||
this._skipMainWindow = false;
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
},
|
||||
|
||||
_extensionAvailable: function(uuid) {
|
||||
@ -158,6 +152,7 @@ const Application = new Lang.Class({
|
||||
let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
shadow_type: Gtk.ShadowType.IN,
|
||||
halign: Gtk.Align.CENTER,
|
||||
propagate_natural_width: true,
|
||||
margin: 18 });
|
||||
this._window.add(scroll);
|
||||
|
||||
@ -177,21 +172,6 @@ const Application = new Lang.Class({
|
||||
this._window.show_all();
|
||||
},
|
||||
|
||||
_addCustomStyle: function() {
|
||||
let provider = new Gtk.CssProvider();
|
||||
|
||||
try {
|
||||
provider.load_from_data(customCss, -1);
|
||||
} catch(e) {
|
||||
log('Failed to add application style');
|
||||
return;
|
||||
}
|
||||
|
||||
let screen = this._window.window.get_screen();
|
||||
let priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
|
||||
Gtk.StyleContext.add_provider_for_screen(screen, provider, priority);
|
||||
},
|
||||
|
||||
_sortList: function(row1, row2) {
|
||||
let name1 = ExtensionUtils.extensions[row1.uuid].metadata.name;
|
||||
let name2 = ExtensionUtils.extensions[row2.uuid].metadata.name;
|
||||
@ -240,7 +220,6 @@ const Application = new Lang.Class({
|
||||
|
||||
_onStartup: function(app) {
|
||||
this._buildUI(app);
|
||||
this._addCustomStyle();
|
||||
this._scanExtensions();
|
||||
},
|
||||
|
||||
@ -317,7 +296,7 @@ const ExtensionRow = new Lang.Class({
|
||||
button.add(new Gtk.Image({ icon_name: 'emblem-system-symbolic',
|
||||
icon_size: Gtk.IconSize.BUTTON,
|
||||
visible: true }));
|
||||
button.get_style_context().add_class('prefs-button');
|
||||
button.get_style_context().add_class('circular');
|
||||
hbox.add(button);
|
||||
|
||||
this.prefsButton = button;
|
||||
|
@ -14,7 +14,7 @@ const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
|
||||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 16;
|
||||
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
|
||||
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
|
||||
|
||||
@ -189,22 +189,21 @@ const AuthPrompt = new Lang.Class({
|
||||
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||
}));
|
||||
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
|
||||
this.emit('next');
|
||||
if (this.nextButton.reactive)
|
||||
this.emit('next');
|
||||
}));
|
||||
},
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
if (this._preemptiveAnswer) {
|
||||
if (this._queryingService)
|
||||
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||
this._preemptiveAnswer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._queryingService)
|
||||
this.clear();
|
||||
|
||||
this._queryingService = serviceName;
|
||||
if (this._preemptiveAnswer) {
|
||||
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||
this._preemptiveAnswer = null;
|
||||
return;
|
||||
}
|
||||
this.setPasswordChar(passwordChar);
|
||||
this.setQuestion(question);
|
||||
|
||||
@ -260,6 +259,7 @@ const AuthPrompt = new Lang.Class({
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this.setActorInDefaultButtonWell(null);
|
||||
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
|
||||
this.cancelButton.reactive = false;
|
||||
},
|
||||
@ -283,6 +283,12 @@ const AuthPrompt = new Lang.Class({
|
||||
if (oldActor)
|
||||
Tweener.removeTweens(oldActor);
|
||||
|
||||
let wasSpinner;
|
||||
if (oldActor == this._spinner.actor)
|
||||
wasSpinner = true;
|
||||
else
|
||||
wasSpinner = false;
|
||||
|
||||
let isSpinner;
|
||||
if (actor == this._spinner.actor)
|
||||
isSpinner = true;
|
||||
@ -292,6 +298,11 @@ const AuthPrompt = new Lang.Class({
|
||||
if (this._defaultButtonWellActor != actor && oldActor) {
|
||||
if (!animate) {
|
||||
oldActor.opacity = 0;
|
||||
|
||||
if (wasSpinner) {
|
||||
if (this._spinner)
|
||||
this._spinner.stop();
|
||||
}
|
||||
} else {
|
||||
Tweener.addTween(oldActor,
|
||||
{ opacity: 0,
|
||||
@ -300,7 +311,7 @@ const AuthPrompt = new Lang.Class({
|
||||
transition: 'linear',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
if (isSpinner) {
|
||||
if (wasSpinner) {
|
||||
if (this._spinner)
|
||||
this._spinner.stop();
|
||||
}
|
||||
@ -403,7 +414,7 @@ const AuthPrompt = new Lang.Class({
|
||||
},
|
||||
|
||||
updateSensitivity: function(sensitive) {
|
||||
this._updateNextButtonSensitivity(sensitive);
|
||||
this._updateNextButtonSensitivity(sensitive && this._entry.text.length > 0);
|
||||
this._entry.reactive = sensitive;
|
||||
this._entry.clutter_text.editable = sensitive;
|
||||
},
|
||||
@ -434,8 +445,9 @@ const AuthPrompt = new Lang.Class({
|
||||
let oldStatus = this.verificationStatus;
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
this.cancelButton.reactive = true;
|
||||
this.nextButton.label = _("Next");
|
||||
|
||||
if (oldStatus == AuthPromptStatus.VERIFYING)
|
||||
if (this._userVerifier)
|
||||
this._userVerifier.cancel();
|
||||
|
||||
this._queryingService = null;
|
||||
@ -490,6 +502,7 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
finish: function(onComplete) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._userVerifier.clear();
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
@ -497,12 +510,13 @@ const AuthPrompt = new Lang.Class({
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._userVerifier.clear();
|
||||
onComplete();
|
||||
}));
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this.verificationStatus == AuthPromptStatus.NOT_VERIFYING || this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) {
|
||||
if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) {
|
||||
return;
|
||||
}
|
||||
this.reset();
|
||||
|
@ -16,6 +16,34 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* In order for transformation animations to look good, they need to be
|
||||
* incremental and have some order to them (e.g., fade out hidden items,
|
||||
* then shrink to close the void left over). Chaining animations in this way can
|
||||
* be error-prone and wordy using just Tweener callbacks.
|
||||
*
|
||||
* The classes in this file help with this:
|
||||
*
|
||||
* - Task. encapsulates schedulable work to be run in a specific scope.
|
||||
*
|
||||
* - ConsecutiveBatch. runs a series of tasks in order and completes
|
||||
* when the last in the series finishes.
|
||||
*
|
||||
* - ConcurrentBatch. runs a set of tasks at the same time and completes
|
||||
* when the last to finish completes.
|
||||
*
|
||||
* - Hold. prevents a batch from completing the pending task until
|
||||
* the hold is released.
|
||||
*
|
||||
* The tasks associated with a batch are specified in a list at batch
|
||||
* construction time as either task objects or plain functions.
|
||||
* Batches are task objects, themselves, so they can be nested.
|
||||
*
|
||||
* These classes aren't specific to GDM, but were found to be unintuitive and so
|
||||
* are not used elsewhere. These APIs may ultimately get dropped entirely and
|
||||
* replaced by something else.
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
|
@ -22,6 +22,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -48,6 +49,7 @@ const _FADE_ANIMATION_TIME = 0.25;
|
||||
const _SCROLL_ANIMATION_TIME = 0.5;
|
||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||
const _LOGO_ICON_HEIGHT = 48;
|
||||
const _MAX_BOTTOM_MENU_ITEMS = 5;
|
||||
|
||||
const UserListItem = new Lang.Class({
|
||||
Name: 'UserListItem',
|
||||
@ -71,6 +73,9 @@ const UserListItem = new Lang.Class({
|
||||
this._userWidget = new UserWidget.UserWidget(this.user);
|
||||
layout.add(this._userWidget.actor);
|
||||
|
||||
this._userWidget.actor.bind_property('label-actor', this.actor, 'label-actor',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
|
||||
scale_x: 0 });
|
||||
layout.add(this._timedLoginIndicator);
|
||||
@ -91,7 +96,7 @@ const UserListItem = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userChangedId);
|
||||
this.user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
@ -207,6 +212,10 @@ const UserList = new Lang.Class({
|
||||
return item;
|
||||
},
|
||||
|
||||
containsUser: function(user) {
|
||||
return this._items[user.get_user_name()] != null;
|
||||
},
|
||||
|
||||
addUser: function(user) {
|
||||
if (!user.is_loaded)
|
||||
return;
|
||||
@ -279,7 +288,16 @@ const SessionMenuButton = new Lang.Class({
|
||||
|
||||
this.actor = new St.Bin({ child: this._button });
|
||||
|
||||
this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP);
|
||||
let side = St.Side.TOP;
|
||||
let align = 0;
|
||||
if (Gdm.get_session_ids().length > _MAX_BOTTOM_MENU_ITEMS) {
|
||||
if (this.actor.text_direction == Clutter.TextDirection.RTL)
|
||||
side = St.Side.RIGHT;
|
||||
else
|
||||
side = St.Side.LEFT;
|
||||
align = 0.5;
|
||||
}
|
||||
this._menu = new PopupMenu.PopupMenu(this._button, align, side);
|
||||
Main.uiGroup.add_actor(this._menu.actor);
|
||||
this._menu.actor.hide();
|
||||
|
||||
@ -473,6 +491,10 @@ const LoginDialog = new Lang.Class({
|
||||
this._disableUserList = undefined;
|
||||
this._userListLoaded = false;
|
||||
|
||||
this._realmManager = new Realmd.Manager();
|
||||
this._realmSignalId = this._realmManager.connect('login-format-changed',
|
||||
Lang.bind(this, this._showRealmLoginHint));
|
||||
|
||||
LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy));
|
||||
|
||||
// If the user list is enabled, it should take key focus; make sure the
|
||||
@ -488,7 +510,7 @@ const LoginDialog = new Lang.Class({
|
||||
let [minWidth, minHeight, natWidth, natHeight] = this._bannerView.get_preferred_size();
|
||||
let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2;
|
||||
|
||||
actorBox.x1 = centerX - natWidth / 2;
|
||||
actorBox.x1 = Math.floor(centerX - natWidth / 2);
|
||||
actorBox.y1 = dialogBox.y1 + Main.layoutManager.panelBox.height;
|
||||
actorBox.x2 = actorBox.x1 + natWidth;
|
||||
actorBox.y2 = actorBox.y1 + natHeight;
|
||||
@ -502,7 +524,7 @@ const LoginDialog = new Lang.Class({
|
||||
let [minWidth, minHeight, natWidth, natHeight] = this._logoBin.get_preferred_size();
|
||||
let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2;
|
||||
|
||||
actorBox.x1 = centerX - natWidth / 2;
|
||||
actorBox.x1 = Math.floor(centerX - natWidth / 2);
|
||||
actorBox.y1 = dialogBox.y2 - natHeight;
|
||||
actorBox.x2 = actorBox.x1 + natWidth;
|
||||
actorBox.y2 = actorBox.y1 + natHeight;
|
||||
@ -517,8 +539,11 @@ const LoginDialog = new Lang.Class({
|
||||
let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2;
|
||||
let centerY = dialogBox.y1 + (dialogBox.y2 - dialogBox.y1) / 2;
|
||||
|
||||
actorBox.x1 = centerX - natWidth / 2;
|
||||
actorBox.y1 = centerY - natHeight / 2;
|
||||
natWidth = Math.min(natWidth, dialogBox.x2 - dialogBox.x1);
|
||||
natHeight = Math.min(natHeight, dialogBox.y2 - dialogBox.y1);
|
||||
|
||||
actorBox.x1 = Math.floor(centerX - natWidth / 2);
|
||||
actorBox.y1 = Math.floor(centerY - natHeight / 2);
|
||||
actorBox.x2 = actorBox.x1 + natWidth;
|
||||
actorBox.y2 = actorBox.y1 + natHeight;
|
||||
|
||||
@ -566,14 +591,21 @@ const LoginDialog = new Lang.Class({
|
||||
// try a different layout, or if we have what extra space we
|
||||
// can hand out
|
||||
if (bannerAllocation) {
|
||||
let leftOverYSpace = dialogHeight - bannerHeight - authPromptHeight - logoHeight;
|
||||
let bannerSpace;
|
||||
|
||||
if (authPromptAllocation)
|
||||
bannerSpace = authPromptAllocation.y1 - bannerAllocation.y1;
|
||||
else
|
||||
bannerSpace = 0;
|
||||
|
||||
let leftOverYSpace = bannerSpace - bannerHeight;
|
||||
|
||||
if (leftOverYSpace > 0) {
|
||||
// First figure out how much left over space is up top
|
||||
let leftOverTopSpace = leftOverYSpace / 2;
|
||||
|
||||
// Then, shift the banner into the middle of that extra space
|
||||
let yShift = leftOverTopSpace / 2;
|
||||
let yShift = Math.floor(leftOverTopSpace / 2);
|
||||
|
||||
bannerAllocation.y1 += yShift;
|
||||
bannerAllocation.y2 += yShift;
|
||||
@ -599,8 +631,8 @@ const LoginDialog = new Lang.Class({
|
||||
let centerGap = wideSpacing / 8;
|
||||
|
||||
// place the banner along the left edge of the center margin
|
||||
bannerAllocation.x2 = centerX - centerGap / 2;
|
||||
bannerAllocation.x1 = bannerAllocation.x2 - wideBannerWidth;
|
||||
bannerAllocation.x2 = Math.floor(centerX - centerGap / 2);
|
||||
bannerAllocation.x1 = Math.floor(bannerAllocation.x2 - wideBannerWidth);
|
||||
|
||||
// figure out how tall it would like to be and try to accomodate
|
||||
// but don't let it get too close to the logo
|
||||
@ -608,11 +640,11 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
let maxWideHeight = dialogHeight - 3 * logoHeight;
|
||||
wideBannerHeight = Math.min(maxWideHeight, wideBannerHeight);
|
||||
bannerAllocation.y1 = centerY - wideBannerHeight / 2;
|
||||
bannerAllocation.y1 = Math.floor(centerY - wideBannerHeight / 2);
|
||||
bannerAllocation.y2 = bannerAllocation.y1 + wideBannerHeight;
|
||||
|
||||
// place the auth prompt along the right edge of the center margin
|
||||
authPromptAllocation.x1 = centerX + centerGap / 2;
|
||||
authPromptAllocation.x1 = Math.floor(centerX + centerGap / 2);
|
||||
authPromptAllocation.x2 = authPromptAllocation.x1 + authPromptWidth;
|
||||
} else {
|
||||
// If we aren't going to do a wide view, then we need to limit
|
||||
@ -622,7 +654,7 @@ const LoginDialog = new Lang.Class({
|
||||
leftOverYSpace += bannerHeight;
|
||||
|
||||
// Then figure out how much of that space is up top
|
||||
let availableTopSpace = leftOverYSpace / 2;
|
||||
let availableTopSpace = Math.floor(leftOverYSpace / 2);
|
||||
|
||||
// Then give all of that space to the banner
|
||||
bannerAllocation.y2 = bannerAllocation.y1 + availableTopSpace;
|
||||
@ -633,7 +665,7 @@ const LoginDialog = new Lang.Class({
|
||||
let leftOverYSpace = dialogHeight - userSelectionHeight - logoHeight;
|
||||
|
||||
if (leftOverYSpace > 0) {
|
||||
let topExpansion = leftOverYSpace / 2;
|
||||
let topExpansion = Math.floor(leftOverYSpace / 2);
|
||||
let bottomExpansion = topExpansion;
|
||||
|
||||
userSelectionAllocation.y1 -= topExpansion;
|
||||
@ -772,6 +804,11 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
this._user = null;
|
||||
|
||||
if (this._nextSignalId) {
|
||||
this._authPrompt.disconnect(this._nextSignalId);
|
||||
this._nextSignalId = 0;
|
||||
}
|
||||
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
if (!this._disableUserList)
|
||||
this._showUserList();
|
||||
@ -826,25 +863,22 @@ const LoginDialog = new Lang.Class({
|
||||
this._authPrompt.setPasswordChar('');
|
||||
this._authPrompt.setQuestion(_("Username: "));
|
||||
|
||||
let realmManager = new Realmd.Manager();
|
||||
let realmSignalId = realmManager.connect('login-format-changed',
|
||||
Lang.bind(this, this._showRealmLoginHint));
|
||||
this._showRealmLoginHint(realmManager.loginFormat);
|
||||
this._showRealmLoginHint(this._realmManager.loginFormat);
|
||||
|
||||
let nextSignalId = this._authPrompt.connect('next',
|
||||
Lang.bind(this, function() {
|
||||
this._authPrompt.disconnect(nextSignalId);
|
||||
this._authPrompt.updateSensitivity(false);
|
||||
let answer = this._authPrompt.getAnswer();
|
||||
this._user = this._userManager.get_user(answer);
|
||||
this._authPrompt.clear();
|
||||
this._authPrompt.startSpinning();
|
||||
this._authPrompt.begin({ userName: answer });
|
||||
this._updateCancelButton();
|
||||
|
||||
realmManager.disconnect(realmSignalId)
|
||||
realmManager.release();
|
||||
}));
|
||||
if (this._nextSignalId)
|
||||
this._authPrompt.disconnect(this._nextSignalId);
|
||||
this._nextSignalId = this._authPrompt.connect('next',
|
||||
Lang.bind(this, function() {
|
||||
this._authPrompt.disconnect(this._nextSignalId);
|
||||
this._nextSignalId = 0;
|
||||
this._authPrompt.updateSensitivity(false);
|
||||
let answer = this._authPrompt.getAnswer();
|
||||
this._user = this._userManager.get_user(answer);
|
||||
this._authPrompt.clear();
|
||||
this._authPrompt.startSpinning();
|
||||
this._authPrompt.begin({ userName: answer });
|
||||
this._updateCancelButton();
|
||||
}));
|
||||
this._updateCancelButton();
|
||||
|
||||
this._authPrompt.updateSensitivity(true);
|
||||
@ -852,7 +886,7 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_loginScreenSessionActivated: function() {
|
||||
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||
if (this.actor.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
@ -869,7 +903,8 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
onUpdateScope: this,
|
||||
onComplete: function() {
|
||||
this._authPrompt.reset();
|
||||
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
|
||||
this._authPrompt.reset();
|
||||
},
|
||||
onCompleteScope: this });
|
||||
},
|
||||
@ -898,11 +933,7 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
onUpdateScope: this,
|
||||
onComplete: function() {
|
||||
let id = Mainloop.idle_add(Lang.bind(this, function() {
|
||||
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] this._greeter.call_start_session_when_ready_sync');
|
||||
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||
},
|
||||
onCompleteScope: this });
|
||||
},
|
||||
@ -1082,18 +1113,11 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onUserListActivated: function(activatedItem) {
|
||||
let tasks = [function() {
|
||||
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
},
|
||||
function() {
|
||||
this._setUserListExpanded(false);
|
||||
}];
|
||||
|
||||
this._user = activatedItem.user;
|
||||
|
||||
this._updateCancelButton();
|
||||
|
||||
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
|
||||
let batch = new Batch.ConcurrentBatch(this, [GdmUtil.cloneAndFadeOutActor(this._userSelectionBox),
|
||||
this._beginVerificationForItem(activatedItem)]);
|
||||
batch.run();
|
||||
},
|
||||
@ -1111,6 +1135,10 @@ const LoginDialog = new Lang.Class({
|
||||
this._userManager.disconnect(this._userRemovedId);
|
||||
this._userRemovedId = 0;
|
||||
}
|
||||
if (this._userChangedId) {
|
||||
this._userManager.disconnect(this._userChangedId);
|
||||
this._userChangedId = 0;
|
||||
}
|
||||
this._textureCache.disconnect(this._updateLogoTextureId);
|
||||
Main.layoutManager.disconnect(this._startupCompleteId);
|
||||
if (this._settings) {
|
||||
@ -1127,6 +1155,12 @@ const LoginDialog = new Lang.Class({
|
||||
this._greeterSessionProxy.disconnect(this._greeterSessionProxyChangedId);
|
||||
this._greeterSessionProxy = null;
|
||||
}
|
||||
if (this._realmManager) {
|
||||
this._realmManager.disconnect(this._realmSignalId);
|
||||
this._realmSignalId = 0;
|
||||
this._realmManager.release();
|
||||
this._realmManager = null;
|
||||
}
|
||||
},
|
||||
|
||||
_loadUserList: function() {
|
||||
@ -1151,6 +1185,14 @@ const LoginDialog = new Lang.Class({
|
||||
this._userList.removeUser(user);
|
||||
}));
|
||||
|
||||
this._userChangedId = this._userManager.connect('user-changed',
|
||||
Lang.bind(this, function(userManager, user) {
|
||||
if (this._userList.containsUser(user) && user.locked)
|
||||
this._userList.removeUser(user);
|
||||
else if (!this._userList.containsUser(user) && !user.locked)
|
||||
this._userList.addUser(user);
|
||||
}));
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
|
@ -35,8 +35,8 @@ 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
|
||||
// Give user 48ms to read each character of a PAM message
|
||||
const USER_READ_TIME = 48
|
||||
|
||||
const MessageType = {
|
||||
NONE: 0,
|
||||
@ -410,7 +410,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
_updateDefaultService: function() {
|
||||
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
|
||||
this._defaultService = PASSWORD_SERVICE_NAME;
|
||||
else if (this.smartcardDetected)
|
||||
else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||
this._defaultService = SMARTCARD_SERVICE_NAME;
|
||||
else if (this._haveFingerprintReader)
|
||||
this._defaultService = FINGERPRINT_SERVICE_NAME;
|
||||
|
@ -31,10 +31,12 @@
|
||||
|
||||
<file>portalHelper/main.js</file>
|
||||
|
||||
<file>ui/accessDialog.js</file>
|
||||
<file>ui/altTab.js</file>
|
||||
<file>ui/animation.js</file>
|
||||
<file>ui/appDisplay.js</file>
|
||||
<file>ui/appFavorites.js</file>
|
||||
<file>ui/audioDeviceSelection.js</file>
|
||||
<file>ui/backgroundMenu.js</file>
|
||||
<file>ui/background.js</file>
|
||||
<file>ui/boxpointer.js</file>
|
||||
@ -57,11 +59,14 @@
|
||||
<file>ui/layout.js</file>
|
||||
<file>ui/lightbox.js</file>
|
||||
<file>ui/lookingGlass.js</file>
|
||||
<file>ui/legacyTray.js</file>
|
||||
<file>ui/magnifier.js</file>
|
||||
<file>ui/magnifierDBus.js</file>
|
||||
<file>ui/main.js</file>
|
||||
<file>ui/messageTray.js</file>
|
||||
<file>ui/messageList.js</file>
|
||||
<file>ui/modalDialog.js</file>
|
||||
<file>ui/mpris.js</file>
|
||||
<file>ui/notificationDaemon.js</file>
|
||||
<file>ui/osdWindow.js</file>
|
||||
<file>ui/osdMonitorLabeler.js</file>
|
||||
|
@ -40,6 +40,9 @@ const SystemdLoginSessionIface = '<node> \
|
||||
<signal name="Lock" /> \
|
||||
<signal name="Unlock" /> \
|
||||
<property name="Active" type="b" access="read" /> \
|
||||
<method name="SetLockedHint"> \
|
||||
<arg type="b" direction="in"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
@ -131,10 +134,13 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
this._proxy.CanSuspendRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
if (error) {
|
||||
asyncCallback(false, false);
|
||||
} else {
|
||||
let needsAuth = result[0] == 'challenge';
|
||||
let canSuspend = needsAuth || result[0] == 'yes';
|
||||
asyncCallback(canSuspend, needsAuth);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -187,7 +193,7 @@ const LoginManagerDummy = new Lang.Class({
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
asyncCallback(false);
|
||||
asyncCallback(false, false);
|
||||
},
|
||||
|
||||
listSessions: function(asyncCallback) {
|
||||
|
103
js/misc/util.js
@ -4,10 +4,12 @@ const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const SCROLL_TIME = 0.1;
|
||||
|
||||
@ -38,6 +40,8 @@ const _urlRegexp = new RegExp(
|
||||
')' +
|
||||
')', 'gi');
|
||||
|
||||
let _desktopSettings = null;
|
||||
|
||||
// findUrls:
|
||||
// @str: string to find URLs in
|
||||
//
|
||||
@ -157,6 +161,105 @@ function _handleSpawnError(command, err) {
|
||||
Main.notifyError(title, err.message);
|
||||
}
|
||||
|
||||
function formatTime(time, params) {
|
||||
let date;
|
||||
// HACK: The built-in Date type sucks at timezones, which we need for the
|
||||
// world clock; it's often more convenient though, so allow either
|
||||
// Date or GLib.DateTime as parameter
|
||||
if (time instanceof Date)
|
||||
date = GLib.DateTime.new_from_unix_local(time.getTime() / 1000);
|
||||
else
|
||||
date = time;
|
||||
|
||||
let now = GLib.DateTime.new_now_local();
|
||||
|
||||
let daysAgo = now.difference(date) / (24 * 60 * 60 * 1000 * 1000);
|
||||
|
||||
let format;
|
||||
|
||||
if (_desktopSettings == null)
|
||||
_desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
let clockFormat = _desktopSettings.get_string('clock-format');
|
||||
let hasAmPm = date.format('%p') != '';
|
||||
|
||||
params = Params.parse(params, { timeOnly: false });
|
||||
|
||||
if (clockFormat == '24h' || !hasAmPm) {
|
||||
// Show only the time if date is on today
|
||||
if (daysAgo < 1 || params.timeOnly)
|
||||
/* Translators: Time in 24h format */
|
||||
format = N_("%H\u2236%M");
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if (daysAgo <2)
|
||||
/* Translators: this is the word "Yesterday" followed by a
|
||||
time string in 24h format. i.e. "Yesterday, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("Yesterday, %H\u2236%M");
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7)
|
||||
/* Translators: this is the week day name followed by a time
|
||||
string in 24h format. i.e. "Monday, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%A, %H\u2236%M");
|
||||
else if (date.get_year() == now.get_year())
|
||||
/* Translators: this is the month name and day number
|
||||
followed by a time string in 24h format.
|
||||
i.e. "May 25, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d, %H\u2236%M");
|
||||
else
|
||||
/* Translators: this is the month name, day number, year
|
||||
number followed by a time string in 24h format.
|
||||
i.e. "May 25 2012, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d %Y, %H\u2236%M");
|
||||
} else {
|
||||
// Show only the time if date is on today
|
||||
if (daysAgo < 1 || params.timeOnly)
|
||||
/* Translators: Time in 12h format */
|
||||
format = N_("%l\u2236%M %p");
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if (daysAgo <2)
|
||||
/* Translators: this is the word "Yesterday" followed by a
|
||||
time string in 12h format. i.e. "Yesterday, 2:30 pm" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("Yesterday, %l\u2236%M %p");
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7)
|
||||
/* Translators: this is the week day name followed by a time
|
||||
string in 12h format. i.e. "Monday, 2:30 pm" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%A, %l\u2236%M %p");
|
||||
else if (date.get_year() == now.get_year())
|
||||
/* Translators: this is the month name and day number
|
||||
followed by a time string in 12h format.
|
||||
i.e. "May 25, 2:30 pm" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d, %l\u2236%M %p");
|
||||
else
|
||||
/* Translators: this is the month name, day number, year
|
||||
number followed by a time string in 12h format.
|
||||
i.e. "May 25 2012, 2:30 pm"*/
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d %Y, %l\u2236%M %p");
|
||||
}
|
||||
return date.format(Shell.util_translate_time_string(format));
|
||||
}
|
||||
|
||||
function createTimeLabel(date, params) {
|
||||
if (_desktopSettings == null)
|
||||
_desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
|
||||
let label = new St.Label({ text: formatTime(date, params) });
|
||||
let id = _desktopSettings.connect('changed::clock-format', function() {
|
||||
label.text = formatTime(date, params);
|
||||
});
|
||||
label.connect('destroy', function() {
|
||||
_desktopSettings.disconnect(id);
|
||||
});
|
||||
return label;
|
||||
}
|
||||
|
||||
// lowerBound:
|
||||
// @array: an array or array-like object, already sorted
|
||||
// according to @cmp
|
||||
|
202
js/ui/accessDialog.js
Normal file
@ -0,0 +1,202 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const RequestIface = '<node> \
|
||||
<interface name="org.freedesktop.impl.portal.Request"> \
|
||||
<method name="Close"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const AccessIface = '<node> \
|
||||
<interface name="org.freedesktop.impl.portal.Access"> \
|
||||
<method name="AccessDialog"> \
|
||||
<arg type="o" name="handle" direction="in"/> \
|
||||
<arg type="s" name="app_id" direction="in"/> \
|
||||
<arg type="s" name="parent_window" direction="in"/> \
|
||||
<arg type="s" name="title" direction="in"/> \
|
||||
<arg type="s" name="subtitle" direction="in"/> \
|
||||
<arg type="s" name="body" direction="in"/> \
|
||||
<arg type="a{sv}" name="options" direction="in"/> \
|
||||
<arg type="u" name="response" direction="out"/> \
|
||||
<arg type="a{sv}" name="results" direction="out"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const DialogResponse = {
|
||||
OK: 0,
|
||||
CANCEL: 1,
|
||||
CLOSED: 2
|
||||
};
|
||||
|
||||
const AccessDialog = new Lang.Class({
|
||||
Name: 'AccessDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(invocation, handle, title, subtitle, body, options) {
|
||||
this.parent({ styleClass: 'access-dialog' });
|
||||
|
||||
this._invocation = invocation;
|
||||
this._handle = handle;
|
||||
|
||||
this._requestExported = false;
|
||||
this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this);
|
||||
|
||||
for (let option in options)
|
||||
options[option] = options[option].deep_unpack();
|
||||
|
||||
this._buildLayout(title, subtitle, body, options);
|
||||
},
|
||||
|
||||
_buildLayout: function(title, subtitle, body, options) {
|
||||
// No support for non-modal system dialogs, so ignore the option
|
||||
//let modal = options['modal'] || true;
|
||||
let denyLabel = options['deny_label'] || _("Deny Access");
|
||||
let grantLabel = options['grant_label'] || _("Grant Access");
|
||||
let iconName = options['icon'] || null;
|
||||
let choices = options['choices'] || [];
|
||||
|
||||
let mainContentBox = new St.BoxLayout();
|
||||
mainContentBox.style_class = 'access-dialog-main-layout';
|
||||
this.contentLayout.add_actor(mainContentBox);
|
||||
|
||||
let icon = new St.Icon({ style_class: 'access-dialog-icon',
|
||||
icon_name: iconName,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
mainContentBox.add_actor(icon);
|
||||
|
||||
let messageBox = new St.BoxLayout({ vertical: true });
|
||||
messageBox.style_class = 'access-dialog-content',
|
||||
mainContentBox.add_actor(messageBox);
|
||||
|
||||
let label;
|
||||
label = new St.Label({ style_class: 'access-dialog-title headline',
|
||||
text: title });
|
||||
messageBox.add_actor(label);
|
||||
|
||||
label = new St.Label({ style_class: 'access-dialog-subtitle',
|
||||
text: subtitle });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
label.clutter_text.line_wrap = true;
|
||||
messageBox.add_actor(label);
|
||||
|
||||
this._choices = new Map();
|
||||
|
||||
for (let i = 0; i < choices.length; i++) {
|
||||
let [id, name, opts, selected] = choices[i];
|
||||
if (opts.length > 0)
|
||||
continue; // radio buttons, not implemented
|
||||
|
||||
let check = new CheckBox.CheckBox();
|
||||
check.getLabelActor().text = name;
|
||||
check.actor.checked = selected == "true";
|
||||
messageBox.add_actor(check.actor);
|
||||
|
||||
this._choices.set(id, check);
|
||||
}
|
||||
|
||||
label = new St.Label({ text: body });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
label.clutter_text.line_wrap = true;
|
||||
messageBox.add_actor(label);
|
||||
|
||||
this.addButton({ label: denyLabel,
|
||||
action: () => {
|
||||
this._sendResponse(DialogResponse.CANCEL);
|
||||
},
|
||||
key: Clutter.KEY_Escape });
|
||||
this.addButton({ label: grantLabel,
|
||||
action: () => {
|
||||
this._sendResponse(DialogResponse.OK);
|
||||
}});
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this.parent();
|
||||
|
||||
let connection = this._invocation.get_connection();
|
||||
this._requestExported = this._request.export(connection, this._handle);
|
||||
},
|
||||
|
||||
CloseAsync: function(invocation, params) {
|
||||
if (this._invocation.get_sender() != invocation.get_sender()) {
|
||||
invocation.return_error_literal(Gio.DBusError,
|
||||
Gio.DBusError.ACCESS_DENIED,
|
||||
'');
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendResponse(DialogResponse.CLOSED);
|
||||
},
|
||||
|
||||
_sendResponse: function(response) {
|
||||
if (this._requestExported)
|
||||
this._request.unexport();
|
||||
this._requestExported = false;
|
||||
|
||||
let results = {};
|
||||
if (response == DialogResponse.OK) {
|
||||
for (let [id, check] of this._choices) {
|
||||
let checked = check.actor.checked ? 'true' : 'false';
|
||||
results[id] = new GLib.Variant('s', checked);
|
||||
}
|
||||
}
|
||||
|
||||
// Delay actual response until the end of the close animation (if any)
|
||||
this.connect('closed', () => {
|
||||
this._invocation.return_value(new GLib.Variant('(ua{sv})',
|
||||
[response, results]));
|
||||
});
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
const AccessDialogDBus = new Lang.Class({
|
||||
Name: 'AccessDialogDBus',
|
||||
|
||||
_init: function() {
|
||||
this._accessDialog = null;
|
||||
|
||||
this._windowTracker = Shell.WindowTracker.get_default();
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop');
|
||||
|
||||
Gio.DBus.session.own_name('org.freedesktop.impl.portal.desktop.gnome', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
AccessDialogAsync: function(params, invocation) {
|
||||
if (this._accessDialog) {
|
||||
invocation.return_error_literal(Gio.DBusError,
|
||||
Gio.DBusError.LIMITS_EXCEEDED,
|
||||
'Already showing a system access dialog');
|
||||
return;
|
||||
}
|
||||
|
||||
let [handle, appId, parentWindow, title, subtitle, body, options] = params;
|
||||
// We probably want to use parentWindow and global.display.focus_window
|
||||
// for this check in the future
|
||||
if (appId && appId + '.desktop' != this._windowTracker.focus_app.id) {
|
||||
invocation.return_error_literal(Gio.DBusError,
|
||||
Gio.DBusError.ACCESS_DENIED,
|
||||
'Only the focused app is allowed to show a system access dialog');
|
||||
return;
|
||||
}
|
||||
|
||||
let dialog = new AccessDialog(invocation, handle, title,
|
||||
subtitle, body, options);
|
||||
dialog.open();
|
||||
|
||||
dialog.connect('closed', () => { this._accessDialog = null; });
|
||||
|
||||
this._accessDialog = dialog;
|
||||
}
|
||||
});
|
194
js/ui/altTab.js
@ -46,6 +46,19 @@ function _createWindowClone(window, size) {
|
||||
y_expand: true });
|
||||
};
|
||||
|
||||
function getWindows(workspace) {
|
||||
// We ignore skip-taskbar windows in switchers, but if they are attached
|
||||
// to their parent, their position in the MRU list may be more appropriate
|
||||
// than the parent; so start with the complete list ...
|
||||
let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL,
|
||||
workspace);
|
||||
// ... map windows to their parent where appropriate ...
|
||||
return windows.map(w => {
|
||||
return w.is_attached_dialog() ? w.get_transient_for() : w;
|
||||
// ... and filter out skip-taskbar windows and duplicates
|
||||
}).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) == i);
|
||||
}
|
||||
|
||||
const AppSwitcherPopup = new Lang.Class({
|
||||
Name: 'AppSwitcherPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
@ -354,6 +367,149 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const CyclerHighlight = new Lang.Class({
|
||||
Name: 'CyclerHighlight',
|
||||
|
||||
_init: function() {
|
||||
this._window = null;
|
||||
|
||||
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
|
||||
this._clone = new Clutter.Clone();
|
||||
this.actor.add_actor(this._clone);
|
||||
|
||||
this._highlight = new St.Widget({ style_class: 'cycler-highlight' });
|
||||
this.actor.add_actor(this._highlight);
|
||||
|
||||
let coordinate = Clutter.BindCoordinate.ALL;
|
||||
let constraint = new Clutter.BindConstraint({ coordinate: coordinate });
|
||||
this._clone.bind_property('source', constraint, 'source', 0);
|
||||
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this.actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._onAllocationChanged));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
},
|
||||
|
||||
set window(w) {
|
||||
if (this._window == w)
|
||||
return;
|
||||
|
||||
this._window = w;
|
||||
|
||||
if (this._clone.source)
|
||||
this._clone.source.sync_visibility();
|
||||
|
||||
let windowActor = this._window ? this._window.get_compositor_private()
|
||||
: null;
|
||||
|
||||
if (windowActor)
|
||||
windowActor.hide();
|
||||
|
||||
this._clone.source = windowActor;
|
||||
},
|
||||
|
||||
_onAllocationChanged: function() {
|
||||
if (!this._window) {
|
||||
this._highlight.set_size(0, 0);
|
||||
this._highlight.hide();
|
||||
} else {
|
||||
let [x, y] = this.actor.allocation.get_origin();
|
||||
let rect = this._window.get_frame_rect();
|
||||
this._highlight.set_size(rect.width, rect.height);
|
||||
this._highlight.set_position(rect.x - x, rect.y - y);
|
||||
this._highlight.show();
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this.window = null;
|
||||
}
|
||||
});
|
||||
|
||||
const CyclerPopup = new Lang.Class({
|
||||
Name: 'CyclerPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
Abstract: true,
|
||||
|
||||
_init : function() {
|
||||
this.parent();
|
||||
|
||||
this._items = this._getWindows();
|
||||
|
||||
if (this._items.length == 0)
|
||||
return;
|
||||
|
||||
this._highlight = new CyclerHighlight();
|
||||
global.window_group.add_actor(this._highlight.actor);
|
||||
|
||||
// We don't show an actual popup, so just provide what SwitcherPopup
|
||||
// expects instead of inheriting from SwitcherList
|
||||
this._switcherList = { actor: new St.Widget(),
|
||||
highlight: Lang.bind(this, this._highlightItem),
|
||||
connect: function() {} };
|
||||
},
|
||||
|
||||
_highlightItem: function(index, justOutline) {
|
||||
this._highlight.window = this._items[index];
|
||||
global.window_group.set_child_above_sibling(this._highlight.actor, null);
|
||||
},
|
||||
|
||||
_finish: function() {
|
||||
let window = this._items[this._selectedIndex];
|
||||
let ws = window.get_workspace();
|
||||
let activeWs = global.screen.get_active_workspace();
|
||||
|
||||
if (window.minimized) {
|
||||
Main.wm.skipNextEffect(window.get_compositor_private());
|
||||
window.unminimize();
|
||||
}
|
||||
|
||||
if (activeWs == ws) {
|
||||
Main.activateWindow(window);
|
||||
} else {
|
||||
// If the selected window is on a different workspace, we don't
|
||||
// want it to disappear, then slide in with the workspace; instead,
|
||||
// always activate it on the active workspace ...
|
||||
activeWs.activate_with_focus(window, global.get_current_time());
|
||||
|
||||
// ... then slide it over to the original workspace if necessary
|
||||
Main.wm.actionMoveWindow(window, ws);
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._highlight.actor.destroy();
|
||||
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const GroupCyclerPopup = new Lang.Class({
|
||||
Name: 'GroupCyclerPopup',
|
||||
Extends: CyclerPopup,
|
||||
|
||||
_getWindows: function() {
|
||||
let app = Shell.WindowTracker.get_default().focus_app;
|
||||
return app ? app.get_windows() : [];
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.CYCLE_GROUP)
|
||||
this._select(this._next());
|
||||
else if (action == Meta.KeyBindingAction.CYCLE_GROUP_BACKWARD)
|
||||
this._select(this._previous());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
});
|
||||
|
||||
const WindowSwitcherPopup = new Lang.Class({
|
||||
Name: 'WindowSwitcherPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
@ -374,7 +530,7 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
|
||||
_getWindowList: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||
return getWindows(workspace);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
@ -401,6 +557,32 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const WindowCyclerPopup = new Lang.Class({
|
||||
Name: 'WindowCyclerPopup',
|
||||
Extends: CyclerPopup,
|
||||
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' });
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_getWindows: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return getWindows(workspace);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.CYCLE_WINDOWS)
|
||||
this._select(this._next());
|
||||
else if (action == Meta.KeyBindingAction.CYCLE_WINDOWS_BACKWARD)
|
||||
this._select(this._previous());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
});
|
||||
|
||||
const AppIcon = new Lang.Class({
|
||||
Name: 'AppIcon',
|
||||
|
||||
@ -448,8 +630,6 @@ const AppSwitcher = new Lang.Class({
|
||||
});
|
||||
if (appIcon.cachedWindows.length > 0)
|
||||
this._addIcon(appIcon);
|
||||
else if (workspace == null)
|
||||
throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name()));
|
||||
}
|
||||
|
||||
this._curApp = -1;
|
||||
@ -687,15 +867,17 @@ const WindowIcon = new Lang.Class({
|
||||
|
||||
this._icon.destroy_all_children();
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
|
||||
switch (mode) {
|
||||
case AppIconMode.THUMBNAIL_ONLY:
|
||||
size = WINDOW_PREVIEW_SIZE;
|
||||
this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
|
||||
this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor));
|
||||
break;
|
||||
|
||||
case AppIconMode.BOTH:
|
||||
size = WINDOW_PREVIEW_SIZE;
|
||||
this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
|
||||
this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor));
|
||||
|
||||
if (this.app)
|
||||
this._icon.add_actor(this._createAppIcon(this.app,
|
||||
@ -707,7 +889,7 @@ const WindowIcon = new Lang.Class({
|
||||
this._icon.add_actor(this._createAppIcon(this.app, size));
|
||||
}
|
||||
|
||||
this._icon.set_size(size, size);
|
||||
this._icon.set_size(size * scaleFactor, size * scaleFactor);
|
||||
},
|
||||
|
||||
_createAppIcon: function(app, size) {
|
||||
|
@ -7,7 +7,7 @@ const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 16;
|
||||
|
||||
const Animation = new Lang.Class({
|
||||
Name: 'Animation',
|
||||
@ -33,7 +33,7 @@ const Animation = new Lang.Class({
|
||||
if (this._frame == 0)
|
||||
this._showFrame(0);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||
this._timeoutId = GLib.timeout_add(GLib.PRIORITY_LOW, this._speed, Lang.bind(this, this._update));
|
||||
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update');
|
||||
}
|
||||
|
||||
|
@ -366,8 +366,6 @@ const AllView = new Lang.Class({
|
||||
Extends: BaseAppView,
|
||||
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
|
||||
this.parent({ usePagination: true }, null);
|
||||
this._scrollView = new St.ScrollView({ style_class: 'all-apps',
|
||||
x_expand: true,
|
||||
@ -502,6 +500,11 @@ const AllView = new Lang.Class({
|
||||
|
||||
_loadApps: function() {
|
||||
let apps = Gio.AppInfo.get_all().filter(function(appInfo) {
|
||||
try {
|
||||
let id = appInfo.get_id(); // catch invalid file encodings
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
return appInfo.should_show();
|
||||
}).map(function(app) {
|
||||
return app.get_id();
|
||||
@ -525,7 +528,7 @@ const AllView = new Lang.Class({
|
||||
// at least on single-monitor setups.
|
||||
// This also disables drag-to-launch on multi-monitor setups,
|
||||
// but we hope that is not used much.
|
||||
let favoritesWritable = this._settings.is_writable('favorite-apps');
|
||||
let favoritesWritable = global.settings.is_writable('favorite-apps');
|
||||
|
||||
apps.forEach(Lang.bind(this, function(appId) {
|
||||
let app = appSys.lookup_app(appId);
|
||||
@ -541,6 +544,13 @@ const AllView = new Lang.Class({
|
||||
|
||||
// Overriden from BaseAppView
|
||||
animate: function (animationDirection, onComplete) {
|
||||
this._scrollView.reactive = false;
|
||||
let completionFunc = Lang.bind(this, function() {
|
||||
this._scrollView.reactive = true;
|
||||
if (onComplete)
|
||||
onComplete();
|
||||
});
|
||||
|
||||
if (animationDirection == IconGrid.AnimationDirection.OUT &&
|
||||
this._displayingPopup && this._currentPopup) {
|
||||
this._currentPopup.popdown();
|
||||
@ -551,10 +561,10 @@ const AllView = new Lang.Class({
|
||||
// signal handler, call again animate which will
|
||||
// call the parent given that popup is already
|
||||
// closed.
|
||||
this.animate(animationDirection, onComplete);
|
||||
this.animate(animationDirection, completionFunc);
|
||||
}));
|
||||
} else {
|
||||
this.parent(animationDirection, onComplete);
|
||||
this.parent(animationDirection, completionFunc);
|
||||
if (animationDirection == IconGrid.AnimationDirection.OUT)
|
||||
this._pageIndicators.animateIndicators(animationDirection);
|
||||
}
|
||||
@ -641,7 +651,7 @@ const AllView = new Lang.Class({
|
||||
},
|
||||
|
||||
_onScroll: function(actor, event) {
|
||||
if (this._displayingPopup)
|
||||
if (this._displayingPopup || !this._scrollView.reactive)
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
let direction = event.get_scroll_direction();
|
||||
@ -748,7 +758,8 @@ const AllView = new Lang.Class({
|
||||
let fadeOffset = Math.min(this._grid.topPadding,
|
||||
this._grid.bottomPadding);
|
||||
this._scrollView.update_fade_effect(fadeOffset, 0);
|
||||
this._scrollView.get_effect('fade').fade_edges = true;
|
||||
if (fadeOffset > 0)
|
||||
this._scrollView.get_effect('fade').fade_edges = true;
|
||||
|
||||
if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages()) {
|
||||
this._adjustment.value = 0;
|
||||
@ -776,8 +787,6 @@ const FrequentView = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent(null, { fillParent: true });
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
|
||||
this.actor = new St.Widget({ style_class: 'frequent-apps',
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true, y_expand: true });
|
||||
@ -820,7 +829,7 @@ const FrequentView = new Lang.Class({
|
||||
// at least on single-monitor setups.
|
||||
// This also disables drag-to-launch on multi-monitor setups,
|
||||
// but we hope that is not used much.
|
||||
let favoritesWritable = this._settings.is_writable('favorite-apps');
|
||||
let favoritesWritable = global.settings.is_writable('favorite-apps');
|
||||
|
||||
for (let i = 0; i < mostUsed.length; i++) {
|
||||
if (!mostUsed[i].get_app_info().should_show())
|
||||
@ -1057,7 +1066,7 @@ const AppSearchProvider = new Lang.Class({
|
||||
|
||||
getInitialResultSet: function(terms, callback, cancellable) {
|
||||
let query = terms.join(' ');
|
||||
let groups = Gio.DesktopAppInfo.search(query);
|
||||
let groups = Shell.AppSystem.search(query);
|
||||
let usage = Shell.AppUsage.get_default();
|
||||
let results = [];
|
||||
groups.forEach(function(group) {
|
||||
@ -1286,7 +1295,10 @@ const FolderIcon = new Lang.Class({
|
||||
if (!_listsIntersect(folderCategories, appCategories))
|
||||
return;
|
||||
|
||||
addAppId(appInfo.get_id());
|
||||
try {
|
||||
addAppId(appInfo.get_id()); // catch invalid file encodings
|
||||
} catch(e) {
|
||||
}
|
||||
});
|
||||
|
||||
this.actor.visible = this.view.getAllItems().length > 0;
|
||||
@ -1702,7 +1714,10 @@ const AppIcon = new Lang.Class({
|
||||
if (!isPoppedUp)
|
||||
this._onMenuPoppedDown();
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
||||
let id = Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
||||
this.actor.connect('destroy', function() {
|
||||
Main.overview.disconnect(id);
|
||||
});
|
||||
|
||||
this._menuManager.addMenu(this._menu);
|
||||
}
|
||||
@ -1794,14 +1809,12 @@ const AppIconMenu = new Lang.Class({
|
||||
|
||||
this.actor.add_style_class_name('app-well-menu');
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
|
||||
// Chain our visibility and lifecycle to that of the source
|
||||
source.actor.connect('notify::mapped', Lang.bind(this, function () {
|
||||
if (!source.actor.mapped)
|
||||
this.close();
|
||||
}));
|
||||
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
|
||||
source.actor.connect('destroy', Lang.bind(this, this.destroy));
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
},
|
||||
@ -1857,7 +1870,7 @@ const AppIconMenu = new Lang.Class({
|
||||
}));
|
||||
}
|
||||
|
||||
let canFavorite = this._settings.is_writable('favorite-apps');
|
||||
let canFavorite = global.settings.is_writable('favorite-apps');
|
||||
|
||||
if (canFavorite) {
|
||||
this._appendSeparator();
|
||||
|
@ -16,16 +16,18 @@ const RENAMED_DESKTOP_IDS = {
|
||||
'glchess.desktop': 'gnome-chess.desktop',
|
||||
'glines.desktop': 'five-or-more.desktop',
|
||||
'gnect.desktop': 'four-in-a-row.desktop',
|
||||
'gnibbles.desktop': 'gnome-nibbles.desktop',
|
||||
'gnibbles.desktop': 'org.gnome.Nibbles.desktop',
|
||||
'gnobots2.desktop': 'gnome-robots.desktop',
|
||||
'gnome-boxes.desktop': 'org.gnome.Boxes.desktop',
|
||||
'gnome-clocks.desktop': 'org.gnome.clocks.desktop',
|
||||
'gnome-contacts.desktop': 'org.gnome.Contacts.desktop',
|
||||
'gnome-documents.desktop': 'org.gnome.Documents.desktop',
|
||||
'gnome-font-viewer.desktop': 'org.gnome.font-viewer.desktop',
|
||||
'gnome-nibbles.desktop': 'org.gnome.Nibbles.desktop',
|
||||
'gnome-photos.desktop': 'org.gnome.Photos.desktop',
|
||||
'gnome-screenshot.desktop': 'org.gnome.Screenshot.desktop',
|
||||
'gnome-software.desktop': 'org.gnome.Software.desktop',
|
||||
'gnome-terminal.desktop': 'org.gnome.Terminal.desktop',
|
||||
'gnome-weather.desktop': 'org.gnome.Weather.Application.desktop',
|
||||
'gnomine.desktop': 'gnome-mines.desktop',
|
||||
'gnotravex.desktop': 'gnome-tetravex.desktop',
|
||||
|
216
js/ui/audioDeviceSelection.js
Normal file
@ -0,0 +1,216 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
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 ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const AudioDevice = {
|
||||
HEADPHONES: 1 << 0,
|
||||
HEADSET: 1 << 1,
|
||||
MICROPHONE: 1 << 2
|
||||
};
|
||||
|
||||
const AudioDeviceSelectionIface = '<node> \
|
||||
<interface name="org.gnome.Shell.AudioDeviceSelection"> \
|
||||
<method name="Open"> \
|
||||
<arg name="devices" direction="in" type="as" /> \
|
||||
</method> \
|
||||
<method name="Close"> \
|
||||
</method> \
|
||||
<signal name="DeviceSelected"> \
|
||||
<arg name="device" type="s" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const AudioDeviceSelectionDialog = new Lang.Class({
|
||||
Name: 'AudioDeviceSelectionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(devices) {
|
||||
this.parent({ styleClass: 'audio-device-selection-dialog' });
|
||||
|
||||
this._deviceItems = {};
|
||||
|
||||
this._buildLayout();
|
||||
|
||||
if (devices & AudioDevice.HEADPHONES)
|
||||
this._addDevice(AudioDevice.HEADPHONES);
|
||||
if (devices & AudioDevice.HEADSET)
|
||||
this._addDevice(AudioDevice.HEADSET);
|
||||
if (devices & AudioDevice.MICROPHONE)
|
||||
this._addDevice(AudioDevice.MICROPHONE);
|
||||
|
||||
if (this._selectionBox.get_n_children() < 2)
|
||||
throw new Error('Too few devices for a selection');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_buildLayout: function(devices) {
|
||||
let title = new St.Label({ style_class: 'audio-selection-title',
|
||||
text: _("Select Audio Device"),
|
||||
x_align: Clutter.ActorAlign.CENTER });
|
||||
|
||||
this.contentLayout.style_class = 'audio-selection-content';
|
||||
this.contentLayout.add(title);
|
||||
|
||||
this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' });
|
||||
this.contentLayout.add(this._selectionBox, { expand: true });
|
||||
|
||||
this.addButton({ action: Lang.bind(this, this._openSettings),
|
||||
label: _("Sound Settings") });
|
||||
this.addButton({ action: Lang.bind(this, this.close),
|
||||
label: _("Cancel"),
|
||||
key: Clutter.Escape });
|
||||
},
|
||||
|
||||
_getDeviceLabel: function(device) {
|
||||
switch(device) {
|
||||
case AudioDevice.HEADPHONES:
|
||||
return _("Headphones");
|
||||
case AudioDevice.HEADSET:
|
||||
return _("Headset");
|
||||
case AudioDevice.MICROPHONE:
|
||||
return _("Microphone");
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
_getDeviceIcon: function(device) {
|
||||
switch(device) {
|
||||
case AudioDevice.HEADPHONES:
|
||||
return 'audio-headphones-symbolic';
|
||||
case AudioDevice.HEADSET:
|
||||
return 'audio-headset-symbolic';
|
||||
case AudioDevice.MICROPHONE:
|
||||
return 'audio-input-microphone-symbolic';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
_addDevice: function(device) {
|
||||
let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
|
||||
vertical: true });
|
||||
box.connect('notify::height',
|
||||
function() {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
function() {
|
||||
box.width = box.height;
|
||||
});
|
||||
});
|
||||
|
||||
let icon = new St.Icon({ style_class: 'audio-selection-device-icon',
|
||||
icon_name: this._getDeviceIcon(device) });
|
||||
box.add(icon);
|
||||
|
||||
let label = new St.Label({ style_class: 'audio-selection-device-label',
|
||||
text: this._getDeviceLabel(device),
|
||||
x_align: Clutter.ActorAlign.CENTER });
|
||||
box.add(label);
|
||||
|
||||
let button = new St.Button({ style_class: 'audio-selection-device',
|
||||
can_focus: true,
|
||||
child: box });
|
||||
this._selectionBox.add(button);
|
||||
|
||||
button.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this.emit('device-selected', device);
|
||||
this.close();
|
||||
Main.overview.hide();
|
||||
}));
|
||||
},
|
||||
|
||||
_openSettings: function() {
|
||||
let desktopFile = 'gnome-sound-panel.desktop'
|
||||
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
|
||||
|
||||
if (!app) {
|
||||
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.close();
|
||||
Main.overview.hide();
|
||||
app.activate();
|
||||
}
|
||||
});
|
||||
|
||||
const AudioDeviceSelectionDBus = new Lang.Class({
|
||||
Name: 'AudioDeviceSelectionDBus',
|
||||
|
||||
_init: function() {
|
||||
this._audioSelectionDialog = null;
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection');
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
_onDialogClosed: function() {
|
||||
this._audioSelectionDialog = null;
|
||||
},
|
||||
|
||||
_onDeviceSelected: function(dialog, device) {
|
||||
let connection = this._dbusImpl.get_connection();
|
||||
let info = this._dbusImpl.get_info();
|
||||
let deviceName = Object.keys(AudioDevice).filter(
|
||||
function(dev) {
|
||||
return AudioDevice[dev] == device;
|
||||
})[0].toLowerCase();
|
||||
connection.emit_signal(this._audioSelectionDialog._sender,
|
||||
this._dbusImpl.get_object_path(),
|
||||
info ? info.name : null,
|
||||
'DeviceSelected',
|
||||
GLib.Variant.new('(s)', [deviceName]));
|
||||
},
|
||||
|
||||
OpenAsync: function(params, invocation) {
|
||||
if (this._audioSelectionDialog) {
|
||||
invocation.return_value(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let [deviceNames] = params;
|
||||
let devices = 0;
|
||||
deviceNames.forEach(function(n) {
|
||||
devices |= AudioDevice[n.toUpperCase()];
|
||||
});
|
||||
|
||||
let dialog;
|
||||
try {
|
||||
dialog = new AudioDeviceSelectionDialog(devices);
|
||||
} catch(e) {
|
||||
invocation.return_value(null);
|
||||
return;
|
||||
}
|
||||
dialog._sender = invocation.get_sender();
|
||||
|
||||
dialog.connect('closed', Lang.bind(this, this._onDialogClosed));
|
||||
dialog.connect('device-selected',
|
||||
Lang.bind(this, this._onDeviceSelected));
|
||||
dialog.open();
|
||||
|
||||
this._audioSelectionDialog = dialog;
|
||||
invocation.return_value(null);
|
||||
},
|
||||
|
||||
CloseAsync: function(params, invocation) {
|
||||
if (this._audioSelectionDialog &&
|
||||
this._audioSelectionDialog._sender == invocation.get_sender())
|
||||
this._audioSelectionDialog.close();
|
||||
|
||||
invocation.return_value(null);
|
||||
}
|
||||
});
|
@ -144,6 +144,7 @@ const BackgroundCache = new Lang.Class({
|
||||
this._pendingFileLoads = [];
|
||||
this._fileMonitors = {};
|
||||
this._backgroundSources = {};
|
||||
this._animations = {};
|
||||
},
|
||||
|
||||
monitorFile: function(file) {
|
||||
@ -162,12 +163,13 @@ const BackgroundCache = new Lang.Class({
|
||||
|
||||
getAnimation: function(params) {
|
||||
params = Params.parse(params, { file: null,
|
||||
settingsSchema: null,
|
||||
onLoaded: null });
|
||||
|
||||
if (_fileEqual0(this._animationFile, params.file)) {
|
||||
if (this._animations[params.settingsSchema] && _fileEqual0(this._animationFile, params.file)) {
|
||||
if (params.onLoaded) {
|
||||
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
params.onLoaded(this._animation);
|
||||
params.onLoaded(this._animations[params.settingsSchema]);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
|
||||
@ -178,12 +180,11 @@ const BackgroundCache = new Lang.Class({
|
||||
let animation = new Animation({ file: params.file });
|
||||
|
||||
animation.load(Lang.bind(this, function() {
|
||||
this._animationFile = params.file;
|
||||
this._animation = animation;
|
||||
this._animations[params.settingsSchema] = animation;
|
||||
|
||||
if (params.onLoaded) {
|
||||
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
params.onLoaded(this._animation);
|
||||
params.onLoaded(this._animations[params.settingsSchema]);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
|
||||
@ -246,6 +247,13 @@ const Background = new Lang.Class({
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this.isLoaded = false;
|
||||
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._timezoneChangedId = this._clock.connect('notify::timezone',
|
||||
Lang.bind(this, function() {
|
||||
if (this._animation)
|
||||
this._loadAnimation(this._animation.file);
|
||||
}));
|
||||
|
||||
this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
@ -264,6 +272,10 @@ const Background = new Lang.Class({
|
||||
}
|
||||
this._fileWatches = null;
|
||||
|
||||
if (this._timezoneChangedId != 0)
|
||||
this._clock.disconnect(this._timezoneChangedId);
|
||||
this._timezoneChangedId = 0;
|
||||
|
||||
if (this._settingsChangedSignalId != 0)
|
||||
this._settings.disconnect(this._settingsChangedSignalId);
|
||||
this._settingsChangedSignalId = 0;
|
||||
@ -403,17 +415,18 @@ const Background = new Lang.Class({
|
||||
|
||||
_loadAnimation: function(file) {
|
||||
this._cache.getAnimation({ file: file,
|
||||
onLoaded: Lang.bind(this, function(animation) {
|
||||
this._animation = animation;
|
||||
settingsSchema: this._settings.schema_id,
|
||||
onLoaded: Lang.bind(this, function(animation) {
|
||||
this._animation = animation;
|
||||
|
||||
if (!this._animation || this._cancellable.is_cancelled()) {
|
||||
this._setLoaded();
|
||||
return;
|
||||
}
|
||||
if (!this._animation || this._cancellable.is_cancelled()) {
|
||||
this._setLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateAnimation();
|
||||
this._watchFile(file);
|
||||
})
|
||||
this._updateAnimation();
|
||||
this._watchFile(file);
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
@ -527,6 +540,10 @@ const BackgroundSource = new Lang.Class({
|
||||
let file = null;
|
||||
let style;
|
||||
|
||||
// We don't watch changes to settings here,
|
||||
// instead we rely on Background to watch those
|
||||
// and emit 'changed' at the right time
|
||||
|
||||
if (this._overrideImage != null) {
|
||||
file = Gio.File.new_for_path(this._overrideImage);
|
||||
style = GDesktopEnums.BackgroundStyle.ZOOM; // Hardcode
|
||||
@ -679,6 +696,7 @@ const BackgroundManager = new Lang.Class({
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
oldBackgroundActor.background.run_dispose();
|
||||
oldBackgroundActor.destroy();
|
||||
}
|
||||
});
|
||||
|
@ -16,9 +16,10 @@ const BackgroundMenu = new Lang.Class({
|
||||
_init: function(layoutManager) {
|
||||
this.parent(layoutManager.dummyCursor, 0, St.Side.TOP);
|
||||
|
||||
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop');
|
||||
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||
|
||||
this.actor.add_style_class_name('background-menu');
|
||||
|
||||
|
@ -3,35 +3,43 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageList = imports.ui.messageList;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Mpris = imports.ui.mpris;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
const ELLIPSIS_CHAR = '\u2026';
|
||||
|
||||
const MESSAGE_ICON_SIZE = 32;
|
||||
|
||||
// alias to prevent xgettext from picking up strings translated in GTK+
|
||||
const gtk30_ = Gettext_gtk30.gettext;
|
||||
const NC_ = function(context, str) { return str; };
|
||||
const NC_ = function(context, str) { return context + '\u0004' + str; };
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
function _sameYear(dateA, dateB) {
|
||||
function sameYear(dateA, dateB) {
|
||||
return (dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
function _sameMonth(dateA, dateB) {
|
||||
return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
|
||||
function sameMonth(dateA, dateB) {
|
||||
return sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
|
||||
}
|
||||
|
||||
function _sameDay(dateA, dateB) {
|
||||
return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
|
||||
function sameDay(dateA, dateB) {
|
||||
return sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
|
||||
}
|
||||
|
||||
function isToday(date) {
|
||||
return sameDay(new Date(), date);
|
||||
}
|
||||
|
||||
function _isWorkDay(date) {
|
||||
@ -58,36 +66,6 @@ function _getEndOfDay(date) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _formatEventTime(event, clockFormat, periodBegin, periodEnd) {
|
||||
let ret;
|
||||
let allDay = (event.allDay || (event.date <= periodBegin && event.end >= periodEnd));
|
||||
if (allDay) {
|
||||
/* Translators: Shown in calendar event list for all day events
|
||||
* Keep it short, best if you can use less then 10 characters
|
||||
*/
|
||||
ret = C_("event list time", "All Day");
|
||||
} else {
|
||||
let date = event.date >= periodBegin ? event.date : event.end;
|
||||
switch (clockFormat) {
|
||||
case '24h':
|
||||
/* Translators: Shown in calendar event list, if 24h format,
|
||||
\u2236 is a ratio character, similar to : */
|
||||
ret = date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* explicit fall-through */
|
||||
case '12h':
|
||||
/* Translators: Shown in calendar event list, if 12h format,
|
||||
\u2236 is a ratio character, similar to : and \u2009 is
|
||||
a thin space */
|
||||
ret = date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _getCalendarDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Calendar grid abbreviation for Sunday.
|
||||
@ -95,46 +73,21 @@ function _getCalendarDayAbbreviation(dayNumber) {
|
||||
* NOTE: These grid abbreviations are always shown together
|
||||
* and in order, e.g. "S M T W T F S".
|
||||
*/
|
||||
C_("grid sunday", "S"),
|
||||
NC_("grid sunday", "S"),
|
||||
/* Translators: Calendar grid abbreviation for Monday */
|
||||
C_("grid monday", "M"),
|
||||
NC_("grid monday", "M"),
|
||||
/* Translators: Calendar grid abbreviation for Tuesday */
|
||||
C_("grid tuesday", "T"),
|
||||
NC_("grid tuesday", "T"),
|
||||
/* Translators: Calendar grid abbreviation for Wednesday */
|
||||
C_("grid wednesday", "W"),
|
||||
NC_("grid wednesday", "W"),
|
||||
/* Translators: Calendar grid abbreviation for Thursday */
|
||||
C_("grid thursday", "T"),
|
||||
NC_("grid thursday", "T"),
|
||||
/* Translators: Calendar grid abbreviation for Friday */
|
||||
C_("grid friday", "F"),
|
||||
NC_("grid friday", "F"),
|
||||
/* Translators: Calendar grid abbreviation for Saturday */
|
||||
C_("grid saturday", "S")
|
||||
NC_("grid saturday", "S")
|
||||
];
|
||||
return abbreviations[dayNumber];
|
||||
}
|
||||
|
||||
function _getEventDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Event list abbreviation for Sunday.
|
||||
*
|
||||
* NOTE: These list abbreviations are normally not shown together
|
||||
* so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
* both be 'T').
|
||||
*/
|
||||
C_("list sunday", "Su"),
|
||||
/* Translators: Event list abbreviation for Monday */
|
||||
C_("list monday", "M"),
|
||||
/* Translators: Event list abbreviation for Tuesday */
|
||||
C_("list tuesday", "T"),
|
||||
/* Translators: Event list abbreviation for Wednesday */
|
||||
C_("list wednesday", "W"),
|
||||
/* Translators: Event list abbreviation for Thursday */
|
||||
C_("list thursday", "Th"),
|
||||
/* Translators: Event list abbreviation for Friday */
|
||||
C_("list friday", "F"),
|
||||
/* Translators: Event list abbreviation for Saturday */
|
||||
C_("list saturday", "S")
|
||||
];
|
||||
return abbreviations[dayNumber];
|
||||
return Shell.util_translate_time_string(abbreviations[dayNumber]);
|
||||
}
|
||||
|
||||
// Abstraction for an appointment/event in a calendar
|
||||
@ -142,7 +95,8 @@ function _getEventDayAbbreviation(dayNumber) {
|
||||
const CalendarEvent = new Lang.Class({
|
||||
Name: 'CalendarEvent',
|
||||
|
||||
_init: function(date, end, summary, allDay) {
|
||||
_init: function(id, date, end, summary, allDay) {
|
||||
this.id = id;
|
||||
this.date = date;
|
||||
this.end = end;
|
||||
this.summary = summary;
|
||||
@ -166,6 +120,9 @@ const EmptyEventSource = new Lang.Class({
|
||||
destroy: function() {
|
||||
},
|
||||
|
||||
ignoreEvent: function(event) {
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
},
|
||||
|
||||
@ -230,6 +187,15 @@ const DBusEventSource = new Lang.Class({
|
||||
this.isLoading = false;
|
||||
this.isDummy = false;
|
||||
|
||||
this._ignoredEvents = new Map();
|
||||
|
||||
let savedState = global.get_persistent_state('as', 'ignored_events');
|
||||
if (savedState)
|
||||
savedState.deep_unpack().forEach(Lang.bind(this,
|
||||
function(eventId) {
|
||||
this._ignoredEvents.set(eventId, true);
|
||||
}));
|
||||
|
||||
this._initialized = false;
|
||||
this._dbusProxy = new CalendarServer();
|
||||
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
|
||||
@ -314,9 +280,10 @@ const DBusEventSource = new Lang.Class({
|
||||
let a = appointments[n];
|
||||
let date = new Date(a[4] * 1000);
|
||||
let end = new Date(a[5] * 1000);
|
||||
let id = a[0];
|
||||
let summary = a[1];
|
||||
let allDay = a[3];
|
||||
let event = new CalendarEvent(date, end, summary, allDay);
|
||||
let event = new CalendarEvent(id, date, end, summary, allDay);
|
||||
newEvents.push(event);
|
||||
}
|
||||
newEvents.sort(function(event1, event2) {
|
||||
@ -343,6 +310,16 @@ const DBusEventSource = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
ignoreEvent: function(event) {
|
||||
if (this._ignoredEvents.get(event.id))
|
||||
return;
|
||||
|
||||
this._ignoredEvents.set(event.id, true);
|
||||
let savedState = new GLib.Variant('as', [...this._ignoredEvents.keys()]);
|
||||
global.set_persistent_state('ignored_events', savedState);
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this.isLoading = true;
|
||||
@ -358,6 +335,10 @@ const DBusEventSource = new Lang.Class({
|
||||
let result = [];
|
||||
for(let n = 0; n < this._events.length; n++) {
|
||||
let event = this._events[n];
|
||||
|
||||
if (this._ignoredEvents.has(event.id))
|
||||
continue;
|
||||
|
||||
if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
|
||||
result.push(event);
|
||||
}
|
||||
@ -390,7 +371,7 @@ const Calendar = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.calendar' });
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' });
|
||||
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
@ -416,7 +397,7 @@ const Calendar = new Lang.Class({
|
||||
this._shouldDateGrabFocus = false;
|
||||
|
||||
this.actor = new St.Widget({ style_class: 'calendar',
|
||||
layout_manager: new Clutter.GridLayout(),
|
||||
layout_manager: new Clutter.TableLayout(),
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('scroll-event',
|
||||
@ -439,7 +420,7 @@ const Calendar = new Lang.Class({
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date) {
|
||||
if (_sameDay(date, this._selectedDate))
|
||||
if (sameDay(date, this._selectedDate))
|
||||
return;
|
||||
|
||||
this._selectedDate = date;
|
||||
@ -454,7 +435,8 @@ const Calendar = new Lang.Class({
|
||||
|
||||
// Top line of the calendar '<| September 2009 |>'
|
||||
this._topBox = new St.BoxLayout();
|
||||
layout.attach(this._topBox, 0, 0, offsetCols + 7, 1);
|
||||
layout.pack(this._topBox, 0, 0);
|
||||
layout.set_span(this._topBox, offsetCols + 7, 1);
|
||||
|
||||
this._backButton = new St.Button({ style_class: 'calendar-change-month-back pager-button',
|
||||
accessible_name: _("Previous month"),
|
||||
@ -485,13 +467,15 @@ const Calendar = new Lang.Class({
|
||||
// and we want, ideally, a single character for e.g. S M T W T F S
|
||||
let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
|
||||
let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
|
||||
text: customDayAbbrev });
|
||||
text: customDayAbbrev,
|
||||
can_focus: true });
|
||||
label.accessible_name = iter.toLocaleFormat('%A');
|
||||
let col;
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
|
||||
else
|
||||
col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
|
||||
layout.attach(label, col, 1, 1, 1);
|
||||
layout.pack(label, col, 1);
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
}
|
||||
|
||||
@ -617,7 +601,8 @@ const Calendar = new Lang.Class({
|
||||
// nRows here means 6 weeks + one header + one navbar
|
||||
let nRows = 8;
|
||||
while (row < 8) {
|
||||
let button = new St.Button({ label: iter.getDate().toString(),
|
||||
// xgettext:no-javascript-format
|
||||
let button = new St.Button({ label: iter.toLocaleFormat(C_("date day number format", "%d")),
|
||||
can_focus: true });
|
||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
@ -648,7 +633,7 @@ const Calendar = new Lang.Class({
|
||||
if (leftMost)
|
||||
styleClass = 'calendar-day-left ' + styleClass;
|
||||
|
||||
if (_sameDay(now, iter))
|
||||
if (sameDay(now, iter))
|
||||
styleClass += ' calendar-today';
|
||||
else if (iter.getMonth() != this._selectedDate.getMonth())
|
||||
styleClass += ' calendar-other-month-day';
|
||||
@ -664,14 +649,17 @@ const Calendar = new Lang.Class({
|
||||
col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
|
||||
else
|
||||
col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
|
||||
layout.attach(button, col, row, 1, 1);
|
||||
layout.pack(button, col, row);
|
||||
|
||||
this._buttons.push(button);
|
||||
|
||||
if (this._useWeekdate && iter.getDay() == 4) {
|
||||
let label = new St.Label({ text: iter.toLocaleFormat('%V'),
|
||||
style_class: 'calendar-day-base calendar-week-number'});
|
||||
layout.attach(label, rtl ? 7 : 0, row, 1, 1);
|
||||
style_class: 'calendar-day-base calendar-week-number',
|
||||
can_focus: true });
|
||||
let weekFormat = Shell.util_translate_time_string(N_("Week %V"));
|
||||
label.accessible_name = iter.toLocaleFormat(weekFormat);
|
||||
layout.pack(label, rtl ? 7 : 0, row);
|
||||
}
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
@ -688,138 +676,170 @@ const Calendar = new Lang.Class({
|
||||
_update: function() {
|
||||
let now = new Date();
|
||||
|
||||
if (_sameYear(this._selectedDate, now))
|
||||
if (sameYear(this._selectedDate, now))
|
||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
|
||||
else
|
||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
|
||||
|
||||
if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin) || !_sameDay(now, this._markedAsToday))
|
||||
if (!this._calendarBegin || !sameMonth(this._selectedDate, this._calendarBegin) || !sameDay(now, this._markedAsToday))
|
||||
this._rebuildCalendar();
|
||||
|
||||
this._buttons.forEach(Lang.bind(this, function(button) {
|
||||
if (_sameDay(button._date, this._selectedDate)) {
|
||||
button.add_style_pseudo_class('active');
|
||||
if (sameDay(button._date, this._selectedDate)) {
|
||||
button.add_style_pseudo_class('selected');
|
||||
if (this._shouldDateGrabFocus)
|
||||
button.grab_key_focus();
|
||||
}
|
||||
else
|
||||
button.remove_style_pseudo_class('active');
|
||||
button.remove_style_pseudo_class('selected');
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
Signals.addSignalMethods(Calendar.prototype);
|
||||
|
||||
const EventsList = new Lang.Class({
|
||||
Name: 'EventsList',
|
||||
const EventMessage = new Lang.Class({
|
||||
Name: 'EventMessage',
|
||||
Extends: MessageList.Message,
|
||||
|
||||
_init: function(event, date) {
|
||||
this._event = event;
|
||||
this._date = date;
|
||||
|
||||
this.parent(this._formatEventTime(), event.summary);
|
||||
},
|
||||
|
||||
_formatEventTime: function() {
|
||||
let periodBegin = _getBeginningOfDay(this._date);
|
||||
let periodEnd = _getEndOfDay(this._date);
|
||||
let allDay = (this._event.allDay || (this._event.date <= periodBegin &&
|
||||
this._event.end >= periodEnd));
|
||||
let title;
|
||||
if (allDay) {
|
||||
/* Translators: Shown in calendar event list for all day events
|
||||
* Keep it short, best if you can use less then 10 characters
|
||||
*/
|
||||
title = C_("event list time", "All Day");
|
||||
} else {
|
||||
let date = this._event.date >= periodBegin ? this._event.date
|
||||
: this._event.end;
|
||||
title = Util.formatTime(date, { timeOnly: true });
|
||||
}
|
||||
|
||||
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||||
if (this._event.date < periodBegin && !this._event.allDay) {
|
||||
if (rtl)
|
||||
title = title + ELLIPSIS_CHAR;
|
||||
else
|
||||
title = ELLIPSIS_CHAR + title;
|
||||
}
|
||||
if (this._event.end > periodEnd && !this._event.allDay) {
|
||||
if (rtl)
|
||||
title = ELLIPSIS_CHAR + title;
|
||||
else
|
||||
title = title + ELLIPSIS_CHAR;
|
||||
}
|
||||
return title;
|
||||
},
|
||||
|
||||
canClose: function() {
|
||||
return isToday(this._date);
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationMessage = new Lang.Class({
|
||||
Name: 'NotificationMessage',
|
||||
Extends: MessageList.Message,
|
||||
|
||||
_init: function(notification) {
|
||||
this.notification = notification;
|
||||
|
||||
this.setUseBodyMarkup(notification.bannerBodyMarkup);
|
||||
this.parent(notification.title, notification.bannerBodyText);
|
||||
|
||||
this.setIcon(this._getIcon());
|
||||
|
||||
this.connect('close', Lang.bind(this,
|
||||
function() {
|
||||
this._closed = true;
|
||||
this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
|
||||
}));
|
||||
this._destroyId = notification.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
if (!this._closed)
|
||||
this.close();
|
||||
}));
|
||||
this._updatedId = notification.connect('updated',
|
||||
Lang.bind(this, this._onUpdated));
|
||||
},
|
||||
|
||||
_getIcon: function() {
|
||||
if (this.notification.gicon)
|
||||
return new St.Icon({ gicon: this.notification.gicon,
|
||||
icon_size: MESSAGE_ICON_SIZE });
|
||||
else
|
||||
return this.notification.source.createIcon(MESSAGE_ICON_SIZE);
|
||||
},
|
||||
|
||||
_onUpdated: function(n, clear) {
|
||||
this.setIcon(this._getIcon());
|
||||
this.setTitle(n.title);
|
||||
this.setBody(n.bannerBodyText);
|
||||
this.setUseBodyMarkup(n.bannerBodyMarkup);
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.notification.activate();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._updatedId)
|
||||
this.notification.disconnect(this._updatedId);
|
||||
this._updatedId = 0;
|
||||
|
||||
if (this._destroyId)
|
||||
this.notification.disconnect(this._destroyId);
|
||||
this._destroyId = 0;
|
||||
}
|
||||
});
|
||||
|
||||
const EventsSection = new Lang.Class({
|
||||
Name: 'EventsSection',
|
||||
Extends: MessageList.MessageListSection,
|
||||
|
||||
_init: function() {
|
||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
this.actor = new St.Widget({ style_class: 'events-table',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(this.actor);
|
||||
this._date = new Date();
|
||||
this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._reloadEvents));
|
||||
this._eventSource = new EmptyEventSource();
|
||||
|
||||
this.parent('');
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
Lang.bind(this, this._appInstalledChanged));
|
||||
this._appInstalledChanged();
|
||||
},
|
||||
|
||||
_ignoreEvent: function(event) {
|
||||
this._eventSource.ignoreEvent(event);
|
||||
},
|
||||
|
||||
setEventSource: function(eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._reloadEvents));
|
||||
},
|
||||
|
||||
_addEvent: function(event, index, includeDayName, periodBegin, periodEnd) {
|
||||
let dayString;
|
||||
if (includeDayName) {
|
||||
if (event.date >= periodBegin)
|
||||
dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||
else /* show event end day if it began earlier */
|
||||
dayString = _getEventDayAbbreviation(event.end.getDay());
|
||||
} else {
|
||||
dayString = '';
|
||||
}
|
||||
|
||||
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
|
||||
text: dayString,
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
dayLabel.clutter_text.line_wrap = false;
|
||||
dayLabel.clutter_text.ellipsize = false;
|
||||
|
||||
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let layout = this.actor.layout_manager;
|
||||
layout.attach(dayLabel, rtl ? 2 : 0, index, 1, 1);
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let timeString = _formatEventTime(event, clockFormat, periodBegin, periodEnd);
|
||||
let timeLabel = new St.Label({ style_class: 'events-day-time',
|
||||
text: timeString,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
timeLabel.clutter_text.line_wrap = false;
|
||||
timeLabel.clutter_text.ellipsize = false;
|
||||
|
||||
let preEllipsisLabel = new St.Label({ style_class: 'events-day-time-ellipses',
|
||||
text: ELLIPSIS_CHAR,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
let postEllipsisLabel = new St.Label({ style_class: 'events-day-time-ellipses',
|
||||
text: ELLIPSIS_CHAR,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
if (event.allDay || event.date >= periodBegin)
|
||||
preEllipsisLabel.opacity = 0;
|
||||
if (event.allDay || event.end <= periodEnd)
|
||||
postEllipsisLabel.opacity = 0;
|
||||
|
||||
let timeLabelBoxLayout = new St.BoxLayout();
|
||||
timeLabelBoxLayout.add(preEllipsisLabel);
|
||||
timeLabelBoxLayout.add(timeLabel);
|
||||
timeLabelBoxLayout.add(postEllipsisLabel);
|
||||
layout.attach(timeLabelBoxLayout, 1, index, 1, 1);
|
||||
|
||||
let titleLabel = new St.Label({ style_class: 'events-day-task',
|
||||
text: event.summary,
|
||||
x_expand: true });
|
||||
titleLabel.clutter_text.line_wrap = true;
|
||||
titleLabel.clutter_text.ellipsize = false;
|
||||
|
||||
layout.attach(titleLabel, rtl ? 0 : 2, index, 1, 1);
|
||||
get allowed() {
|
||||
return Main.sessionMode.showCalendarEvents;
|
||||
},
|
||||
|
||||
_addPeriod: function(header, index, periodBegin, periodEnd, includeDayName, showNothingScheduled) {
|
||||
let events = this._eventSource.getEvents(periodBegin, periodEnd);
|
||||
|
||||
if (events.length == 0 && !showNothingScheduled)
|
||||
return index;
|
||||
|
||||
let label = new St.Label({ style_class: 'events-day-header', text: header });
|
||||
let layout = this.actor.layout_manager;
|
||||
layout.attach(label, 0, index, 3, 1);
|
||||
index++;
|
||||
|
||||
for (let n = 0; n < events.length; n++) {
|
||||
this._addEvent(events[n], index, includeDayName, periodBegin, periodEnd);
|
||||
index++;
|
||||
_updateTitle: function() {
|
||||
if (isToday(this._date)) {
|
||||
this._title.label = _("Events");
|
||||
return;
|
||||
}
|
||||
|
||||
if (events.length == 0 && showNothingScheduled) {
|
||||
/* Translators: Text to show if there are no events */
|
||||
let nothingEvent = new CalendarEvent(periodBegin, periodBegin, _("Nothing Scheduled"), true);
|
||||
this._addEvent(nothingEvent, index, false, periodBegin, periodEnd);
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
},
|
||||
|
||||
_showOtherDay: function(day) {
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let dayFormat;
|
||||
let now = new Date();
|
||||
if (_sameYear(day, now))
|
||||
if (sameYear(this._date, now))
|
||||
/* Translators: Shown on calendar heading when selected day occurs on current year */
|
||||
dayFormat = Shell.util_translate_time_string(NC_("calendar heading",
|
||||
"%A, %B %d"));
|
||||
@ -827,61 +847,344 @@ const EventsList = new Lang.Class({
|
||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||
dayFormat = Shell.util_translate_time_string(NC_("calendar heading",
|
||||
"%A, %B %d, %Y"));
|
||||
let dayString = day.toLocaleFormat(dayFormat);
|
||||
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
|
||||
this._title.label = this._date.toLocaleFormat(dayFormat);
|
||||
},
|
||||
|
||||
_showToday: function() {
|
||||
this.actor.destroy_all_children();
|
||||
let index = 0;
|
||||
|
||||
let now = new Date();
|
||||
let dayBegin = _getBeginningOfDay(now);
|
||||
let dayEnd = _getEndOfDay(now);
|
||||
index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
|
||||
|
||||
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||
index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
|
||||
|
||||
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
||||
|
||||
if (dayInWeek < 5) {
|
||||
/* If now is within the first 5 days we show "This week" and
|
||||
* include events up until and including Saturday/Sunday
|
||||
* (depending on whether a week starts on Sunday/Monday).
|
||||
*/
|
||||
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
||||
index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
|
||||
} else {
|
||||
/* otherwise it's one of the two last days of the week ... show
|
||||
* "Next week" and include events up until and including *next*
|
||||
* Saturday/Sunday
|
||||
*/
|
||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
||||
index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Sets the event list to show events from a specific date
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this._date)) {
|
||||
this._date = date;
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
_reloadEvents: function() {
|
||||
if (this._eventSource.isLoading)
|
||||
return;
|
||||
|
||||
let today = new Date();
|
||||
if (_sameDay (this._date, today)) {
|
||||
this._showToday();
|
||||
this._reloading = true;
|
||||
|
||||
this._list.destroy_all_children();
|
||||
|
||||
let periodBegin = _getBeginningOfDay(this._date);
|
||||
let periodEnd = _getEndOfDay(this._date);
|
||||
let events = this._eventSource.getEvents(periodBegin, periodEnd);
|
||||
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
let event = events[i];
|
||||
|
||||
let message = new EventMessage(event, this._date);
|
||||
message.connect('close', Lang.bind(this, function() {
|
||||
this._ignoreEvent(event);
|
||||
}));
|
||||
this.addMessage(message, false);
|
||||
}
|
||||
|
||||
this._reloading = false;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_appInstalledChanged: function() {
|
||||
this._calendarApp = undefined;
|
||||
this._title.reactive = (this._getCalendarApp() != null);
|
||||
},
|
||||
|
||||
_getCalendarApp: function() {
|
||||
if (this._calendarApp !== undefined)
|
||||
return this._calendarApp;
|
||||
|
||||
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
|
||||
if (apps && (apps.length > 0)) {
|
||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||
let defaultInRecommended = apps.some(function(a) { return a.equal(app); });
|
||||
this._calendarApp = defaultInRecommended ? app : apps[0];
|
||||
} else {
|
||||
this._showOtherDay(this._date);
|
||||
this._calendarApp = null;
|
||||
}
|
||||
return this._calendarApp;
|
||||
},
|
||||
|
||||
_onTitleClicked: function() {
|
||||
this.parent();
|
||||
|
||||
let app = this._getCalendarApp();
|
||||
if (app.get_id() == 'evolution.desktop')
|
||||
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||
app.launch([], global.create_app_launch_context(0, -1));
|
||||
},
|
||||
|
||||
setDate: function(date) {
|
||||
this.parent(date);
|
||||
this._updateTitle();
|
||||
this._reloadEvents();
|
||||
},
|
||||
|
||||
_shouldShow: function() {
|
||||
return !this.empty || !isToday(this._date);
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
if (this._reloading)
|
||||
return;
|
||||
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationSection = new Lang.Class({
|
||||
Name: 'NotificationSection',
|
||||
Extends: MessageList.MessageListSection,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Notifications"));
|
||||
|
||||
this._sources = new Map();
|
||||
this._nUrgent = 0;
|
||||
|
||||
Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
|
||||
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
|
||||
this._sourceAdded(Main.messageTray, source);
|
||||
}));
|
||||
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||
},
|
||||
|
||||
get allowed() {
|
||||
return Main.sessionMode.hasNotifications &&
|
||||
!Main.sessionMode.isGreeter;
|
||||
},
|
||||
|
||||
_createTimeLabel: function() {
|
||||
let label = Util.createTimeLabel(new Date());
|
||||
label.style_class = 'event-time',
|
||||
label.x_align = Clutter.ActorAlign.END;
|
||||
return label;
|
||||
},
|
||||
|
||||
_sourceAdded: function(tray, source) {
|
||||
let obj = {
|
||||
destroyId: 0,
|
||||
notificationAddedId: 0,
|
||||
};
|
||||
|
||||
obj.destroyId = source.connect('destroy', Lang.bind(this, function(source) {
|
||||
this._onSourceDestroy(source, obj);
|
||||
}));
|
||||
obj.notificationAddedId = source.connect('notification-added',
|
||||
Lang.bind(this, this._onNotificationAdded));
|
||||
|
||||
this._sources.set(source, obj);
|
||||
},
|
||||
|
||||
_onNotificationAdded: function(source, notification) {
|
||||
let message = new NotificationMessage(notification);
|
||||
message.setSecondaryActor(this._createTimeLabel());
|
||||
|
||||
let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
|
||||
|
||||
let updatedId = notification.connect('updated', Lang.bind(this,
|
||||
function() {
|
||||
message.setSecondaryActor(this._createTimeLabel());
|
||||
this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.actor.mapped);
|
||||
}));
|
||||
let destroyId = notification.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
notification.disconnect(destroyId);
|
||||
notification.disconnect(updatedId);
|
||||
if (isUrgent)
|
||||
this._nUrgent--;
|
||||
}));
|
||||
|
||||
if (isUrgent) {
|
||||
// Keep track of urgent notifications to keep them on top
|
||||
this._nUrgent++;
|
||||
} else if (this.mapped) {
|
||||
// Only acknowledge non-urgent notifications in case it
|
||||
// has important actions that are inaccessible when not
|
||||
// shown as banner
|
||||
notification.acknowledged = true;
|
||||
}
|
||||
|
||||
let index = isUrgent ? 0 : this._nUrgent;
|
||||
this.addMessageAtIndex(message, index, this.actor.mapped);
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source, obj) {
|
||||
source.disconnect(obj.destroyId);
|
||||
source.disconnect(obj.notificationAddedId);
|
||||
|
||||
this._sources.delete(source);
|
||||
},
|
||||
|
||||
_onMapped: function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
for (let message of this._messages.keys())
|
||||
if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
|
||||
message.notification.acknowledged = true;
|
||||
},
|
||||
|
||||
_onTitleClicked: function() {
|
||||
this.parent();
|
||||
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-notifications-panel.desktop');
|
||||
|
||||
if (!app) {
|
||||
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||
return;
|
||||
}
|
||||
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_shouldShow: function() {
|
||||
return !this.empty && isToday(this._date);
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this.parent();
|
||||
this._title.reactive = Main.sessionMode.allowSettings;
|
||||
}
|
||||
});
|
||||
|
||||
const Placeholder = new Lang.Class({
|
||||
Name: 'Placeholder',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'message-list-placeholder',
|
||||
vertical: true });
|
||||
|
||||
this._date = new Date();
|
||||
|
||||
let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
|
||||
let otherFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
|
||||
this._todayIcon = new Gio.FileIcon({ file: todayFile });
|
||||
this._otherIcon = new Gio.FileIcon({ file: otherFile });
|
||||
|
||||
this._icon = new St.Icon();
|
||||
this.actor.add_actor(this._icon);
|
||||
|
||||
this._label = new St.Label();
|
||||
this.actor.add_actor(this._label);
|
||||
|
||||
this._sync();
|
||||
},
|
||||
|
||||
setDate: function(date) {
|
||||
if (sameDay(this._date, date))
|
||||
return;
|
||||
this._date = date;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let today = isToday(this._date);
|
||||
if (today && this._icon.gicon == this._todayIcon)
|
||||
return;
|
||||
if (!today && this._icon.gicon == this._otherIcon)
|
||||
return;
|
||||
|
||||
if (today) {
|
||||
this._icon.gicon = this._todayIcon;
|
||||
this._label.text = _("No Notifications");
|
||||
} else {
|
||||
this._icon.gicon = this._otherIcon;
|
||||
this._label.text = _("No Events");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const CalendarMessageList = new Lang.Class({
|
||||
Name: 'CalendarMessageList',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Widget({ style_class: 'message-list',
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true, y_expand: true });
|
||||
|
||||
this._placeholder = new Placeholder();
|
||||
this.actor.add_actor(this._placeholder.actor);
|
||||
|
||||
this._scrollView = new St.ScrollView({ style_class: 'vfade',
|
||||
overlay_scrollbars: true,
|
||||
x_expand: true, y_expand: true,
|
||||
x_fill: true, y_fill: true });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor.add_actor(this._scrollView);
|
||||
|
||||
this._sectionList = new St.BoxLayout({ style_class: 'message-list-sections',
|
||||
vertical: true,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
this._scrollView.add_actor(this._sectionList);
|
||||
this._sections = new Map();
|
||||
|
||||
this._mediaSection = new Mpris.MediaSection();
|
||||
this._addSection(this._mediaSection);
|
||||
|
||||
this._notificationSection = new NotificationSection();
|
||||
this._addSection(this._notificationSection);
|
||||
|
||||
this._eventsSection = new EventsSection();
|
||||
this._addSection(this._eventsSection);
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
|
||||
},
|
||||
|
||||
_addSection: function(section) {
|
||||
let obj = {
|
||||
destroyId: 0,
|
||||
visibleId: 0,
|
||||
emptyChangedId: 0,
|
||||
keyFocusId: 0
|
||||
};
|
||||
obj.destroyId = section.actor.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
this._removeSection(section);
|
||||
}));
|
||||
obj.visibleId = section.actor.connect('notify::visible',
|
||||
Lang.bind(this, this._sync));
|
||||
obj.emptyChangedId = section.connect('empty-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
obj.keyFocusId = section.connect('key-focus-in',
|
||||
Lang.bind(this, this._onKeyFocusIn));
|
||||
|
||||
this._sections.set(section, obj);
|
||||
this._sectionList.add_actor(section.actor);
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_removeSection: function(section) {
|
||||
let obj = this._sections.get(section);
|
||||
section.actor.disconnect(obj.destroyId);
|
||||
section.actor.disconnect(obj.visibleId);
|
||||
section.disconnect(obj.emptyChangedId);
|
||||
section.disconnect(obj.keyFocusId);
|
||||
|
||||
this._sections.delete(section);
|
||||
this._sectionList.remove_actor(section.actor);
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_onKeyFocusIn: function(section, actor) {
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let sections = [...this._sections.keys()];
|
||||
let visible = sections.some(function(s) {
|
||||
return s.allowed;
|
||||
});
|
||||
this.actor.visible = visible;
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
let showPlaceholder = sections.every(function(s) {
|
||||
return s.empty || !s.actor.visible;
|
||||
});
|
||||
this._placeholder.actor.visible = showPlaceholder;
|
||||
},
|
||||
|
||||
setEventSource: function(eventSource) {
|
||||
this._eventsSection.setEventSource(eventSource);
|
||||
},
|
||||
|
||||
setDate: function(date) {
|
||||
for (let section of this._sections.keys())
|
||||
section.setDate(date);
|
||||
this._placeholder.setDate(date);
|
||||
}
|
||||
});
|
||||
|
@ -313,6 +313,10 @@ const AutorunSource = new Lang.Class({
|
||||
|
||||
getIcon: function() {
|
||||
return this.mount.get_icon();
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus');
|
||||
}
|
||||
});
|
||||
|
||||
@ -325,16 +329,19 @@ const AutorunNotification = new Lang.Class({
|
||||
|
||||
this._manager = manager;
|
||||
this._mount = source.mount;
|
||||
},
|
||||
|
||||
source.apps.forEach(Lang.bind(this, function (app) {
|
||||
createBanner: function() {
|
||||
let banner = new MessageTray.NotificationBanner(this);
|
||||
|
||||
this.source.apps.forEach(Lang.bind(this, function (app) {
|
||||
let actor = this._buttonForApp(app);
|
||||
|
||||
if (actor)
|
||||
this.addButton(actor);
|
||||
banner.addButton(actor);
|
||||
}));
|
||||
|
||||
// set the notification to urgent, so that it expands out
|
||||
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||
return banner;
|
||||
},
|
||||
|
||||
_buttonForApp: function(app) {
|
||||
@ -352,6 +359,7 @@ const AutorunNotification = new Lang.Class({
|
||||
let button = new St.Button({ child: box,
|
||||
x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
x_expand: true,
|
||||
button_mask: St.ButtonMask.ONE,
|
||||
style_class: 'hotplug-notification-item button' });
|
||||
|
||||
@ -363,7 +371,7 @@ const AutorunNotification = new Lang.Class({
|
||||
return button;
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
activate: function() {
|
||||
this.parent();
|
||||
|
||||
let app = Gio.app_info_get_default_for_type('inode/directory', false);
|
||||
|
@ -9,9 +9,15 @@ const Gio = imports.gi.Gio;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gcr = imports.gi.Gcr;
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const WORK_SPINNER_ICON_SIZE = 16;
|
||||
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
|
||||
const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
||||
|
||||
const KeyringDialog = new Lang.Class({
|
||||
Name: 'KeyringDialog',
|
||||
@ -58,27 +64,47 @@ const KeyringDialog = new Lang.Class({
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._workSpinner = null;
|
||||
this._controlTable = null;
|
||||
|
||||
|
||||
this._cancelButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
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 });
|
||||
key: Clutter.Escape });
|
||||
this._continueButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true },
|
||||
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||
default: true });
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
_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();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_buildControlTable: function() {
|
||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
|
||||
@ -101,15 +127,22 @@ const KeyringDialog = new Lang.Class({
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
|
||||
|
||||
let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
|
||||
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||
this._workSpinner.actor.opacity = 0;
|
||||
|
||||
if (rtl) {
|
||||
layout.attach(this._passwordEntry, 0, row, 1, 1);
|
||||
layout.attach(label, 1, row, 1, 1);
|
||||
layout.attach(this._workSpinner.actor, 0, row, 1, 1);
|
||||
layout.attach(this._passwordEntry, 1, row, 1, 1);
|
||||
layout.attach(label, 2, row, 1, 1);
|
||||
} else {
|
||||
layout.attach(label, 0, row, 1, 1);
|
||||
layout.attach(this._passwordEntry, 1, row, 1, 1);
|
||||
layout.attach(this._workSpinner.actor, 2, row, 1, 1);
|
||||
}
|
||||
row++;
|
||||
} else {
|
||||
this._workSpinner = null;
|
||||
this._passwordEntry = null;
|
||||
}
|
||||
|
||||
@ -178,7 +211,7 @@ const KeyringDialog = new Lang.Class({
|
||||
|
||||
this._continueButton.can_focus = sensitive;
|
||||
this._continueButton.reactive = sensitive;
|
||||
this.setWorking(!sensitive);
|
||||
this._setWorking(!sensitive);
|
||||
},
|
||||
|
||||
_ensureOpen: function() {
|
||||
|
@ -12,6 +12,8 @@ const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
@ -338,6 +340,7 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
content.message = _("PIN code is needed for the mobile broadband device");
|
||||
content.secrets.push({ label: _("PIN: "), key: 'pin',
|
||||
value: gsmSetting.pin || '', password: true });
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case 'cdma':
|
||||
@ -610,6 +613,15 @@ const NetworkAgent = new Lang.Class({
|
||||
|
||||
this._dialogs = { };
|
||||
this._vpnRequests = { };
|
||||
this._notifications = { };
|
||||
|
||||
this._pluginDir = Gio.file_new_for_path(GLib.build_filenamev([Config.SYSCONFDIR, 'NetworkManager/VPN']));
|
||||
try {
|
||||
let monitor = this._pluginDir.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||
monitor.connect('changed', () => { this._vpnCacheBuilt = false; });
|
||||
} catch(e) {
|
||||
log('Failed to create monitor for VPN plugin dir: ' + e.message);
|
||||
}
|
||||
|
||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
@ -632,21 +644,92 @@ const NetworkAgent = new Lang.Class({
|
||||
this._vpnRequests[requestId].cancel(true);
|
||||
this._vpnRequests = { };
|
||||
|
||||
for (requestId in this._notifications)
|
||||
this._notifications[requestId].destroy();
|
||||
this._notifications = { };
|
||||
|
||||
this._enabled = false;
|
||||
},
|
||||
|
||||
_showNotification: function(requestId, connection, settingName, hints, flags) {
|
||||
let source = new MessageTray.Source(_("Network Manager"), 'network-transmit-receive');
|
||||
source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
|
||||
|
||||
let title, body;
|
||||
|
||||
let connectionSetting = connection.get_setting_connection();
|
||||
let connectionType = connectionSetting.get_connection_type();
|
||||
switch (connectionType) {
|
||||
case '802-11-wireless':
|
||||
let wirelessSetting = connection.get_setting_wireless();
|
||||
let ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
|
||||
title = _("Authentication required by wireless network");
|
||||
body = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid);
|
||||
break;
|
||||
case '802-3-ethernet':
|
||||
title = _("Wired 802.1X authentication");
|
||||
body = _("A password is required to connect to “%s”.".format(connection.get_id()));
|
||||
break;
|
||||
case 'pppoe':
|
||||
title = _("DSL authentication");
|
||||
body = _("A password is required to connect to “%s”.".format(connection.get_id()));
|
||||
break;
|
||||
case 'gsm':
|
||||
if (hints.indexOf('pin') != -1) {
|
||||
let gsmSetting = connection.get_setting_gsm();
|
||||
title = _("PIN code required");
|
||||
message = _("PIN code is needed for the mobile broadband device");
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case 'cdma':
|
||||
case 'bluetooth':
|
||||
title = _("Mobile broadband network password");
|
||||
message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id());
|
||||
break;
|
||||
default:
|
||||
log('Invalid connection type: ' + connectionType);
|
||||
this._native.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
let notification = new MessageTray.Notification(source, title, body);
|
||||
|
||||
notification.connect('activated', Lang.bind(this, function() {
|
||||
notification.answered = true;
|
||||
this._handleRequest(requestId, connection, settingName, hints, flags);
|
||||
}));
|
||||
|
||||
this._notifications[requestId] = notification;
|
||||
notification.connect('destroy', Lang.bind(this, function() {
|
||||
if (!notification.answered)
|
||||
this._native.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
delete this._notifications[requestId];
|
||||
}));
|
||||
|
||||
Main.messageTray.add(source);
|
||||
source.notify(notification);
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||
if (!this._enabled) {
|
||||
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(flags & NMClient.SecretAgentGetSecretsFlags.USER_REQUESTED))
|
||||
this._showNotification(requestId, connection, settingName, hints, flags);
|
||||
else
|
||||
this._handleRequest(requestId, connection, settingName, hints, flags);
|
||||
},
|
||||
|
||||
_handleRequest: function(requestId, connection, settingName, hints, flags) {
|
||||
if (settingName == 'vpn') {
|
||||
this._vpnRequest(requestId, connection, hints, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
|
||||
let dialog = new NetworkSecretDialog(this._native, requestId, connection, settingName, hints);
|
||||
dialog.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._dialogs[requestId];
|
||||
}));
|
||||
@ -690,9 +773,8 @@ const NetworkAgent = new Lang.Class({
|
||||
this._vpnCacheBuilt = true;
|
||||
this._vpnBinaries = { };
|
||||
|
||||
let dir = Gio.file_new_for_path(GLib.build_filenamev([Config.SYSCONFDIR, 'NetworkManager/VPN']));
|
||||
try {
|
||||
let fileEnum = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let fileEnum = this._pluginDir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let info;
|
||||
|
||||
while ((info = fileEnum.next_file(null))) {
|
||||
@ -702,7 +784,7 @@ const NetworkAgent = new Lang.Class({
|
||||
|
||||
try {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
keyfile.load_from_file(dir.get_child(name).get_path(), GLib.KeyFileFlags.NONE);
|
||||
keyfile.load_from_file(this._pluginDir.get_child(name).get_path(), GLib.KeyFileFlags.NONE);
|
||||
let service = keyfile.get_string('VPN Connection', 'service');
|
||||
let binary = keyfile.get_string('GNOME', 'auth-dialog');
|
||||
let externalUIMode = false;
|
||||
@ -721,13 +803,21 @@ const NetworkAgent = new Lang.Class({
|
||||
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
|
||||
}
|
||||
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) {
|
||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints };
|
||||
else
|
||||
try {
|
||||
let aliases = keyfile.get_string_list('VPN Connection', 'aliases');
|
||||
|
||||
for (let alias of aliases) {
|
||||
this._vpnBinaries[alias] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints };
|
||||
}
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
} else {
|
||||
throw new Error('VPN plugin at %s is not executable'.format(path));
|
||||
}
|
||||
} catch(e) {
|
||||
log('Error \'%s\' while processing VPN keyfile \'%s\''.
|
||||
format(e.message, dir.get_child(name).get_path()));
|
||||
format(e.message, this._pluginDir.get_child(name).get_path()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -13,13 +13,19 @@ const Mainloop = imports.mainloop;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
const PolkitAgent = imports.gi.PolkitAgent;
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const Components = imports.ui.components;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const DIALOG_ICON_SIZE = 48;
|
||||
|
||||
const WORK_SPINNER_ICON_SIZE = 16;
|
||||
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
|
||||
const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
||||
|
||||
const AuthenticationDialog = new Lang.Class({
|
||||
Name: 'AuthenticationDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -136,6 +142,13 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||
this._passwordBox.add(this._passwordEntry,
|
||||
{ expand: true });
|
||||
|
||||
let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
|
||||
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||
this._workSpinner.actor.opacity = 0;
|
||||
|
||||
this._passwordBox.add(this._workSpinner.actor);
|
||||
|
||||
this.setInitialKeyFocus(this._passwordEntry);
|
||||
this._passwordBox.hide();
|
||||
|
||||
@ -165,17 +178,10 @@ const AuthenticationDialog = new Lang.Class({
|
||||
|
||||
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
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 });
|
||||
key: Clutter.Escape });
|
||||
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true },
|
||||
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||
default: true });
|
||||
|
||||
this._doneEmitted = false;
|
||||
|
||||
@ -183,6 +189,30 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._cookie = cookie;
|
||||
},
|
||||
|
||||
_setWorking: function(working) {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
performAuthentication: function() {
|
||||
this.destroySession();
|
||||
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||
@ -229,7 +259,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
|
||||
this._okButton.can_focus = sensitive;
|
||||
this._okButton.reactive = sensitive;
|
||||
this.setWorking(!sensitive);
|
||||
this._setWorking(!sensitive);
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
|
@ -27,8 +27,8 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
_init: function() {
|
||||
this._items = [];
|
||||
this.addGroup(global.window_group, _("Windows"),
|
||||
'emblem-documents-symbolic', { sortGroup: SortGroup.TOP,
|
||||
focusCallback: Lang.bind(this, this._focusWindows) });
|
||||
'focus-windows-symbolic', { sortGroup: SortGroup.TOP,
|
||||
focusCallback: Lang.bind(this, this._focusWindows) });
|
||||
},
|
||||
|
||||
addGroup: function(root, name, icon, params) {
|
||||
|
@ -259,7 +259,7 @@ const ShowAppsIcon = new Lang.Class({
|
||||
},
|
||||
|
||||
_createIcon: function(size) {
|
||||
this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
|
||||
this._iconActor = new St.Icon({ icon_name: 'view-app-grid-symbolic',
|
||||
icon_size: size,
|
||||
style_class: 'show-apps-icon',
|
||||
track_hover: true });
|
||||
@ -270,7 +270,7 @@ const ShowAppsIcon = new Lang.Class({
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
if (!this._settings.is_writable('favorite-apps'))
|
||||
if (!global.settings.is_writable('favorite-apps'))
|
||||
return false;
|
||||
|
||||
let id = app.get_id();
|
||||
@ -428,8 +428,6 @@ const Dash = new Lang.Class({
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
||||
@ -519,10 +517,13 @@ const Dash = new Lang.Class({
|
||||
this._syncLabel(item, appIcon);
|
||||
}));
|
||||
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
let id = Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
this._labelShowing = false;
|
||||
item.hideLabel();
|
||||
}));
|
||||
item.child.connect('destroy', function() {
|
||||
Main.overview.disconnect(id);
|
||||
});
|
||||
|
||||
if (appIcon) {
|
||||
appIcon.connect('sync-tooltip', Lang.bind(this, function() {
|
||||
@ -643,15 +644,14 @@ const Dash = new Lang.Class({
|
||||
let firstIcon = firstButton._delegate.icon;
|
||||
|
||||
let minHeight, natHeight;
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
|
||||
// Enforce the current icon size during the size request
|
||||
firstIcon.setIconSize(this.iconSize);
|
||||
firstIcon.icon.ensure_style();
|
||||
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
||||
firstIcon.icon.set_size(this.iconSize * scaleFactor, this.iconSize * scaleFactor);
|
||||
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
let iconSizes = baseIconSizes.map(function(s) {
|
||||
return s * scaleFactor;
|
||||
});
|
||||
firstIcon.icon.set_size(currentWidth, currentHeight);
|
||||
|
||||
// Subtract icon padding and box spacing from the available height
|
||||
availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
|
||||
@ -659,6 +659,10 @@ const Dash = new Lang.Class({
|
||||
|
||||
let availSize = availHeight / iconChildren.length;
|
||||
|
||||
let iconSizes = baseIconSizes.map(function(s) {
|
||||
return s * scaleFactor;
|
||||
});
|
||||
|
||||
let newIconSize = baseIconSizes[0];
|
||||
for (let i = 0; i < iconSizes.length; i++) {
|
||||
if (iconSizes[i] < availSize)
|
||||
@ -859,7 +863,7 @@ const Dash = new Lang.Class({
|
||||
if (app == null || app.is_window_backed())
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
if (!this._settings.is_writable('favorite-apps'))
|
||||
if (!global.settings.is_writable('favorite-apps'))
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
||||
@ -938,7 +942,7 @@ const Dash = new Lang.Class({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this._settings.is_writable('favorite-apps'))
|
||||
if (!global.settings.is_writable('favorite-apps'))
|
||||
return false;
|
||||
|
||||
let id = app.get_id();
|
||||
|
@ -4,6 +4,8 @@ const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GWeather = imports.gi.GWeather;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Cairo = imports.cairo;
|
||||
@ -19,22 +21,293 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
|
||||
function _onVertSepRepaint(area) {
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let stippleColor = themeNode.get_color('-stipple-color');
|
||||
let stippleWidth = themeNode.get_length('-stipple-width');
|
||||
let x = Math.floor(width/2) + 0.5;
|
||||
cr.moveTo(x, 0);
|
||||
cr.lineTo(x, height);
|
||||
Clutter.cairo_set_source_color(cr, stippleColor);
|
||||
cr.setDash([1, 3], 1); // Hard-code for now
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
cr.$dispose();
|
||||
function _isToday(date) {
|
||||
let now = new Date();
|
||||
return now.getYear() == date.getYear() &&
|
||||
now.getMonth() == date.getMonth() &&
|
||||
now.getDate() == date.getDate();
|
||||
}
|
||||
|
||||
const TodayButton = new Lang.Class({
|
||||
Name: 'TodayButton',
|
||||
|
||||
_init: function(calendar) {
|
||||
// Having the ability to go to the current date if the user is already
|
||||
// on the current date can be confusing. So don't make the button reactive
|
||||
// until the selected date changes.
|
||||
this.actor = new St.Button({ style_class: 'datemenu-today-button',
|
||||
x_align: St.Align.START,
|
||||
can_focus: true,
|
||||
reactive: false
|
||||
});
|
||||
this.actor.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._calendar.setDate(new Date(), false);
|
||||
}));
|
||||
|
||||
let hbox = new St.BoxLayout({ vertical: true });
|
||||
this.actor.add_actor(hbox);
|
||||
|
||||
this._dayLabel = new St.Label({ style_class: 'day-label',
|
||||
x_align: Clutter.ActorAlign.START });
|
||||
hbox.add_actor(this._dayLabel);
|
||||
|
||||
this._dateLabel = new St.Label({ style_class: 'date-label' });
|
||||
hbox.add_actor(this._dateLabel);
|
||||
|
||||
this._calendar = calendar;
|
||||
this._calendar.connect('selected-date-changed', Lang.bind(this,
|
||||
function(calendar, date) {
|
||||
// Make the button reactive only if the selected date is not the
|
||||
// current date.
|
||||
this.actor.reactive = !_isToday(date)
|
||||
}));
|
||||
},
|
||||
|
||||
setDate: function(date) {
|
||||
this._dayLabel.set_text(date.toLocaleFormat('%A'));
|
||||
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
let dateFormat = Shell.util_translate_time_string (N_("%B %e %Y"));
|
||||
this._dateLabel.set_text(date.toLocaleFormat(dateFormat));
|
||||
|
||||
/* Translators: This is the accessible name of the date button shown
|
||||
* below the time in the shell; it should combine the weekday and the
|
||||
* date, e.g. "Tuesday February 17 2015".
|
||||
*/
|
||||
let dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
|
||||
this.actor.accessible_name = date.toLocaleFormat(dateFormat);
|
||||
}
|
||||
});
|
||||
|
||||
const WorldClocksSection = new Lang.Class({
|
||||
Name: 'WorldClocksSection',
|
||||
|
||||
_init: function() {
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._settings = null;
|
||||
this._clockNotifyId = 0;
|
||||
this._changedId = 0;
|
||||
|
||||
this._locations = [];
|
||||
|
||||
this.actor = new St.Button({ style_class: 'world-clocks-button',
|
||||
x_fill: true,
|
||||
can_focus: true });
|
||||
this.actor.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
let app = this._getClockApp();
|
||||
app.activate();
|
||||
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
}));
|
||||
|
||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
this._grid = new St.Widget({ style_class: 'world-clocks-grid',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(this._grid);
|
||||
|
||||
this.actor.child = this._grid;
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_getClockApp: function() {
|
||||
return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this.actor.visible = (this._getClockApp() != null);
|
||||
|
||||
if (this.actor.visible) {
|
||||
if (!this._settings) {
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.clocks' });
|
||||
this._changedId =
|
||||
this._settings.connect('changed::world-clocks',
|
||||
Lang.bind(this, this._clocksChanged));
|
||||
this._clocksChanged();
|
||||
}
|
||||
} else {
|
||||
if (this._settings)
|
||||
this._settings.disconnect(this._changedId);
|
||||
this._settings = null;
|
||||
this._changedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_clocksChanged: function() {
|
||||
this._grid.destroy_all_children();
|
||||
this._locations = [];
|
||||
|
||||
let world = GWeather.Location.get_world();
|
||||
let clocks = this._settings.get_value('world-clocks').deep_unpack();
|
||||
for (let i = 0; i < clocks.length; i++) {
|
||||
let l = world.deserialize(clocks[i].location);
|
||||
this._locations.push({ location: l });
|
||||
}
|
||||
|
||||
this._locations.sort(function(a, b) {
|
||||
return a.location.get_timezone().get_offset() -
|
||||
b.location.get_timezone().get_offset();
|
||||
});
|
||||
|
||||
let layout = this._grid.layout_manager;
|
||||
let title = (this._locations.length == 0) ? _("Add world clocks…")
|
||||
: _("World Clocks");
|
||||
let header = new St.Label({ style_class: 'world-clocks-header',
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
text: title });
|
||||
layout.attach(header, 0, 0, 2, 1);
|
||||
this.actor.label_actor = header;
|
||||
|
||||
for (let i = 0; i < this._locations.length; i++) {
|
||||
let l = this._locations[i].location;
|
||||
|
||||
let label = new St.Label({ style_class: 'world-clocks-city',
|
||||
text: l.get_city_name(),
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
x_expand: true });
|
||||
|
||||
let time = new St.Label({ style_class: 'world-clocks-time',
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
x_expand: true });
|
||||
|
||||
if (this._grid.text_direction == Clutter.TextDirection.RTL) {
|
||||
layout.attach(time, 0, i + 1, 1, 1);
|
||||
layout.attach(label, 1, i + 1, 1, 1);
|
||||
} else {
|
||||
layout.attach(label, 0, i + 1, 1, 1);
|
||||
layout.attach(time, 1, i + 1, 1, 1);
|
||||
}
|
||||
|
||||
this._locations[i].actor = time;
|
||||
}
|
||||
|
||||
if (this._grid.get_n_children() > 1) {
|
||||
if (!this._clockNotifyId)
|
||||
this._clockNotifyId =
|
||||
this._clock.connect('notify::clock', Lang.bind(this, this._updateLabels));
|
||||
this._updateLabels();
|
||||
} else {
|
||||
if (this._clockNotifyId)
|
||||
this._clock.disconnect(this._clockNotifyId);
|
||||
this._clockNotifyId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_updateLabels: function() {
|
||||
for (let i = 0; i < this._locations.length; i++) {
|
||||
let l = this._locations[i];
|
||||
let tz = GLib.TimeZone.new(l.location.get_timezone().get_tzid());
|
||||
let now = GLib.DateTime.new_now(tz);
|
||||
l.actor.text = Util.formatTime(now, { timeOnly: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const MessagesIndicator = new Lang.Class({
|
||||
Name: 'MessagesIndicator',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Label({ text: '⚫', visible: false, y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
|
||||
this._sources = [];
|
||||
|
||||
Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
|
||||
Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
|
||||
Main.messageTray.connect('queue-changed', Lang.bind(this, this._updateCount));
|
||||
|
||||
let sources = Main.messageTray.getSources();
|
||||
sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
|
||||
},
|
||||
|
||||
_onSourceAdded: function(tray, source) {
|
||||
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;
|
||||
this._sources.forEach(Lang.bind(this,
|
||||
function(source) {
|
||||
count += source.unseenCount;
|
||||
}));
|
||||
count -= Main.messageTray.queueCount;
|
||||
|
||||
this.actor.visible = (count > 0);
|
||||
}
|
||||
});
|
||||
|
||||
const IndicatorPad = new Lang.Class({
|
||||
Name: 'IndicatorPad',
|
||||
Extends: St.Widget,
|
||||
|
||||
_init: function(actor) {
|
||||
this._source = actor;
|
||||
this._source.connect('notify::visible',
|
||||
Lang.bind(this, this.queue_relayout));
|
||||
this.parent();
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
if (this._source.visible)
|
||||
return this._source.get_preferred_width(forHeight);
|
||||
return [0, 0];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
if (this._source.visible)
|
||||
return this._source.get_preferred_height(forWidth);
|
||||
return [0, 0];
|
||||
}
|
||||
});
|
||||
|
||||
const FreezableBinLayout = new Lang.Class({
|
||||
Name: 'FreezableBinLayout',
|
||||
Extends: Clutter.BinLayout,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._frozen = false;
|
||||
this._savedWidth = [NaN, NaN];
|
||||
this._savedHeight = [NaN, NaN];
|
||||
},
|
||||
|
||||
set frozen(v) {
|
||||
if (this._frozen == v)
|
||||
return;
|
||||
|
||||
this._frozen = v;
|
||||
if (!this._frozen)
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
if (!this._frozen || this._savedWidth.some(isNaN))
|
||||
this._savedWidth = this.parent(container, forHeight);
|
||||
return this._savedWidth;
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
if (!this._frozen || this._savedHeight.some(isNaN))
|
||||
this._savedHeight = this.parent(container, forWidth);
|
||||
return this._savedHeight;
|
||||
}
|
||||
});
|
||||
|
||||
const DateMenuButton = new Lang.Class({
|
||||
Name: 'DateMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
@ -44,92 +317,76 @@ const DateMenuButton = new Lang.Class({
|
||||
let hbox;
|
||||
let vbox;
|
||||
|
||||
let menuAlignment = 0.25;
|
||||
let menuAlignment = 0.5;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
menuAlignment = 1.0 - menuAlignment;
|
||||
this.parent(menuAlignment);
|
||||
|
||||
this._clockDisplay = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
|
||||
this._indicator = new MessagesIndicator();
|
||||
|
||||
let box = new St.BoxLayout();
|
||||
box.add_actor(new IndicatorPad(this._indicator.actor));
|
||||
box.add_actor(this._clockDisplay);
|
||||
box.add_actor(this._indicator.actor);
|
||||
|
||||
this.actor.label_actor = this._clockDisplay;
|
||||
this.actor.add_actor(this._clockDisplay);
|
||||
this.actor.add_actor(box);
|
||||
this.actor.add_style_class_name ('clock-display');
|
||||
|
||||
|
||||
let layout = new FreezableBinLayout();
|
||||
let bin = new St.Widget({ layout_manager: layout });
|
||||
this.menu.box.add_child(bin);
|
||||
|
||||
hbox = new St.BoxLayout({ name: 'calendarArea' });
|
||||
this.menu.box.add_child(hbox);
|
||||
bin.add_actor(hbox);
|
||||
|
||||
// Fill up the first column
|
||||
|
||||
vbox = new St.BoxLayout({vertical: true});
|
||||
hbox.add(vbox);
|
||||
|
||||
// Date
|
||||
// Having the ability to go to the current date if the user is already
|
||||
// on the current date can be confusing. So don't make the button reactive
|
||||
// until the selected date changes.
|
||||
this._date = new St.Button({ style_class: 'datemenu-date-label',
|
||||
reactive: false
|
||||
});
|
||||
this._date.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this._calendar.setDate(new Date(), false);
|
||||
}));
|
||||
vbox.add(this._date, { x_fill: false });
|
||||
|
||||
this._eventList = new Calendar.EventsList();
|
||||
this._calendar = new Calendar.Calendar();
|
||||
|
||||
this._calendar.connect('selected-date-changed',
|
||||
Lang.bind(this, function(calendar, date) {
|
||||
this._eventList.setDate(date);
|
||||
|
||||
// Make the button reactive only if the selected date is not the current date.
|
||||
this._date.can_focus = this._date.reactive = !this._isToday(date)
|
||||
layout.frozen = !_isToday(date);
|
||||
this._messageList.setDate(date);
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
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) {
|
||||
item.actor.show_on_set_parent = false;
|
||||
item.actor.reparent(vbox);
|
||||
this._dateAndTimeSeparator = separator;
|
||||
}
|
||||
|
||||
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(this._separator);
|
||||
|
||||
// Fill up the second column
|
||||
hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
// Whenever the menu is opened, select today
|
||||
if (isOpen) {
|
||||
let now = new Date();
|
||||
this._calendar.setDate(now);
|
||||
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
let dateFormat = Shell.util_translate_time_string (N_("%A %B %e, %Y"));
|
||||
this._date.set_label(now.toLocaleFormat(dateFormat));
|
||||
this._date.setDate(now);
|
||||
this._messageList.setDate(now);
|
||||
}
|
||||
}));
|
||||
|
||||
// Fill up the first column
|
||||
this._messageList = new Calendar.CalendarMessageList();
|
||||
hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({ style_class: 'datemenu-calendar-column',
|
||||
vertical: true });
|
||||
hbox.add(vbox);
|
||||
|
||||
this._date = new TodayButton(this._calendar);
|
||||
vbox.add_actor(this._date.actor);
|
||||
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade',
|
||||
x_expand: true, x_fill: true,
|
||||
overlay_scrollbars: true });
|
||||
this._displaysSection.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
vbox.add_actor(this._displaysSection);
|
||||
|
||||
let displaysBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'datemenu-displays-box' });
|
||||
this._displaysSection.add_actor(displaysBox);
|
||||
|
||||
this._clocksItem = new WorldClocksSection();
|
||||
displaysBox.add(this._clocksItem.actor, { x_fill: true });
|
||||
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
@ -139,36 +396,6 @@ const DateMenuButton = new Lang.Class({
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_isToday: function(date) {
|
||||
let now = new Date();
|
||||
return now.getYear() == date.getYear() &&
|
||||
now.getMonth() == date.getMonth() &&
|
||||
now.getDate() == date.getDate();
|
||||
},
|
||||
|
||||
_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;
|
||||
}
|
||||
},
|
||||
|
||||
_getEventSource: function() {
|
||||
return new Calendar.DBusEventSource();
|
||||
},
|
||||
@ -178,12 +405,9 @@ const DateMenuButton = new Lang.Class({
|
||||
this._eventSource.destroy();
|
||||
|
||||
this._calendar.setEventSource(eventSource);
|
||||
this._eventList.setEventSource(eventSource);
|
||||
this._messageList.setEventSource(eventSource);
|
||||
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
|
||||
this._updateEventsVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -195,44 +419,10 @@ const DateMenuButton = new Lang.Class({
|
||||
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;
|
||||
},
|
||||
|
||||
_getCalendarApp: function() {
|
||||
if (this._calendarApp !== undefined)
|
||||
return this._calendarApp;
|
||||
|
||||
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
|
||||
if (apps && (apps.length > 0)) {
|
||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||
let defaultInRecommended = apps.some(function(a) { return a.equal(app); });
|
||||
this._calendarApp = defaultInRecommended ? app : apps[0];
|
||||
} else {
|
||||
this._calendarApp = null;
|
||||
}
|
||||
return this._calendarApp;
|
||||
},
|
||||
|
||||
_getClockApp: function() {
|
||||
return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
|
||||
let app = this._getCalendarApp();
|
||||
if (app.get_id() == 'evolution.desktop')
|
||||
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||
app.launch([], global.create_app_launch_context(0, -1));
|
||||
},
|
||||
|
||||
_onOpenClocksActivate: function() {
|
||||
this.menu.close();
|
||||
let app = this._getClockApp();
|
||||
app.activate();
|
||||
// Displays are not actually expected to launch Settings when activated
|
||||
// but the corresponding app (clocks, weather); however we can consider
|
||||
// that display-specific settings, so re-use "allowSettings" here ...
|
||||
this._displaysSection.visible = Main.sessionMode.allowSettings;
|
||||
}
|
||||
});
|
||||
|
141
js/ui/dnd.js
@ -79,9 +79,12 @@ const _Draggable = new Lang.Class({
|
||||
dragActorOpacity: undefined });
|
||||
|
||||
this.actor = actor;
|
||||
if (!params.manualMode)
|
||||
if (!params.manualMode) {
|
||||
this.actor.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('touch-event',
|
||||
Lang.bind(this, this._onTouchEvent));
|
||||
}
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
@ -121,8 +124,50 @@ const _Draggable = new Lang.Class({
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onTouchEvent: function (actor, event) {
|
||||
if (event.type() != Clutter.EventType.TOUCH_BEGIN ||
|
||||
!global.display.is_pointer_emulating_sequence(event.get_event_sequence()))
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (Tweener.getTweenCount(actor))
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
this._touchSequence = event.get_event_sequence();
|
||||
|
||||
this._buttonDown = true;
|
||||
this._grabActor();
|
||||
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
this._dragStartX = stageX;
|
||||
this._dragStartY = stageY;
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_grabDevice: function(actor) {
|
||||
let manager = Clutter.DeviceManager.get_default();
|
||||
let pointer = manager.get_core_device(Clutter.InputDeviceType.POINTER_DEVICE);
|
||||
|
||||
if (pointer && this._touchSequence)
|
||||
pointer.sequence_grab(this._touchSequence, actor);
|
||||
else if (pointer)
|
||||
pointer.grab (actor);
|
||||
|
||||
this._grabbedDevice = pointer;
|
||||
},
|
||||
|
||||
_ungrabDevice: function() {
|
||||
if (this._touchSequence)
|
||||
this._grabbedDevice.sequence_ungrab (this._touchSequence);
|
||||
else
|
||||
this._grabbedDevice.ungrab();
|
||||
|
||||
this._touchSequence = null;
|
||||
this._grabbedDevice = null;
|
||||
},
|
||||
|
||||
_grabActor: function() {
|
||||
Clutter.grab_pointer(this.actor);
|
||||
this._grabDevice(this.actor);
|
||||
this._onEventId = this.actor.connect('event',
|
||||
Lang.bind(this, this._onEvent));
|
||||
},
|
||||
@ -131,7 +176,7 @@ const _Draggable = new Lang.Class({
|
||||
if (!this._onEventId)
|
||||
return;
|
||||
|
||||
Clutter.ungrab_pointer();
|
||||
this._ungrabDevice();
|
||||
this.actor.disconnect(this._onEventId);
|
||||
this._onEventId = null;
|
||||
},
|
||||
@ -140,13 +185,13 @@ const _Draggable = new Lang.Class({
|
||||
if (!this._eventsGrabbed) {
|
||||
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
|
||||
if (this._eventsGrabbed)
|
||||
Clutter.grab_pointer(_getEventHandlerActor());
|
||||
this._grabDevice(_getEventHandlerActor());
|
||||
}
|
||||
},
|
||||
|
||||
_ungrabEvents: function() {
|
||||
if (this._eventsGrabbed) {
|
||||
Clutter.ungrab_pointer();
|
||||
this._ungrabDevice();
|
||||
Main.popModal(_getEventHandlerActor());
|
||||
this._eventsGrabbed = false;
|
||||
}
|
||||
@ -157,7 +202,9 @@ const _Draggable = new Lang.Class({
|
||||
// didn't start the drag, to drop the draggable in case the drag was in progress, and
|
||||
// to complete the drag and ensure that whatever happens to be under the pointer does
|
||||
// not get triggered if the drag was cancelled with Esc.
|
||||
if (event.type() == Clutter.EventType.BUTTON_RELEASE) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_RELEASE ||
|
||||
(event.type() == Clutter.EventType.TOUCH_END &&
|
||||
global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
|
||||
this._buttonDown = false;
|
||||
if (this._dragInProgress) {
|
||||
return this._dragActorDropped(event);
|
||||
@ -172,7 +219,9 @@ const _Draggable = new Lang.Class({
|
||||
}
|
||||
// We intercept MOTION event to figure out if the drag has started and to draw
|
||||
// this._dragActor under the pointer when dragging is in progress
|
||||
} else if (event.type() == Clutter.EventType.MOTION) {
|
||||
} else if (event.type() == Clutter.EventType.MOTION ||
|
||||
(event.type() == Clutter.EventType.TOUCH_UPDATE &&
|
||||
global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
|
||||
if (this._dragInProgress) {
|
||||
return this._updateDragPosition(event);
|
||||
} else if (this._dragActor == null) {
|
||||
@ -214,7 +263,7 @@ const _Draggable = new Lang.Class({
|
||||
* This function is useful to call if you've specified manualMode
|
||||
* for the draggable.
|
||||
*/
|
||||
startDrag: function (stageX, stageY, time) {
|
||||
startDrag: function (stageX, stageY, time, sequence) {
|
||||
currentDraggable = this;
|
||||
this._dragInProgress = true;
|
||||
|
||||
@ -228,6 +277,8 @@ const _Draggable = new Lang.Class({
|
||||
this.emit('drag-begin', time);
|
||||
if (this._onEventId)
|
||||
this._ungrabActor();
|
||||
|
||||
this._touchSequence = sequence;
|
||||
this._grabEvents();
|
||||
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
|
||||
|
||||
@ -338,8 +389,8 @@ const _Draggable = new Lang.Class({
|
||||
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
|
||||
if ((Math.abs(stageX - this._dragStartX) > threshold ||
|
||||
Math.abs(stageY - this._dragStartY) > threshold)) {
|
||||
this.startDrag(stageX, stageY, event.get_time());
|
||||
this._updateDragPosition(event);
|
||||
this.startDrag(stageX, stageY, event.get_time(), this._touchSequence);
|
||||
this._updateDragPosition(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -520,20 +571,13 @@ const _Draggable = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this._animationInProgress = true;
|
||||
// No target, so snap back
|
||||
Tweener.addTween(this._dragActor,
|
||||
{ x: snapBackX,
|
||||
y: snapBackY,
|
||||
scale_x: snapBackScale,
|
||||
scale_y: snapBackScale,
|
||||
opacity: this._dragOrigOpacity,
|
||||
time: SNAP_BACK_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._onAnimationComplete,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [this._dragActor, eventTime]
|
||||
});
|
||||
this._animateDragEnd(eventTime,
|
||||
{ x: snapBackX,
|
||||
y: snapBackY,
|
||||
scale_x: snapBackScale,
|
||||
scale_y: snapBackScale,
|
||||
time: SNAP_BACK_ANIMATION_TIME,
|
||||
});
|
||||
},
|
||||
|
||||
_restoreDragActor: function(eventTime) {
|
||||
@ -545,18 +589,44 @@ const _Draggable = new Lang.Class({
|
||||
this._dragActor.set_scale(restoreScale, restoreScale);
|
||||
this._dragActor.opacity = 0;
|
||||
|
||||
this._animateDragEnd(eventTime,
|
||||
{ time: REVERT_ANIMATION_TIME });
|
||||
},
|
||||
|
||||
_animateDragEnd: function (eventTime, params) {
|
||||
this._animationInProgress = true;
|
||||
Tweener.addTween(this._dragActor,
|
||||
{ opacity: this._dragOrigOpacity,
|
||||
time: REVERT_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._onAnimationComplete,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [this._dragActor, eventTime]
|
||||
});
|
||||
|
||||
// finish animation if the actor gets destroyed
|
||||
// during it
|
||||
this._dragActorDestroyId =
|
||||
this._dragActor.connect('destroy',
|
||||
Lang.bind(this, this._finishAnimation));
|
||||
|
||||
params['opacity'] = this._dragOrigOpacity;
|
||||
params['transition'] = 'easeOutQuad';
|
||||
params['onComplete'] = this._onAnimationComplete;
|
||||
params['onCompleteScope'] = this;
|
||||
params['onCompleteParams'] = [this._dragActor, eventTime];
|
||||
|
||||
// start the animation
|
||||
Tweener.addTween(this._dragActor, params)
|
||||
},
|
||||
|
||||
_finishAnimation : function () {
|
||||
if (!this._animationInProgress)
|
||||
return
|
||||
|
||||
this._animationInProgress = false;
|
||||
if (!this._buttonDown)
|
||||
this._dragComplete();
|
||||
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
},
|
||||
|
||||
_onAnimationComplete : function (dragActor, eventTime) {
|
||||
dragActor.disconnect(this._dragActorDestroyId);
|
||||
this._dragActorDestroyId = 0;
|
||||
|
||||
if (this._dragOrigParent) {
|
||||
Main.uiGroup.remove_child(this._dragActor);
|
||||
this._dragOrigParent.add_actor(this._dragActor);
|
||||
@ -565,12 +635,9 @@ const _Draggable = new Lang.Class({
|
||||
} else {
|
||||
dragActor.destroy();
|
||||
}
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this.emit('drag-end', eventTime, false);
|
||||
|
||||
this._animationInProgress = false;
|
||||
if (!this._buttonDown)
|
||||
this._dragComplete();
|
||||
this.emit('drag-end', eventTime, false);
|
||||
this._finishAnimation();
|
||||
},
|
||||
|
||||
_dragComplete: function() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2010 Red Hat, Inc
|
||||
* Copyright 2010-2016 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
|
||||
@ -114,7 +114,7 @@ const restartDialogContent = {
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const restartInstallDialogContent = {
|
||||
const restartUpdateDialogContent = {
|
||||
|
||||
subject: C_("title", "Restart & Install Updates"),
|
||||
description: function(seconds) {
|
||||
@ -132,18 +132,38 @@ const restartInstallDialogContent = {
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const restartUpgradeDialogContent = {
|
||||
|
||||
subject: C_("title", "Restart & Install Upgrade"),
|
||||
upgradeDescription: function(distroName, distroVersion) {
|
||||
/* Translators: This is the text displayed for system upgrades in the
|
||||
shut down dialog. First %s gets replaced with the distro name and
|
||||
second %s with the distro version to upgrade to */
|
||||
return _("%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have backed up and that the computer is plugged in.").format(distroName, distroVersion);
|
||||
},
|
||||
disableTimer: true,
|
||||
showBatteryWarning: false,
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart & Install") }],
|
||||
iconName: 'view-refresh-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const DialogType = {
|
||||
LOGOUT: 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */,
|
||||
SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */,
|
||||
RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */,
|
||||
UPDATE_RESTART: 3
|
||||
UPDATE_RESTART: 3,
|
||||
UPGRADE_RESTART: 4
|
||||
};
|
||||
|
||||
const DialogContent = {
|
||||
0 /* DialogType.LOGOUT */: logoutDialogContent,
|
||||
1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* DialogType.RESTART */: restartDialogContent,
|
||||
3 /* DialogType.UPDATE_RESTART */: restartInstallDialogContent
|
||||
3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent,
|
||||
4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent
|
||||
};
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
@ -163,7 +183,10 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
|
||||
const PkOfflineIface = '<node> \
|
||||
<interface name="org.freedesktop.PackageKit.Offline"> \
|
||||
<property name="UpdatePrepared" type="b" access="read"/> \
|
||||
<property name="TriggerAction" type="s" access="read"/> \
|
||||
<property name="UpdateTriggered" type="b" access="read"/> \
|
||||
<property name="UpgradePrepared" type="b" access="read"/> \
|
||||
<property name="UpgradeTriggered" type="b" access="read"/> \
|
||||
<property name="PreparedUpgrade" type="a{sv}" access="read"/> \
|
||||
<method name="Trigger"> \
|
||||
<arg type="s" name="action" direction="in"/> \
|
||||
</method> \
|
||||
@ -415,11 +438,19 @@ const EndSessionDialog = new Lang.Class({
|
||||
|
||||
if (dialogContent.descriptionWithUser)
|
||||
description = dialogContent.descriptionWithUser(realName, displayTime);
|
||||
else
|
||||
description = dialogContent.description(displayTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Use a different description when we are installing a system upgrade
|
||||
if (dialogContent.upgradeDescription) {
|
||||
let name = this._pkOfflineProxy.PreparedUpgrade['name'].deep_unpack();
|
||||
let version = this._pkOfflineProxy.PreparedUpgrade['version'].deep_unpack();
|
||||
|
||||
if (name != null && version != null)
|
||||
description = dialogContent.upgradeDescription(name, version);
|
||||
}
|
||||
|
||||
// Fall back to regular description
|
||||
if (!description)
|
||||
description = dialogContent.description(displayTime);
|
||||
|
||||
@ -698,9 +729,12 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._type = type;
|
||||
|
||||
if (this._type == DialogType.RESTART &&
|
||||
this._pkOfflineProxy.TriggerAction == 'reboot')
|
||||
this._type = DialogType.UPDATE_RESTART;
|
||||
if (this._type == DialogType.RESTART) {
|
||||
if (this._pkOfflineProxy.UpdateTriggered)
|
||||
this._type = DialogType.UPDATE_RESTART;
|
||||
else if (this._pkOfflineProxy.UpgradeTriggered)
|
||||
this._type = DialogType.UPGRADE_RESTART;
|
||||
}
|
||||
|
||||
this._applications = [];
|
||||
this._applicationList.destroy_all_children();
|
||||
@ -727,19 +761,19 @@ const EndSessionDialog = new Lang.Class({
|
||||
if (dialogContent.showOtherSessions)
|
||||
this._loadSessions();
|
||||
|
||||
let updateAlreadyTriggered = this._pkOfflineProxy.TriggerAction == 'power-off' || this._pkOfflineProxy.TriggerAction == 'reboot';
|
||||
let updateTriggered = this._pkOfflineProxy.UpdateTriggered;
|
||||
let updatePrepared = this._pkOfflineProxy.UpdatePrepared;
|
||||
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
|
||||
|
||||
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
|
||||
this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed);
|
||||
this._checkBox.actor.checked = (updatePrepared && updateAlreadyTriggered);
|
||||
this._checkBox.actor.checked = (updatePrepared && updateTriggered);
|
||||
|
||||
// We show the warning either together with the checkbox, or when
|
||||
// updates have already been triggered, but the user doesn't have
|
||||
// enough permissions to cancel them.
|
||||
this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
|
||||
(this._checkBox.actor.visible || updatePrepared && updateAlreadyTriggered && !updatesAllowed));
|
||||
(this._checkBox.actor.visible || updatePrepared && updateTriggered && !updatesAllowed));
|
||||
|
||||
this._updateButtons();
|
||||
|
||||
@ -749,7 +783,9 @@ const EndSessionDialog = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this._startTimer();
|
||||
if (!dialogContent.disableTimer)
|
||||
this._startTimer();
|
||||
|
||||
this._sync();
|
||||
|
||||
let signalId = this.connect('opened',
|
||||
|
@ -81,7 +81,6 @@ function init() {
|
||||
|
||||
// Miscellaneous monkeypatching
|
||||
_patchContainerClass(St.BoxLayout);
|
||||
_patchContainerClass(St.Table);
|
||||
|
||||
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
|
||||
column_spacing: 'spacing-columns' });
|
||||
|
@ -211,7 +211,7 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
let icon = new St.Icon({ gicon: gicon });
|
||||
box.add(icon);
|
||||
|
||||
let label = new St.Label({ style_class: 'prompt-dialog-headline',
|
||||
let label = new St.Label({ style_class: 'prompt-dialog-headline headline',
|
||||
text: message });
|
||||
box.add(label);
|
||||
},
|
||||
|
@ -334,7 +334,7 @@ function _sessionUpdated() {
|
||||
// from allowExtensions in the future
|
||||
if (Main.sessionMode.allowExtensions) {
|
||||
if (initted)
|
||||
onEnabledExtensionsChanged();
|
||||
enabledExtensions = getEnabledExtensions();
|
||||
enableAllExtensions();
|
||||
} else {
|
||||
disableAllExtensions();
|
||||
|
@ -158,10 +158,22 @@ const CandidatePopup = new Lang.Class({
|
||||
|
||||
panelService.connect('set-cursor-location',
|
||||
Lang.bind(this, function(ps, x, y, w, h) {
|
||||
Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
||||
this._setDummyCursorGeometry(x, y, w, h);
|
||||
}));
|
||||
try {
|
||||
panelService.connect('set-cursor-location-relative',
|
||||
Lang.bind(this, function(ps, x, y, w, h) {
|
||||
if (!global.display.focus_window)
|
||||
return;
|
||||
let window = global.display.focus_window.get_compositor_private();
|
||||
this._setDummyCursorGeometry(window.x + x, window.y + y, w, h);
|
||||
}));
|
||||
} catch(e) {
|
||||
// Only recent IBus versions have support for this signal
|
||||
// which is used for wayland clients. In order to work
|
||||
// with older IBus versions we can silently ignore the
|
||||
// signal's absence.
|
||||
}
|
||||
panelService.connect('update-preedit-text',
|
||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||
this._preeditText.visible = visible;
|
||||
@ -246,6 +258,12 @@ const CandidatePopup = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_setDummyCursorGeometry: function(x, y, w, h) {
|
||||
Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let isVisible = (this._preeditText.visible ||
|
||||
this._auxText.visible ||
|
||||
|
@ -6,9 +6,11 @@ const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const InputSourceManager = imports.ui.status.keyboard;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Layout = imports.ui.layout;
|
||||
@ -23,9 +25,6 @@ const KEYBOARD_TYPE = 'keyboard-type';
|
||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
||||
|
||||
const CURSOR_BUS_NAME = 'org.gnome.SettingsDaemon.Cursor';
|
||||
const CURSOR_OBJECT_PATH = '/org/gnome/SettingsDaemon/Cursor';
|
||||
|
||||
const CARIBOU_BUS_NAME = 'org.gnome.Caribou.Daemon';
|
||||
const CARIBOU_OBJECT_PATH = '/org/gnome/Caribou/Daemon';
|
||||
|
||||
@ -60,14 +59,7 @@ const CaribouDaemonIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const CursorManagerIface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Cursor"> \
|
||||
<property name="ShowOSK" type="b" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const CaribouDaemonProxy = Gio.DBusProxy.makeProxyWrapper(CaribouDaemonIface);
|
||||
const CursorManagerProxy = Gio.DBusProxy.makeProxyWrapper(CursorManagerIface);
|
||||
|
||||
const Key = new Lang.Class({
|
||||
Name: 'Key',
|
||||
@ -120,6 +112,35 @@ const Key = new Lang.Class({
|
||||
key.release();
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
button.connect('touch-event', Lang.bind(this,
|
||||
function (actor, event) {
|
||||
let device = event.get_device();
|
||||
let sequence = event.get_event_sequence();
|
||||
|
||||
// We only handle touch events here on wayland. On X11
|
||||
// we do get emulated pointer events, which already works
|
||||
// for single-touch cases. Besides, the X11 passive touch grab
|
||||
// set up by Mutter will make us see first the touch events
|
||||
// and later the pointer events, so it will look like two
|
||||
// unrelated series of events, we want to avoid double handling
|
||||
// in these cases.
|
||||
if (!Meta.is_wayland_compositor())
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (!this._touchPressed &&
|
||||
event.type() == Clutter.EventType.TOUCH_BEGIN) {
|
||||
device.sequence_grab(sequence, actor);
|
||||
this._touchPressed = true;
|
||||
key.press();
|
||||
} else if (this._touchPressed &&
|
||||
event.type() == Clutter.EventType.TOUCH_END &&
|
||||
device.sequence_get_grabbed_actor(sequence) == actor) {
|
||||
device.sequence_ungrab(sequence);
|
||||
this._touchPressed = false;
|
||||
key.release();
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
|
||||
return button;
|
||||
},
|
||||
@ -184,21 +205,23 @@ const Keyboard = new Lang.Class({
|
||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._sync));
|
||||
this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
|
||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._sync));
|
||||
this._watchNameId = Gio.bus_watch_name(Gio.BusType.SESSION, CURSOR_BUS_NAME, 0,
|
||||
Lang.bind(this, this._sync),
|
||||
Lang.bind(this, this._sync));
|
||||
this._daemonProxy = null;
|
||||
this._cursorProxy = new CursorManagerProxy(Gio.DBus.session, CURSOR_BUS_NAME,
|
||||
CURSOR_OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._cursorProxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
}));
|
||||
this._lastDeviceId = null;
|
||||
|
||||
if (Meta.is_wayland_compositor() &&
|
||||
Caribou.DisplayAdapter.set_default)
|
||||
Caribou.DisplayAdapter.set_default(new ShellWaylandAdapter());
|
||||
|
||||
Meta.get_backend().connect('last-device-changed', Lang.bind(this,
|
||||
function (backend, deviceId) {
|
||||
let manager = Clutter.DeviceManager.get_default();
|
||||
let device = manager.get_device(deviceId);
|
||||
|
||||
if (device.get_device_name().indexOf('XTEST') < 0) {
|
||||
this._lastDeviceId = deviceId;
|
||||
this._sync();
|
||||
}
|
||||
}));
|
||||
this._sync();
|
||||
|
||||
this._showIdleId = 0;
|
||||
@ -217,9 +240,22 @@ const Keyboard = new Lang.Class({
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
_lastDeviceIsTouchscreen: function () {
|
||||
if (!this._lastDeviceId)
|
||||
return false;
|
||||
|
||||
let manager = Clutter.DeviceManager.get_default();
|
||||
let device = manager.get_device(this._lastDeviceId);
|
||||
|
||||
if (!device)
|
||||
return false;
|
||||
|
||||
return device.get_device_type() == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE;
|
||||
},
|
||||
|
||||
_sync: function () {
|
||||
this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD) ||
|
||||
this._cursorProxy.ShowOSK;
|
||||
this._lastDeviceIsTouchscreen();
|
||||
if (!this._enableKeyboard && !this._keyboard)
|
||||
return;
|
||||
if (this._enableKeyboard && this._keyboard &&
|
||||
@ -717,3 +753,53 @@ const KeyboardSource = new Lang.Class({
|
||||
this._keyboard.show(Main.layoutManager.bottomIndex);
|
||||
}
|
||||
});
|
||||
|
||||
const ShellWaylandAdapter = new Lang.Class({
|
||||
Name: 'ShellWaylandAdapter',
|
||||
Extends: Caribou.XAdapter,
|
||||
|
||||
_init: function () {
|
||||
this.parent();
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
this._virtualDevice = deviceManager.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE);
|
||||
|
||||
this._inputSourceManager = InputSourceManager.getInputSourceManager();
|
||||
this._sourceChangedId = this._inputSourceManager.connect('current-source-changed',
|
||||
Lang.bind(this, this._onSourceChanged));
|
||||
this._sourcesModifiedId = this._inputSourceManager.connect ('sources-changed',
|
||||
Lang.bind(this, this._onSourcesModified));
|
||||
},
|
||||
|
||||
_onSourcesModified: function () {
|
||||
this.emit('config-changed');
|
||||
},
|
||||
|
||||
_onSourceChanged: function (inputSourceManager, oldSource) {
|
||||
let source = inputSourceManager.currentSource;
|
||||
this.emit('group-changed', source.index, source.id, '');
|
||||
},
|
||||
|
||||
vfunc_get_groups: function () {
|
||||
let inputSources = this._inputSourceManager.inputSources;
|
||||
let groups = []
|
||||
let variants = [];
|
||||
|
||||
for (let i in inputSources) {
|
||||
let is = inputSources[i];
|
||||
groups[is.index] = is.id;
|
||||
variants[is.index] = '';
|
||||
}
|
||||
|
||||
return [groups, groups.length, variants, variants.length];
|
||||
},
|
||||
|
||||
vfunc_keyval_press: function(keyval) {
|
||||
this._virtualDevice.notify_keyval(Clutter.get_current_event_time(),
|
||||
keyval, Clutter.KeyState.PRESSED);
|
||||
},
|
||||
|
||||
vfunc_keyval_release: function(keyval) {
|
||||
this._virtualDevice.notify_keyval(Clutter.get_current_event_time(),
|
||||
keyval, Clutter.KeyState.RELEASED);
|
||||
},
|
||||
});
|
||||
|
181
js/ui/layout.js
@ -11,6 +11,7 @@ const St = imports.gi.St;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const BackgroundMenu = imports.ui.backgroundMenu;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
@ -21,11 +22,6 @@ const STARTUP_ANIMATION_TIME = 0.5;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.15;
|
||||
const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
|
||||
|
||||
// The message tray takes this much pressure
|
||||
// in the pressure barrier at once to release it.
|
||||
const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
|
||||
const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
|
||||
|
||||
const HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
|
||||
const HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
|
||||
|
||||
@ -50,11 +46,16 @@ const MonitorConstraint = new Lang.Class({
|
||||
'index': GObject.ParamSpec.int('index',
|
||||
'Monitor index', 'Track specific monitor',
|
||||
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
-1, 64, -1)},
|
||||
-1, 64, -1),
|
||||
'work-area': GObject.ParamSpec.boolean('work-area',
|
||||
'Work-area', 'Track monitor\'s work-area',
|
||||
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
false)},
|
||||
|
||||
_init: function(props) {
|
||||
this._primary = false;
|
||||
this._index = -1;
|
||||
this._workArea = false;
|
||||
|
||||
this.parent(props);
|
||||
},
|
||||
@ -84,6 +85,19 @@ const MonitorConstraint = new Lang.Class({
|
||||
this.notify('index');
|
||||
},
|
||||
|
||||
get work_area() {
|
||||
return this._workArea;
|
||||
},
|
||||
|
||||
set work_area(v) {
|
||||
if (v == this._workArea)
|
||||
return;
|
||||
this._workArea = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
this.notify('work-area');
|
||||
},
|
||||
|
||||
vfunc_set_actor: function(actor) {
|
||||
if (actor) {
|
||||
if (!this._monitorsChangedId) {
|
||||
@ -91,10 +105,21 @@ const MonitorConstraint = new Lang.Class({
|
||||
this.actor.queue_relayout();
|
||||
}));
|
||||
}
|
||||
|
||||
if (!this._workareasChangedId) {
|
||||
this._workareasChangedId = global.screen.connect('workareas-changed', Lang.bind(this, function() {
|
||||
if (this._workArea)
|
||||
this.actor.queue_relayout();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
if (this._monitorsChangedId)
|
||||
Main.layoutManager.disconnect(this._monitorsChangedId);
|
||||
this._monitorsChangedId = 0;
|
||||
|
||||
if (this._workareasChangedId)
|
||||
global.screen.disconnect(this._workareasChangedId);
|
||||
this._workareasChangedId = 0;
|
||||
}
|
||||
|
||||
this.parent(actor);
|
||||
@ -104,15 +129,21 @@ const MonitorConstraint = new Lang.Class({
|
||||
if (!this._primary && this._index < 0)
|
||||
return;
|
||||
|
||||
let monitor;
|
||||
if (this._primary) {
|
||||
monitor = Main.layoutManager.primaryMonitor;
|
||||
let index;
|
||||
if (this._primary)
|
||||
index = Main.layoutManager.primaryIndex;
|
||||
else
|
||||
index = Math.min(this._index, Main.layoutManager.monitors.length - 1);
|
||||
|
||||
let rect;
|
||||
if (this._workArea) {
|
||||
let ws = global.screen.get_workspace_by_index(0);
|
||||
rect = ws.get_work_area_for_monitor(index);
|
||||
} else {
|
||||
let index = Math.min(this._index, Main.layoutManager.monitors.length - 1);
|
||||
monitor = Main.layoutManager.monitors[index];
|
||||
rect = Main.layoutManager.monitors[index];
|
||||
}
|
||||
|
||||
actorBox.init_rect(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||
actorBox.init_rect(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
});
|
||||
|
||||
@ -135,6 +166,7 @@ const Monitor = new Lang.Class({
|
||||
const defaultParams = {
|
||||
trackFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
|
||||
const LayoutManager = new Lang.Class({
|
||||
@ -149,7 +181,6 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._keyboardIndex = -1;
|
||||
this._rightPanelBarrier = null;
|
||||
this._trayBarrier = null;
|
||||
|
||||
this._inOverview = false;
|
||||
this._updateRegionIdle = 0;
|
||||
@ -189,7 +220,8 @@ const LayoutManager = new Lang.Class({
|
||||
global.stage.add_child(this.uiGroup);
|
||||
|
||||
this.overviewGroup = new St.Widget({ name: 'overviewGroup',
|
||||
visible: false });
|
||||
visible: false,
|
||||
reactive: true });
|
||||
this.addChrome(this.overviewGroup);
|
||||
|
||||
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
|
||||
@ -206,11 +238,6 @@ const LayoutManager = new Lang.Class({
|
||||
this.panelBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._panelBoxChanged));
|
||||
|
||||
this.trayBox = new St.Widget({ name: 'trayBox',
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.addChrome(this.trayBox);
|
||||
this._setupTrayPressure();
|
||||
|
||||
this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup',
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.uiGroup.add_actor(this.modalDialogGroup);
|
||||
@ -248,6 +275,18 @@ const LayoutManager = new Lang.Class({
|
||||
global.screen.connect('in-fullscreen-changed',
|
||||
Lang.bind(this, this._updateFullscreen));
|
||||
this._monitorsChanged();
|
||||
|
||||
// NVIDIA drivers don't preserve FBO contents across
|
||||
// suspend/resume, see
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=739178
|
||||
if (Shell.util_need_background_refresh()) {
|
||||
LoginManager.getLoginManager().connect('prepare-for-sleep',
|
||||
function(lm, suspending) {
|
||||
if (suspending)
|
||||
return;
|
||||
Meta.Background.refresh_all();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// This is called by Main after everything else is constructed
|
||||
@ -423,10 +462,6 @@ const LayoutManager = new Lang.Class({
|
||||
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||
|
||||
this.keyboardIndex = this.primaryIndex;
|
||||
|
||||
this.trayBox.set_position(this.bottomMonitor.x,
|
||||
this.bottomMonitor.y + this.bottomMonitor.height);
|
||||
this.trayBox.set_size(this.bottomMonitor.width, -1);
|
||||
},
|
||||
|
||||
_panelBoxChanged: function() {
|
||||
@ -455,50 +490,9 @@ const LayoutManager = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_setupTrayPressure: function() {
|
||||
this._trayPressure = new PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
|
||||
MESSAGE_TRAY_PRESSURE_TIMEOUT,
|
||||
Shell.ActionMode.NORMAL |
|
||||
Shell.ActionMode.OVERVIEW);
|
||||
this._trayPressure.setEventFilter(this._trayBarrierEventFilter);
|
||||
this._trayPressure.connect('trigger', function(barrier) {
|
||||
if (Main.layoutManager.bottomMonitor.inFullscreen)
|
||||
return;
|
||||
|
||||
Main.messageTray.openTray();
|
||||
});
|
||||
},
|
||||
|
||||
_updateTrayBarrier: function() {
|
||||
let monitor = this.bottomMonitor;
|
||||
|
||||
if (this._trayBarrier) {
|
||||
this._trayPressure.removeBarrier(this._trayBarrier);
|
||||
this._trayBarrier.destroy();
|
||||
this._trayBarrier = null;
|
||||
}
|
||||
|
||||
this._trayBarrier = new Meta.Barrier({ display: global.display,
|
||||
x1: monitor.x, x2: monitor.x + monitor.width,
|
||||
y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
|
||||
directions: Meta.BarrierDirection.NEGATIVE_Y });
|
||||
this._trayPressure.addBarrier(this._trayBarrier);
|
||||
},
|
||||
|
||||
_trayBarrierEventFilter: function(event) {
|
||||
// Throw out all events where the pointer was grabbed by another
|
||||
// client, as the client that grabbed the pointer expects to have
|
||||
// complete control over it
|
||||
if (event.grabbed && Main.modalCount == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
this._updateMonitors();
|
||||
this._updateBoxes();
|
||||
this._updateTrayBarrier();
|
||||
this._updateHotCorners();
|
||||
this._updateBackgrounds();
|
||||
this._updateFullscreen();
|
||||
@ -598,7 +592,10 @@ const LayoutManager = new Lang.Class({
|
||||
this.addChrome(this._coverPane);
|
||||
|
||||
if (Meta.is_restart()) {
|
||||
// On restart, we don't do an animation
|
||||
// On restart, we don't do an animation. Force an update of the
|
||||
// regions immediately so that maximized windows restore to the
|
||||
// right size taking struts into account.
|
||||
this._updateRegions();
|
||||
} else if (Main.sessionMode.isGreeter) {
|
||||
this.panelBox.translation_y = -this.panelBox.height;
|
||||
} else {
|
||||
@ -608,7 +605,6 @@ const LayoutManager = new Lang.Class({
|
||||
// the UI group to get the correct allocation for the struts.
|
||||
this._updateRegions();
|
||||
|
||||
this.trayBox.hide();
|
||||
this.keyboardBox.hide();
|
||||
|
||||
let monitor = this.primaryMonitor;
|
||||
@ -675,7 +671,6 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._startingUp = false;
|
||||
|
||||
this.trayBox.show();
|
||||
this.keyboardBox.show();
|
||||
|
||||
if (!Main.sessionMode.isGreeter) {
|
||||
@ -748,10 +743,11 @@ const LayoutManager = new Lang.Class({
|
||||
// @actor: an actor to add to the chrome
|
||||
// @params: (optional) additional params
|
||||
//
|
||||
// Adds @actor to the chrome, and extends the input region
|
||||
// to include it. Changes in @actor's size, position, and
|
||||
// visibility will automatically result in appropriate changes
|
||||
// to the input region.
|
||||
// Adds @actor to the chrome, and (unless %affectsInputRegion in
|
||||
// @params is %false) extends the input region to include it.
|
||||
// Changes in @actor's size, position, and visibility will
|
||||
// automatically result in appropriate changes to the input
|
||||
// region.
|
||||
//
|
||||
// If %affectsStruts in @params is %true (and @actor is along a
|
||||
// screen edge), then @actor's size and position will also affect
|
||||
@ -845,6 +841,7 @@ const LayoutManager = new Lang.Class({
|
||||
// need to connect to 'destroy' too.
|
||||
|
||||
this._trackedActors.push(actorData);
|
||||
this._updateActorVisibility(actorData);
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
@ -863,25 +860,23 @@ const LayoutManager = new Lang.Class({
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_updateActorVisibility: function(actorData) {
|
||||
if (!actorData.trackFullscreen)
|
||||
return;
|
||||
|
||||
let monitor = this.findMonitorForActor(actorData.actor);
|
||||
actorData.actor.visible = !(global.window_group.visible &&
|
||||
monitor &&
|
||||
monitor.inFullscreen);
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
|
||||
|
||||
global.window_group.visible = windowsVisible;
|
||||
global.top_window_group.visible = windowsVisible;
|
||||
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i], visible;
|
||||
if (!actorData.trackFullscreen)
|
||||
continue;
|
||||
|
||||
if (!windowsVisible)
|
||||
visible = true;
|
||||
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
visible = false;
|
||||
else
|
||||
visible = true;
|
||||
actorData.actor.visible = visible;
|
||||
}
|
||||
this._trackedActors.forEach(Lang.bind(this, this._updateActorVisibility));
|
||||
},
|
||||
|
||||
getWorkAreaForMonitor: function(monitorIndex) {
|
||||
@ -947,13 +942,18 @@ const LayoutManager = new Lang.Class({
|
||||
if (Main.modalCount > 0)
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
// Bug workaround - get_transformed_position()/get_transformed_size() don't work after
|
||||
// a change in stage size until the first pick or paint.
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=761565
|
||||
global.stage.get_actor_at_pos(Clutter.PickMode.ALL, 0, 0);
|
||||
|
||||
let rects = [], struts = [], i;
|
||||
let isPopupMenuVisible = global.top_window_group.get_children().some(isPopupMetaWindow);
|
||||
let wantsInputRegion = !isPopupMenuVisible;
|
||||
|
||||
for (i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!wantsInputRegion && !actorData.affectsStruts)
|
||||
if (!(actorData.affectsInputRegion && wantsInputRegion) && !actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
let [x, y] = actorData.actor.get_transformed_position();
|
||||
@ -963,7 +963,7 @@ const LayoutManager = new Lang.Class({
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
|
||||
if (wantsInputRegion && actorData.actor.get_paint_visibility())
|
||||
if (actorData.affectsInputRegion && wantsInputRegion && actorData.actor.get_paint_visibility())
|
||||
rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h }));
|
||||
|
||||
if (actorData.affectsStruts) {
|
||||
@ -1324,8 +1324,11 @@ const PressureBarrier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onBarrierLeft: function(barrier, event) {
|
||||
this._reset();
|
||||
this._isTriggered = false;
|
||||
barrier._isHit = false;
|
||||
if (this._barriers.every(function(b) { return !b._isHit; })) {
|
||||
this._reset();
|
||||
this._isTriggered = false;
|
||||
}
|
||||
},
|
||||
|
||||
_trigger: function() {
|
||||
@ -1335,6 +1338,8 @@ const PressureBarrier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onBarrierHit: function(barrier, event) {
|
||||
barrier._isHit = true;
|
||||
|
||||
// If we've triggered the barrier, wait until the pointer has the
|
||||
// left the barrier hitbox until we trigger it again.
|
||||
if (this._isTriggered)
|
||||
|
272
js/ui/legacyTray.js
Normal file
@ -0,0 +1,272 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const Lang = imports.lang;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const OverviewControls = imports.ui.overviewControls;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
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'
|
||||
};
|
||||
|
||||
// Offset of the original position from the bottom-right corner
|
||||
const CONCEALED_WIDTH = 3;
|
||||
const REVEAL_ANIMATION_TIME = 0.2;
|
||||
const TEMP_REVEAL_TIME = 2;
|
||||
|
||||
const BARRIER_THRESHOLD = 70;
|
||||
const BARRIER_TIMEOUT = 1000;
|
||||
|
||||
const LegacyTray = new Lang.Class({
|
||||
Name: 'LegacyTray',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Widget({ clip_to_allocation: true,
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
let constraint = new Layout.MonitorConstraint({ primary: true,
|
||||
work_area: true });
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this._slideLayout = new OverviewControls.SlideLayout();
|
||||
this._slideLayout.translationX = 0;
|
||||
this._slideLayout.slideDirection = OverviewControls.SlideDirection.LEFT;
|
||||
|
||||
this._slider = new St.Widget({ x_expand: true, y_expand: true,
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
layout_manager: this._slideLayout });
|
||||
this.actor.add_actor(this._slider);
|
||||
this._slider.connect('notify::allocation', Lang.bind(this, this._syncBarrier));
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'legacy-tray' });
|
||||
this._slider.add_actor(this._box);
|
||||
|
||||
this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle',
|
||||
/* translators: 'Hide' is a verb */
|
||||
accessible_name: _("Hide tray"),
|
||||
can_focus: true });
|
||||
this._concealHandle.child = new St.Icon({ icon_name: 'go-previous-symbolic' });
|
||||
this._box.add_child(this._concealHandle);
|
||||
|
||||
this._iconBox = new St.BoxLayout({ style_class: 'legacy-tray-icon-box' });
|
||||
this._box.add_actor(this._iconBox);
|
||||
|
||||
this._revealHandle = new St.Button({ style_class: 'legacy-tray-handle' });
|
||||
this._revealHandle.child = new St.Icon({ icon_name: 'go-next-symbolic' });
|
||||
this._box.add_child(this._revealHandle);
|
||||
|
||||
this._revealHandle.bind_property('visible',
|
||||
this._concealHandle, 'visible',
|
||||
GObject.BindingFlags.BIDIRECTIONAL |
|
||||
GObject.BindingFlags.INVERT_BOOLEAN);
|
||||
this._revealHandle.connect('notify::visible',
|
||||
Lang.bind(this, this._sync));
|
||||
this._revealHandle.connect('notify::hover',
|
||||
Lang.bind(this ,this._sync));
|
||||
this._revealHandle.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._concealHandle.show();
|
||||
}));
|
||||
this._concealHandle.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._revealHandle.show();
|
||||
}));
|
||||
|
||||
this._horizontalBarrier = null;
|
||||
this._pressureBarrier = new Layout.PressureBarrier(BARRIER_THRESHOLD,
|
||||
BARRIER_TIMEOUT,
|
||||
Shell.ActionMode.NORMAL);
|
||||
this._pressureBarrier.connect('trigger', Lang.bind(this, function() {
|
||||
this._concealHandle.show();
|
||||
}));
|
||||
|
||||
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
|
||||
Main.layoutManager.trackChrome(this._slider, { affectsInputRegion: true });
|
||||
Main.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor,
|
||||
_("Status Icons"), 'focus-legacy-systray-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.BOTTOM });
|
||||
|
||||
this._trayManager = new Shell.TrayManager();
|
||||
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
this._trayManager.manage_screen(global.screen, this.actor);
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this,
|
||||
function() {
|
||||
Tweener.removeTweens(this._slider);
|
||||
Tweener.addTween(this._slider, { opacity: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}));
|
||||
Main.overview.connect('shown', Lang.bind(this, this._sync));
|
||||
Main.overview.connect('hiding', Lang.bind(this,
|
||||
function() {
|
||||
this._sync();
|
||||
Tweener.removeTweens(this._slider);
|
||||
Tweener.addTween(this._slider, { opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}));
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
global.screen.connect('in-fullscreen-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
|
||||
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(tm, icon) {
|
||||
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
|
||||
return;
|
||||
|
||||
let button = new St.Button({ child: icon,
|
||||
style_class: 'legacy-tray-icon',
|
||||
button_mask: St.ButtonMask.ONE |
|
||||
St.ButtonMask.TWO |
|
||||
St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
x_fill: true, y_fill: true });
|
||||
|
||||
let app = Shell.WindowTracker.get_default().get_app_from_pid(icon.pid);
|
||||
if (!app)
|
||||
app = Shell.AppSystem.get_default().lookup_startup_wmclass(wmClass);
|
||||
if (!app)
|
||||
app = Shell.AppSystem.get_default().lookup_desktop_wmclass(wmClass);
|
||||
if (app)
|
||||
button.accessible_name = app.get_name();
|
||||
else
|
||||
button.accessible_name = icon.title;
|
||||
|
||||
button.connect('clicked',
|
||||
function() {
|
||||
icon.click(Clutter.get_current_event());
|
||||
});
|
||||
button.connect('key-press-event',
|
||||
function() {
|
||||
icon.click(Clutter.get_current_event());
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
});
|
||||
button.connect('key-focus-in', Lang.bind(this,
|
||||
function() {
|
||||
this._concealHandle.show();
|
||||
}));
|
||||
|
||||
this._iconBox.add_actor(button);
|
||||
|
||||
if (!this._concealHandle.visible) {
|
||||
this._concealHandle.show();
|
||||
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, TEMP_REVEAL_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._concealHandle.hide();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onTrayIconRemoved: function(tm, icon) {
|
||||
if (!this.actor.contains(icon))
|
||||
return;
|
||||
|
||||
icon.get_parent().destroy();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_syncBarrier: function() {
|
||||
let rtl = (this._slider.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
let [x, y] = this._slider.get_transformed_position();
|
||||
let [w, h] = this._slider.get_transformed_size();
|
||||
|
||||
let x1 = Math.round(x);
|
||||
if (rtl)
|
||||
x1 += Math.round(w);
|
||||
|
||||
let x2 = x1;
|
||||
let y1 = Math.round(y);
|
||||
let y2 = y1 + Math.round(h);
|
||||
|
||||
if (this._horizontalBarrier &&
|
||||
this._horizontalBarrier.x1 == x1 &&
|
||||
this._horizontalBarrier.y1 == y1 &&
|
||||
this._horizontalBarrier.x2 == x2 &&
|
||||
this._horizontalBarrier.y2 == y2)
|
||||
return;
|
||||
|
||||
this._unsetBarrier();
|
||||
|
||||
let directions = (rtl ? Meta.BarrierDirection.NEGATIVE_X : Meta.BarrierDirection.POSITIVE_X);
|
||||
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
|
||||
x1: x1, x2: x2,
|
||||
y1: y1, y2: y2,
|
||||
directions: directions });
|
||||
this._pressureBarrier.addBarrier(this._horizontalBarrier);
|
||||
},
|
||||
|
||||
_unsetBarrier: function() {
|
||||
if (this._horizontalBarrier == null)
|
||||
return;
|
||||
|
||||
this._pressureBarrier.removeBarrier(this._horizontalBarrier);
|
||||
this._horizontalBarrier.destroy();
|
||||
this._horizontalBarrier = null;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
// FIXME: we no longer treat tray icons as notifications
|
||||
let allowed = Main.sessionMode.hasNotifications;
|
||||
let hasIcons = this._iconBox.get_n_children() > 0;
|
||||
let inOverview = Main.overview.visible && !Main.overview.animationInProgress;
|
||||
let inFullscreen = Main.layoutManager.primaryMonitor.inFullscreen;
|
||||
this.actor.visible = allowed && hasIcons && !inOverview && !inFullscreen;
|
||||
|
||||
if (!hasIcons)
|
||||
this._concealHandle.hide();
|
||||
|
||||
let targetSlide;
|
||||
if (this._concealHandle.visible) {
|
||||
targetSlide = 1.0;
|
||||
} else if (!hasIcons) {
|
||||
targetSlide = 0.0;
|
||||
} else {
|
||||
let [, boxWidth] = this._box.get_preferred_width(-1);
|
||||
let [, handleWidth] = this._revealHandle.get_preferred_width(-1);
|
||||
|
||||
if (this._revealHandle.hover)
|
||||
targetSlide = handleWidth / boxWidth;
|
||||
else
|
||||
targetSlide = CONCEALED_WIDTH / boxWidth;
|
||||
}
|
||||
|
||||
if (this.actor.visible) {
|
||||
Tweener.addTween(this._slideLayout,
|
||||
{ slideX: targetSlide,
|
||||
time: REVEAL_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
} else {
|
||||
this._slideLayout.slideX = targetSlide;
|
||||
this._unsetBarrier();
|
||||
}
|
||||
}
|
||||
});
|
@ -4,6 +4,7 @@ const Atspi = imports.gi.Atspi;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Lang = imports.lang;
|
||||
@ -22,6 +23,8 @@ const MOUSE_POLL_FREQUENCY = 50;
|
||||
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
||||
const NO_CHANGE = 0.0;
|
||||
|
||||
const POINTER_REST_TIME = 1000; // milliseconds
|
||||
|
||||
// Settings
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const SHOW_KEY = 'screen-magnifier-enabled';
|
||||
@ -444,55 +447,6 @@ const Magnifier = new Lang.Class({
|
||||
this._appSettings = new Gio.Settings({ schema_id: APPLICATIONS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: MAGNIFIER_SCHEMA });
|
||||
|
||||
if (zoomRegion) {
|
||||
// Mag factor is accurate to two decimal places.
|
||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||
if (aPref != 0.0)
|
||||
zoomRegion.setMagFactor(aPref, aPref);
|
||||
|
||||
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setScreenPosition(aPref);
|
||||
|
||||
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
|
||||
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
|
||||
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setFocusTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setCaretTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setInvertLightness(aPref);
|
||||
|
||||
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setColorSaturation(aPref);
|
||||
|
||||
let bc = {};
|
||||
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
zoomRegion.setBrightness(bc);
|
||||
|
||||
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
zoomRegion.setContrast(bc);
|
||||
}
|
||||
|
||||
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
||||
this.addCrosshairs();
|
||||
this.setCrosshairsVisible(showCrosshairs);
|
||||
|
||||
this._appSettings.connect('changed::' + SHOW_KEY,
|
||||
Lang.bind(this, function() {
|
||||
this.setActive(this._appSettings.get_boolean(SHOW_KEY));
|
||||
@ -561,6 +515,56 @@ const Magnifier = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
|
||||
}));
|
||||
|
||||
if (zoomRegion) {
|
||||
// Mag factor is accurate to two decimal places.
|
||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||
if (aPref != 0.0)
|
||||
zoomRegion.setMagFactor(aPref, aPref);
|
||||
|
||||
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setScreenPosition(aPref);
|
||||
|
||||
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
|
||||
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
|
||||
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setFocusTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setCaretTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setInvertLightness(aPref);
|
||||
|
||||
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setColorSaturation(aPref);
|
||||
|
||||
let bc = {};
|
||||
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
zoomRegion.setBrightness(bc);
|
||||
|
||||
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
zoomRegion.setContrast(bc);
|
||||
}
|
||||
|
||||
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
||||
this.addCrosshairs();
|
||||
this.setCrosshairsVisible(showCrosshairs);
|
||||
|
||||
return this._appSettings.get_boolean(SHOW_KEY);
|
||||
},
|
||||
|
||||
@ -708,6 +712,9 @@ const ZoomRegion = new Lang.Class({
|
||||
this._xCaret = 0;
|
||||
this._yCaret = 0;
|
||||
|
||||
this._pointerIdleMonitor = Meta.IdleMonitor.get_for_device(Meta.VIRTUAL_CORE_POINTER_ID);
|
||||
this._scrollContentsTimerId = 0;
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._focusCaretTracker.connect('caret-moved',
|
||||
@ -1067,6 +1074,26 @@ const ZoomRegion = new Lang.Class({
|
||||
return this._isMouseOverRegion();
|
||||
},
|
||||
|
||||
_clearScrollContentsTimer: function() {
|
||||
if (this._scrollContentsTimerId != 0) {
|
||||
Mainloop.source_remove(this._scrollContentsTimerId);
|
||||
this._scrollContentsTimerId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_scrollContentsToDelayed: function(x, y) {
|
||||
if (this._pointerIdleMonitor.get_idletime() >= POINTER_REST_TIME) {
|
||||
this.scrollContentsTo(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
this._clearScrollContentsTimer();
|
||||
this._scrollContentsTimerId = Mainloop.timeout_add(POINTER_REST_TIME, Lang.bind(this, function() {
|
||||
this._scrollContentsToDelayed(x, y);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* scrollContentsTo:
|
||||
* Shift the contents of the magnified view such it is centered on the given
|
||||
@ -1075,6 +1102,8 @@ const ZoomRegion = new Lang.Class({
|
||||
* @y: The y-coord of the point to center on.
|
||||
*/
|
||||
scrollContentsTo: function(x, y) {
|
||||
this._clearScrollContentsTimer();
|
||||
|
||||
this._followingCursor = false;
|
||||
this._changeROI({ xCenter: x,
|
||||
yCenter: y });
|
||||
@ -1380,7 +1409,7 @@ const ZoomRegion = new Lang.Class({
|
||||
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
|
||||
[xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
|
||||
|
||||
this.scrollContentsTo(xCaret, yCaret);
|
||||
this._scrollContentsToDelayed(xCaret, yCaret);
|
||||
},
|
||||
|
||||
_centerFromFocusPosition: function() {
|
||||
@ -1394,7 +1423,7 @@ const ZoomRegion = new Lang.Class({
|
||||
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
|
||||
[xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
|
||||
|
||||
this.scrollContentsTo(xFocus, yFocus);
|
||||
this._scrollContentsToDelayed(xFocus, yFocus);
|
||||
},
|
||||
|
||||
_centerFromPointPush: function(xPoint, yPoint) {
|
||||
|
@ -11,6 +11,8 @@ const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AccessDialog = imports.ui.accessDialog;
|
||||
const AudioDeviceSelection = imports.ui.audioDeviceSelection;
|
||||
const Components = imports.ui.components;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
@ -18,6 +20,7 @@ const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const Keyboard = imports.ui.keyboard;
|
||||
const LegacyTray = imports.ui.legacyTray;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const OsdWindow = imports.ui.osdWindow;
|
||||
@ -52,6 +55,7 @@ let overview = null;
|
||||
let runDialog = null;
|
||||
let lookingGlass = null;
|
||||
let wm = null;
|
||||
let legacyTray = null;
|
||||
let messageTray = null;
|
||||
let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
@ -60,6 +64,8 @@ let ctrlAltTabManager = null;
|
||||
let osdWindowManager = null;
|
||||
let osdMonitorLabeler = null;
|
||||
let sessionMode = null;
|
||||
let shellAccessDialogDBusService = null;
|
||||
let shellAudioSelectionDBusService = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
@ -76,6 +82,7 @@ let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _a11ySettings = null;
|
||||
let _themeResource = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
if (sessionMode.isPrimary)
|
||||
@ -117,6 +124,8 @@ function start() {
|
||||
_loadDefaultStylesheet);
|
||||
_initializeUI();
|
||||
|
||||
shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus();
|
||||
shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus();
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
|
||||
@ -135,9 +144,7 @@ function _initializeUI() {
|
||||
Shell.WindowTracker.get_default();
|
||||
Shell.AppUsage.get_default();
|
||||
|
||||
let resource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource');
|
||||
resource._register();
|
||||
|
||||
reloadThemeResource();
|
||||
_loadDefaultStylesheet();
|
||||
|
||||
// Setup the stage hierarchy early
|
||||
@ -159,8 +166,9 @@ function _initializeUI() {
|
||||
if (LoginManager.canLock())
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
|
||||
panel = new Panel.Panel();
|
||||
legacyTray = new LegacyTray.LegacyTray();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
panel = new Panel.Panel();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
@ -186,6 +194,8 @@ function _initializeUI() {
|
||||
return true;
|
||||
});
|
||||
|
||||
global.display.connect('gl-video-memory-purged', loadTheme);
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
@ -287,6 +297,14 @@ function setThemeStylesheet(cssStylesheet) {
|
||||
_cssStylesheet = cssStylesheet ? Gio.File.new_for_path(cssStylesheet) : null;
|
||||
}
|
||||
|
||||
function reloadThemeResource() {
|
||||
if (_themeResource)
|
||||
_themeResource._unregister();
|
||||
|
||||
_themeResource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource');
|
||||
_themeResource._register();
|
||||
}
|
||||
|
||||
/**
|
||||
* loadTheme:
|
||||
*
|
||||
@ -526,6 +544,7 @@ function activateWindow(window, time, workspaceNum) {
|
||||
}
|
||||
|
||||
overview.hide();
|
||||
panel.closeCalendar();
|
||||
}
|
||||
|
||||
// TODO - replace this timeout with some system to guess when the user might
|
||||
|
726
js/ui/messageList.js
Normal file
@ -0,0 +1,726 @@
|
||||
const Atk = imports.gi.Atk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MESSAGE_ANIMATION_TIME = 0.1;
|
||||
|
||||
const DEFAULT_EXPAND_LINES = 6;
|
||||
|
||||
function _fixMarkup(text, allowMarkup) {
|
||||
if (allowMarkup) {
|
||||
// Support &, ", ', < and >, escape all other
|
||||
// occurrences of '&'.
|
||||
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
||||
|
||||
// Support <b>, <i>, and <u>, escape anything else
|
||||
// so it displays as raw markup.
|
||||
_text = _text.replace(/<(?!\/?[biu]>)/g, '<');
|
||||
|
||||
try {
|
||||
Pango.parse_markup(_text, -1, '');
|
||||
return _text;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// !allowMarkup, or invalid markup
|
||||
return GLib.markup_escape_text(text, -1);
|
||||
}
|
||||
|
||||
const URLHighlighter = new Lang.Class({
|
||||
Name: 'URLHighlighter',
|
||||
|
||||
_init: function(text, lineWrap, allowMarkup) {
|
||||
if (!text)
|
||||
text = '';
|
||||
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter',
|
||||
x_expand: true, x_align: Clutter.ActorAlign.START });
|
||||
this._linkColor = '#ccccff';
|
||||
this.actor.connect('style-changed', Lang.bind(this, function() {
|
||||
let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false);
|
||||
if (hasColor) {
|
||||
let linkColor = color.to_string().substr(0, 7);
|
||||
if (linkColor != this._linkColor) {
|
||||
this._linkColor = linkColor;
|
||||
this._highlightUrls();
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.actor.clutter_text.line_wrap = lineWrap;
|
||||
this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
|
||||
this.setMarkup(text, allowMarkup);
|
||||
this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||
// Don't try to URL highlight when invisible.
|
||||
// The MessageTray doesn't actually hide us, so
|
||||
// we need to check for paint opacities as well.
|
||||
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
// Keep Notification.actor from seeing this and taking
|
||||
// a pointer grab, which would block our button-release-event
|
||||
// handler, if an URL is clicked
|
||||
return this._findUrlAtPos(event) != -1;
|
||||
}));
|
||||
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
|
||||
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let urlId = this._findUrlAtPos(event);
|
||||
if (urlId != -1) {
|
||||
let url = this._urls[urlId].url;
|
||||
if (url.indexOf(':') == -1)
|
||||
url = 'http://' + url;
|
||||
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
|
||||
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let urlId = this._findUrlAtPos(event);
|
||||
if (urlId != -1 && !this._cursorChanged) {
|
||||
global.screen.set_cursor(Meta.Cursor.POINTING_HAND);
|
||||
this._cursorChanged = true;
|
||||
} else if (urlId == -1) {
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this._cursorChanged = false;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
this.actor.connect('leave-event', Lang.bind(this, function() {
|
||||
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._cursorChanged) {
|
||||
this._cursorChanged = false;
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
},
|
||||
|
||||
setMarkup: function(text, allowMarkup) {
|
||||
text = text ? _fixMarkup(text, allowMarkup) : '';
|
||||
this._text = text;
|
||||
|
||||
this.actor.clutter_text.set_markup(text);
|
||||
/* clutter_text.text contain text without markup */
|
||||
this._urls = Util.findUrls(this.actor.clutter_text.text);
|
||||
this._highlightUrls();
|
||||
},
|
||||
|
||||
_highlightUrls: function() {
|
||||
// text here contain markup
|
||||
let urls = Util.findUrls(this._text);
|
||||
let markup = '';
|
||||
let pos = 0;
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
let url = urls[i];
|
||||
let str = this._text.substr(pos, url.pos - pos);
|
||||
markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>';
|
||||
pos = url.pos + url.url.length;
|
||||
}
|
||||
markup += this._text.substr(pos);
|
||||
this.actor.clutter_text.set_markup(markup);
|
||||
},
|
||||
|
||||
_findUrlAtPos: function(event) {
|
||||
let success;
|
||||
let [x, y] = event.get_coords();
|
||||
[success, x, y] = this.actor.transform_stage_point(x, y);
|
||||
let find_pos = -1;
|
||||
for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
|
||||
let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i);
|
||||
if (py > y || py + line_height < y || x < px)
|
||||
continue;
|
||||
find_pos = i;
|
||||
}
|
||||
if (find_pos != -1) {
|
||||
for (let i = 0; i < this._urls.length; i++)
|
||||
if (find_pos >= this._urls[i].pos &&
|
||||
this._urls[i].pos + this._urls[i].url.length > find_pos)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
|
||||
const ScaleLayout = new Lang.Class({
|
||||
Name: 'ScaleLayout',
|
||||
Extends: Clutter.BinLayout,
|
||||
|
||||
_connectContainer: function(container) {
|
||||
if (this._container == container)
|
||||
return;
|
||||
|
||||
if (this._container)
|
||||
for (let id of this._signals)
|
||||
this._container.disconnect(id);
|
||||
|
||||
this._container = container;
|
||||
this._signals = [];
|
||||
|
||||
if (this._container)
|
||||
for (let signal of ['notify::scale-x', 'notify::scale-y']) {
|
||||
let id = this._container.connect(signal, Lang.bind(this,
|
||||
function() {
|
||||
this.layout_changed();
|
||||
}));
|
||||
this._signals.push(id);
|
||||
}
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
this._connectContainer(container);
|
||||
|
||||
let [min, nat] = this.parent(container, forHeight);
|
||||
return [Math.floor(min * container.scale_x),
|
||||
Math.floor(nat * container.scale_x)];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
this._connectContainer(container);
|
||||
|
||||
let [min, nat] = this.parent(container, forWidth);
|
||||
return [Math.floor(min * container.scale_y),
|
||||
Math.floor(nat * container.scale_y)];
|
||||
}
|
||||
});
|
||||
|
||||
const LabelExpanderLayout = new Lang.Class({
|
||||
Name: 'LabelExpanderLayout',
|
||||
Extends: Clutter.LayoutManager,
|
||||
Properties: { 'expansion': GObject.ParamSpec.double('expansion',
|
||||
'Expansion',
|
||||
'Expansion of the layout, between 0 (collapsed) ' +
|
||||
'and 1 (fully expanded',
|
||||
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
0, 1, 0)},
|
||||
|
||||
_init: function(params) {
|
||||
this._expansion = 0;
|
||||
this._expandLines = DEFAULT_EXPAND_LINES;
|
||||
|
||||
this.parent(params);
|
||||
},
|
||||
|
||||
get expansion() {
|
||||
return this._expansion;
|
||||
},
|
||||
|
||||
set expansion(v) {
|
||||
if (v == this._expansion)
|
||||
return;
|
||||
this._expansion = v;
|
||||
this.notify('expansion');
|
||||
|
||||
let visibleIndex = this._expansion > 0 ? 1 : 0;
|
||||
for (let i = 0; this._container && i < this._container.get_n_children(); i++)
|
||||
this._container.get_child_at_index(i).visible = (i == visibleIndex);
|
||||
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
set expandLines(v) {
|
||||
if (v == this._expandLines)
|
||||
return;
|
||||
this._expandLines = v;
|
||||
if (this._expansion > 0)
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
vfunc_set_container: function(container) {
|
||||
this._container = container;
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
let [min, nat] = [0, 0];
|
||||
|
||||
for (let i = 0; i < container.get_n_children(); i++) {
|
||||
if (i > 1)
|
||||
break; // we support one unexpanded + one expanded child
|
||||
|
||||
let child = container.get_child_at_index(i);
|
||||
let [childMin, childNat] = child.get_preferred_width(forHeight);
|
||||
[min, nat] = [Math.max(min, childMin), Math.max(nat, childNat)];
|
||||
}
|
||||
|
||||
return [min, nat];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
let [min, nat] = [0, 0];
|
||||
|
||||
let children = container.get_children();
|
||||
if (children[0])
|
||||
[min, nat] = children[0].get_preferred_height(forWidth);
|
||||
|
||||
if (children[1]) {
|
||||
let [min2, nat2] = children[1].get_preferred_height(forWidth);
|
||||
let [expMin, expNat] = [Math.min(min2, min * this._expandLines),
|
||||
Math.min(nat2, nat * this._expandLines)];
|
||||
[min, nat] = [min + this._expansion * (expMin - min),
|
||||
nat + this._expansion * (expNat - nat)];
|
||||
}
|
||||
|
||||
return [min, nat];
|
||||
},
|
||||
|
||||
vfunc_allocate: function(container, box, flags) {
|
||||
for (let i = 0; i < container.get_n_children(); i++) {
|
||||
let child = container.get_child_at_index(i);
|
||||
|
||||
if (child.visible)
|
||||
child.allocate(box, flags);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const Message = new Lang.Class({
|
||||
Name: 'Message',
|
||||
|
||||
_init: function(title, body) {
|
||||
this.expanded = false;
|
||||
|
||||
this.actor = new St.Button({ style_class: 'message',
|
||||
accessible_role: Atk.Role.NOTIFICATION,
|
||||
can_focus: true,
|
||||
x_expand: true, x_fill: true });
|
||||
this.actor.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPressed));
|
||||
|
||||
let vbox = new St.BoxLayout({ vertical: true });
|
||||
this.actor.set_child(vbox);
|
||||
|
||||
let hbox = new St.BoxLayout();
|
||||
vbox.add_actor(hbox);
|
||||
|
||||
this._actionBin = new St.Widget({ layout_manager: new ScaleLayout(),
|
||||
visible: false });
|
||||
vbox.add_actor(this._actionBin);
|
||||
|
||||
this._iconBin = new St.Bin({ style_class: 'message-icon-bin',
|
||||
y_expand: true,
|
||||
visible: false });
|
||||
hbox.add_actor(this._iconBin);
|
||||
|
||||
let contentBox = new St.BoxLayout({ style_class: 'message-content',
|
||||
vertical: true, x_expand: true });
|
||||
hbox.add_actor(contentBox);
|
||||
|
||||
this._mediaControls = new St.BoxLayout();
|
||||
hbox.add_actor(this._mediaControls);
|
||||
|
||||
let titleBox = new St.BoxLayout();
|
||||
contentBox.add_actor(titleBox);
|
||||
|
||||
this.titleLabel = new St.Label({ style_class: 'message-title',
|
||||
x_expand: true,
|
||||
x_align: Clutter.ActorAlign.START });
|
||||
this.setTitle(title);
|
||||
titleBox.add_actor(this.titleLabel);
|
||||
|
||||
this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin' });
|
||||
titleBox.add_actor(this._secondaryBin);
|
||||
|
||||
let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic',
|
||||
icon_size: 16 });
|
||||
this._closeButton = new St.Button({ child: closeIcon, visible: false });
|
||||
titleBox.add_actor(this._closeButton);
|
||||
|
||||
this._bodyStack = new St.Widget({ x_expand: true });
|
||||
this._bodyStack.layout_manager = new LabelExpanderLayout();
|
||||
contentBox.add_actor(this._bodyStack);
|
||||
|
||||
this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup);
|
||||
this.bodyLabel.actor.add_style_class_name('message-body');
|
||||
this._bodyStack.add_actor(this.bodyLabel.actor);
|
||||
this.setBody(body);
|
||||
|
||||
this._closeButton.connect('clicked', Lang.bind(this, this.close));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._sync));
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this._sync();
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.emit('close');
|
||||
},
|
||||
|
||||
setIcon: function(actor) {
|
||||
this._iconBin.child = actor;
|
||||
this._iconBin.visible = (actor != null);
|
||||
},
|
||||
|
||||
setSecondaryActor: function(actor) {
|
||||
this._secondaryBin.child = actor;
|
||||
},
|
||||
|
||||
setTitle: function(text) {
|
||||
let title = text ? _fixMarkup(text.replace(/\n/g, ' '), false) : '';
|
||||
this.titleLabel.clutter_text.set_markup(title);
|
||||
},
|
||||
|
||||
setBody: function(text) {
|
||||
this._bodyText = text;
|
||||
this.bodyLabel.setMarkup(text ? text.replace(/\n/g, ' ') : '',
|
||||
this._useBodyMarkup);
|
||||
if (this._expandedLabel)
|
||||
this._expandedLabel.setMarkup(text, this._useBodyMarkup);
|
||||
},
|
||||
|
||||
setUseBodyMarkup: function(enable) {
|
||||
if (this._useBodyMarkup === enable)
|
||||
return;
|
||||
this._useBodyMarkup = enable;
|
||||
if (this.bodyLabel)
|
||||
this.setBody(this._bodyText);
|
||||
},
|
||||
|
||||
setActionArea: function(actor) {
|
||||
if (actor == null) {
|
||||
if (this._actionBin.get_n_children() > 0)
|
||||
this._actionBin.get_child_at_index(0).destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._actionBin.get_n_children() > 0)
|
||||
throw new Error('Message already has an action area');
|
||||
|
||||
this._actionBin.add_actor(actor);
|
||||
this._actionBin.visible = this.expanded;
|
||||
},
|
||||
|
||||
addMediaControl: function(iconName, callback) {
|
||||
let icon = new St.Icon({ icon_name: iconName, icon_size: 16 });
|
||||
let button = new St.Button({ style_class: 'message-media-control',
|
||||
child: icon });
|
||||
button.connect('clicked', callback);
|
||||
this._mediaControls.add_actor(button);
|
||||
return button;
|
||||
},
|
||||
|
||||
setExpandedBody: function(actor) {
|
||||
if (actor == null) {
|
||||
if (this._bodyStack.get_n_children() > 1)
|
||||
this._bodyStack.get_child_at_index(1).destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._bodyStack.get_n_children() > 1)
|
||||
throw new Error('Message already has an expanded body actor');
|
||||
|
||||
this._bodyStack.insert_child_at_index(actor, 1);
|
||||
},
|
||||
|
||||
setExpandedLines: function(nLines) {
|
||||
this._bodyStack.layout_manager.expandLines = nLines;
|
||||
},
|
||||
|
||||
expand: function(animate) {
|
||||
this.expanded = true;
|
||||
|
||||
this._actionBin.visible = (this._actionBin.get_n_children() > 0);
|
||||
|
||||
if (this._bodyStack.get_n_children() < 2) {
|
||||
this._expandedLabel = new URLHighlighter(this._bodyText,
|
||||
true, this._useBodyMarkup);
|
||||
this.setExpandedBody(this._expandedLabel.actor);
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
Tweener.addTween(this._bodyStack.layout_manager,
|
||||
{ expansion: 1,
|
||||
time: MessageTray.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
this._actionBin.scale_y = 0;
|
||||
Tweener.addTween(this._actionBin,
|
||||
{ scale_y: 1,
|
||||
time: MessageTray.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
} else {
|
||||
this._bodyStack.layout_manager.expansion = 1;
|
||||
this._actionBin.scale_y = 1;
|
||||
}
|
||||
|
||||
this.emit('expanded');
|
||||
},
|
||||
|
||||
unexpand: function(animate) {
|
||||
if (animate) {
|
||||
Tweener.addTween(this._bodyStack.layout_manager,
|
||||
{ expansion: 0,
|
||||
time: MessageTray.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
Tweener.addTween(this._actionBin,
|
||||
{ scale_y: 0,
|
||||
time: MessageTray.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
this._actionBin.hide();
|
||||
this.expanded = false;
|
||||
}});
|
||||
} else {
|
||||
this._bodyStack.layout_manager.expansion = 0;
|
||||
this._actionBin.scale_y = 0;
|
||||
this.expanded = false;
|
||||
}
|
||||
|
||||
this.emit('unexpanded');
|
||||
},
|
||||
|
||||
canClose: function() {
|
||||
return this._mediaControls.get_n_children() == 0;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let hovered = this.actor.hover;
|
||||
this._closeButton.visible = hovered && this.canClose();
|
||||
this._secondaryBin.visible = !hovered;
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
},
|
||||
|
||||
_onKeyPressed: function(a, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
|
||||
if (keysym == Clutter.KEY_Delete ||
|
||||
keysym == Clutter.KEY_KP_Delete) {
|
||||
this.close();
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Message.prototype);
|
||||
|
||||
const MessageListSection = new Lang.Class({
|
||||
Name: 'MessageListSection',
|
||||
|
||||
_init: function(title) {
|
||||
this.actor = new St.BoxLayout({ style_class: 'message-list-section',
|
||||
clip_to_allocation: true,
|
||||
x_expand: true, vertical: true });
|
||||
let titleBox = new St.BoxLayout({ style_class: 'message-list-section-title-box' });
|
||||
this.actor.add_actor(titleBox);
|
||||
|
||||
this._title = new St.Button({ style_class: 'message-list-section-title',
|
||||
label: title,
|
||||
can_focus: true,
|
||||
x_expand: true,
|
||||
x_align: St.Align.START });
|
||||
titleBox.add_actor(this._title);
|
||||
|
||||
this._title.connect('clicked', Lang.bind(this, this._onTitleClicked));
|
||||
this._title.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
|
||||
let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic' });
|
||||
this._closeButton = new St.Button({ style_class: 'message-list-section-close',
|
||||
child: closeIcon,
|
||||
accessible_name: _("Clear section"),
|
||||
can_focus: true });
|
||||
this._closeButton.set_x_align(Clutter.ActorAlign.END);
|
||||
titleBox.add_actor(this._closeButton);
|
||||
|
||||
this._closeButton.connect('clicked', Lang.bind(this, this.clear));
|
||||
|
||||
this._list = new St.BoxLayout({ style_class: 'message-list-section-list',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._list);
|
||||
|
||||
this._list.connect('actor-added', Lang.bind(this, this._sync));
|
||||
this._list.connect('actor-removed', Lang.bind(this, this._sync));
|
||||
|
||||
let id = Main.sessionMode.connect('updated',
|
||||
Lang.bind(this, this._sync));
|
||||
this.actor.connect('destroy', function() {
|
||||
Main.sessionMode.disconnect(id);
|
||||
});
|
||||
|
||||
this._messages = new Map();
|
||||
this._date = new Date();
|
||||
this.empty = true;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_onTitleClicked: function() {
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
_onKeyFocusIn: function(actor) {
|
||||
this.emit('key-focus-in', actor);
|
||||
},
|
||||
|
||||
get allowed() {
|
||||
return true;
|
||||
},
|
||||
|
||||
setDate: function(date) {
|
||||
if (Calendar.sameDay(date, this._date))
|
||||
return;
|
||||
this._date = date;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
addMessage: function(message, animate) {
|
||||
this.addMessageAtIndex(message, -1, animate);
|
||||
},
|
||||
|
||||
addMessageAtIndex: function(message, index, animate) {
|
||||
let obj = {
|
||||
container: null,
|
||||
destroyId: 0,
|
||||
keyFocusId: 0,
|
||||
closeId: 0
|
||||
};
|
||||
let pivot = new Clutter.Point({ x: .5, y: .5 });
|
||||
let scale = animate ? 0 : 1;
|
||||
obj.container = new St.Widget({ layout_manager: new ScaleLayout(),
|
||||
pivot_point: pivot,
|
||||
scale_x: scale, scale_y: scale });
|
||||
obj.keyFocusId = message.actor.connect('key-focus-in',
|
||||
Lang.bind(this, this._onKeyFocusIn));
|
||||
obj.destroyId = message.actor.connect('destroy',
|
||||
Lang.bind(this, function() {
|
||||
this.removeMessage(message, false);
|
||||
}));
|
||||
obj.closeId = message.connect('close',
|
||||
Lang.bind(this, function() {
|
||||
this.removeMessage(message, true);
|
||||
}));
|
||||
|
||||
this._messages.set(message, obj);
|
||||
obj.container.add_actor(message.actor);
|
||||
|
||||
this._list.insert_child_at_index(obj.container, index);
|
||||
|
||||
if (animate)
|
||||
Tweener.addTween(obj.container, { scale_x: 1,
|
||||
scale_y: 1,
|
||||
time: MESSAGE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
moveMessage: function(message, index, animate) {
|
||||
let obj = this._messages.get(message);
|
||||
|
||||
if (!animate) {
|
||||
this._list.set_child_at_index(obj.container, index);
|
||||
return;
|
||||
}
|
||||
|
||||
let onComplete = Lang.bind(this, function() {
|
||||
this._list.set_child_at_index(obj.container, index);
|
||||
Tweener.addTween(obj.container, { scale_x: 1,
|
||||
scale_y: 1,
|
||||
time: MESSAGE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
});
|
||||
Tweener.addTween(obj.container, { scale_x: 0,
|
||||
scale_y: 0,
|
||||
time: MESSAGE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: onComplete });
|
||||
},
|
||||
|
||||
removeMessage: function(message, animate) {
|
||||
let obj = this._messages.get(message);
|
||||
|
||||
message.actor.disconnect(obj.destroyId);
|
||||
message.actor.disconnect(obj.keyFocusId);
|
||||
message.disconnect(obj.closeId);
|
||||
|
||||
this._messages.delete(message);
|
||||
|
||||
if (animate) {
|
||||
Tweener.addTween(obj.container, { scale_x: 0, scale_y: 0,
|
||||
time: MESSAGE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
obj.container.destroy();
|
||||
global.sync_pointer();
|
||||
}});
|
||||
} else {
|
||||
obj.container.destroy();
|
||||
global.sync_pointer();
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
let messages = [...this._messages.keys()].filter(function(message) {
|
||||
return message.canClose();
|
||||
});
|
||||
|
||||
// If there are few messages, letting them all zoom out looks OK
|
||||
if (messages.length < 2) {
|
||||
messages.forEach(function(message) {
|
||||
message.close();
|
||||
});
|
||||
} else {
|
||||
// Otherwise we slide them out one by one, and then zoom them
|
||||
// out "off-screen" in the end to smoothly shrink the parent
|
||||
let delay = MESSAGE_ANIMATION_TIME / Math.max(messages.length, 5);
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
let message = messages[i];
|
||||
let obj = this._messages.get(message);
|
||||
Tweener.addTween(obj.container,
|
||||
{ anchor_x: this._list.width,
|
||||
opacity: 0,
|
||||
time: MESSAGE_ANIMATION_TIME,
|
||||
delay: i * delay,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
message.close();
|
||||
}});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_canClear: function() {
|
||||
for (let message of this._messages.keys())
|
||||
if (message.canClose())
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
_shouldShow: function() {
|
||||
return !this.empty;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let empty = this._list.get_n_children() == 0;
|
||||
let changed = this.empty !== empty;
|
||||
this.empty = empty;
|
||||
|
||||
if (changed)
|
||||
this.emit('empty-changed');
|
||||
|
||||
this._closeButton.visible = this._canClear();
|
||||
this.actor.visible = this.allowed && this._shouldShow();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(MessageListSection.prototype);
|
2103
js/ui/messageTray.js
@ -14,7 +14,6 @@ const Atk = imports.gi.Atk;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const Layout = imports.ui.layout;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
@ -23,10 +22,6 @@ const Tweener = imports.ui.tweener;
|
||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||
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 = {
|
||||
OPENED: 0,
|
||||
CLOSED: 1,
|
||||
@ -79,7 +74,9 @@ const ModalDialog = new Lang.Class({
|
||||
this._group.add_actor(this._backgroundBin);
|
||||
|
||||
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
vertical: true });
|
||||
// modal dialogs are fixed width and grow vertically; set the request
|
||||
// mode accordingly so wrapped labels are handled correctly during
|
||||
// size requests.
|
||||
@ -100,7 +97,8 @@ const ModalDialog = new Lang.Class({
|
||||
this.backgroundStack.add_actor(this.dialogLayout);
|
||||
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true,
|
||||
style_class: "modal-dialog-content-box" });
|
||||
this.dialogLayout.add(this.contentLayout,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
@ -108,8 +106,7 @@ const ModalDialog = new Lang.Class({
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
vertical: false });
|
||||
this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout ({ homogeneous:true }) });
|
||||
this.dialogLayout.add(this.buttonLayout,
|
||||
{ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
@ -118,8 +115,6 @@ const ModalDialog = new Lang.Class({
|
||||
this._initialKeyFocus = this.dialogLayout;
|
||||
this._initialKeyFocusDestroyId = 0;
|
||||
this._savedKeyFocus = null;
|
||||
|
||||
this._workSpinner = null;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -147,16 +142,12 @@ const ModalDialog = new Lang.Class({
|
||||
else
|
||||
x_alignment = St.Align.MIDDLE;
|
||||
|
||||
this.addButton(buttonInfo, { expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: x_alignment,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.addButton(buttonInfo);
|
||||
}
|
||||
},
|
||||
|
||||
addButton: function(buttonInfo, layoutInfo) {
|
||||
let label = buttonInfo['label'];
|
||||
addButton: function(buttonInfo) {
|
||||
let label = buttonInfo['label']
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
let isDefault = buttonInfo['default'];
|
||||
@ -170,10 +161,12 @@ const ModalDialog = new Lang.Class({
|
||||
else
|
||||
keys = [];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button button',
|
||||
let button = new St.Button({ style_class: 'modal-dialog-linked-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
label: label });
|
||||
button.connect('clicked', action);
|
||||
|
||||
@ -188,47 +181,11 @@ const ModalDialog = new Lang.Class({
|
||||
for (let i in keys)
|
||||
this._buttonKeys[keys[i]] = buttonInfo;
|
||||
|
||||
this.buttonLayout.add(button, layoutInfo);
|
||||
this.buttonLayout.add_actor(button);
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
placeSpinner: function(layoutInfo) {
|
||||
let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
|
||||
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||
this._workSpinner.actor.opacity = 0;
|
||||
this._workSpinner.actor.show();
|
||||
|
||||
this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
|
||||
},
|
||||
|
||||
setWorking: function(working) {
|
||||
if (!this._workSpinner)
|
||||
return;
|
||||
|
||||
Tweener.removeTweens(this._workSpinner.actor);
|
||||
if (working) {
|
||||
this._workSpinner.play();
|
||||
Tweener.addTween(this._workSpinner.actor,
|
||||
{ opacity: 255,
|
||||
delay: WORK_SPINNER_ANIMATION_DELAY,
|
||||
time: WORK_SPINNER_ANIMATION_TIME,
|
||||
transition: 'linear'
|
||||
});
|
||||
} else {
|
||||
Tweener.addTween(this._workSpinner.actor,
|
||||
{ opacity: 0,
|
||||
time: WORK_SPINNER_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
if (this._workSpinner)
|
||||
this._workSpinner.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, event) {
|
||||
this._pressedKey = event.get_key_symbol();
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
@ -302,7 +259,7 @@ const ModalDialog = new Lang.Class({
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
return true;
|
||||
|
||||
if (!this.pushModal({ timestamp: timestamp }))
|
||||
if (!this.pushModal(timestamp))
|
||||
return false;
|
||||
|
||||
this._fadeOpen(onPrimary);
|
||||
@ -361,8 +318,11 @@ const ModalDialog = new Lang.Class({
|
||||
pushModal: function (timestamp) {
|
||||
if (this._hasModal)
|
||||
return true;
|
||||
if (!Main.pushModal(this._group, { timestamp: timestamp,
|
||||
actionMode: this._actionMode }))
|
||||
|
||||
let params = { actionMode: this._actionMode };
|
||||
if (timestamp)
|
||||
params['timestamp'] = timestamp;
|
||||
if (!Main.pushModal(this._group, params))
|
||||
return false;
|
||||
|
||||
this._hasModal = true;
|
||||
|
270
js/ui/mpris.js
Normal file
@ -0,0 +1,270 @@
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Main = imports.ui.main;
|
||||
const MessageList = imports.ui.messageList;
|
||||
|
||||
const DBusIface = '<node> \
|
||||
<interface name="org.freedesktop.DBus"> \
|
||||
<method name="ListNames"> \
|
||||
<arg type="as" direction="out" name="names" /> \
|
||||
</method> \
|
||||
<signal name="NameOwnerChanged"> \
|
||||
<arg type="s" direction="out" name="name" /> \
|
||||
<arg type="s" direction="out" name="oldOwner" /> \
|
||||
<arg type="s" direction="out" name="newOwner" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const DBusProxy = Gio.DBusProxy.makeProxyWrapper(DBusIface);
|
||||
|
||||
const MprisIface = '<node> \
|
||||
<interface name="org.mpris.MediaPlayer2"> \
|
||||
<method name="Raise" /> \
|
||||
<property name="CanRaise" type="b" access="read" /> \
|
||||
<property name="DesktopEntry" type="s" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const MprisProxy = Gio.DBusProxy.makeProxyWrapper(MprisIface);
|
||||
|
||||
const MprisPlayerIface = '<node> \
|
||||
<interface name="org.mpris.MediaPlayer2.Player"> \
|
||||
<method name="PlayPause" /> \
|
||||
<method name="Next" /> \
|
||||
<method name="Previous" /> \
|
||||
<property name="CanPlay" type="b" access="read" /> \
|
||||
<property name="Metadata" type="a{sv}" access="read" /> \
|
||||
<property name="PlaybackStatus" type="s" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
|
||||
|
||||
const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.';
|
||||
|
||||
const MediaMessage = new Lang.Class({
|
||||
Name: 'MediaMessage',
|
||||
Extends: MessageList.Message,
|
||||
|
||||
_init: function(player) {
|
||||
this._player = player;
|
||||
|
||||
this.parent('', '');
|
||||
|
||||
this._icon = new St.Icon({ style_class: 'media-message-cover-icon' });
|
||||
this.setIcon(this._icon);
|
||||
|
||||
this.addMediaControl('media-skip-backward-symbolic',
|
||||
Lang.bind(this, function() {
|
||||
this._player.previous();
|
||||
}));
|
||||
|
||||
this._playPauseButton = this.addMediaControl(null,
|
||||
Lang.bind(this, function() {
|
||||
this._player.playPause();
|
||||
}));
|
||||
|
||||
this.addMediaControl('media-skip-forward-symbolic',
|
||||
Lang.bind(this, function() {
|
||||
this._player.next();
|
||||
}));
|
||||
|
||||
this._player.connect('changed', Lang.bind(this, this._update));
|
||||
this._player.connect('closed', Lang.bind(this, this.close));
|
||||
this._update();
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this._player.raise();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this.setTitle(this._player.trackArtists.join(', '));
|
||||
this.setBody(this._player.trackTitle);
|
||||
|
||||
if (this._player.trackCoverUrl) {
|
||||
let file = Gio.File.new_for_uri(this._player.trackCoverUrl);
|
||||
this._icon.gicon = new Gio.FileIcon({ file: file });
|
||||
this._icon.remove_style_class_name('fallback');
|
||||
} else {
|
||||
this._icon.icon_name = 'audio-x-generic-symbolic';
|
||||
this._icon.add_style_class_name('fallback');
|
||||
}
|
||||
|
||||
let isPlaying = this._player.status == 'Playing';
|
||||
let iconName = isPlaying ? 'media-playback-pause-symbolic'
|
||||
: 'media-playback-start-symbolic';
|
||||
this._playPauseButton.child.icon_name = iconName;
|
||||
}
|
||||
});
|
||||
|
||||
const MprisPlayer = new Lang.Class({
|
||||
Name: 'MprisPlayer',
|
||||
|
||||
_init: function(busName) {
|
||||
this._mprisProxy = new MprisProxy(Gio.DBus.session, busName,
|
||||
'/org/mpris/MediaPlayer2',
|
||||
Lang.bind(this, this._onMprisProxyReady));
|
||||
this._playerProxy = new MprisPlayerProxy(Gio.DBus.session, busName,
|
||||
'/org/mpris/MediaPlayer2',
|
||||
Lang.bind(this, this._onPlayerProxyReady));
|
||||
|
||||
this._visible = false;
|
||||
this._trackArtists = [];
|
||||
this._trackTitle = '';
|
||||
this._trackCoverUrl = '';
|
||||
},
|
||||
|
||||
get status() {
|
||||
return this._playerProxy.PlaybackStatus;
|
||||
},
|
||||
|
||||
get trackArtists() {
|
||||
return this._trackArtists;
|
||||
},
|
||||
|
||||
get trackTitle() {
|
||||
return this._trackTitle;
|
||||
},
|
||||
|
||||
get trackCoverUrl() {
|
||||
return this._trackCoverUrl;
|
||||
},
|
||||
|
||||
playPause: function() {
|
||||
this._playerProxy.PlayPauseRemote();
|
||||
},
|
||||
|
||||
next: function() {
|
||||
this._playerProxy.NextRemote();
|
||||
},
|
||||
|
||||
previous: function() {
|
||||
this._playerProxy.PreviousRemote();
|
||||
},
|
||||
|
||||
raise: function() {
|
||||
// The remote Raise() method may run into focus stealing prevention,
|
||||
// so prefer activating the app via .desktop file if possible
|
||||
let app = null;
|
||||
if (this._mprisProxy.DesktopEntry) {
|
||||
let desktopId = this._mprisProxy.DesktopEntry + '.desktop';
|
||||
app = Shell.AppSystem.get_default().lookup_app(desktopId);
|
||||
}
|
||||
|
||||
if (app)
|
||||
app.activate();
|
||||
else if (this._mprisProxy.CanRaise)
|
||||
this._mprisProxy.RaiseRemote();
|
||||
},
|
||||
|
||||
_close: function() {
|
||||
this._mprisProxy.disconnect(this._ownerNotifyId);
|
||||
this._mprisProxy = null;
|
||||
|
||||
this._playerProxy.disconnect(this._propsChangedId);
|
||||
this._playerProxy = null;
|
||||
|
||||
this.emit('closed');
|
||||
},
|
||||
|
||||
_onMprisProxyReady: function() {
|
||||
this._ownerNotifyId = this._mprisProxy.connect('notify::g-name-owner',
|
||||
Lang.bind(this, function() {
|
||||
if (!this._mprisProxy.g_name_owner)
|
||||
this._close();
|
||||
}));
|
||||
},
|
||||
|
||||
_onPlayerProxyReady: function() {
|
||||
this._propsChangedId = this._playerProxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._updateState));
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_updateState: function() {
|
||||
let metadata = {};
|
||||
for (let prop in this._playerProxy.Metadata)
|
||||
metadata[prop] = this._playerProxy.Metadata[prop].deep_unpack();
|
||||
|
||||
this._trackArtists = metadata['xesam:artist'] || [_("Unknown artist")];
|
||||
this._trackTitle = metadata['xesam:title'] || _("Unknown title");
|
||||
this._trackCoverUrl = metadata['mpris:artUrl'] || '';
|
||||
this.emit('changed');
|
||||
|
||||
let visible = this._playerProxy.CanPlay;
|
||||
|
||||
if (this._visible != visible) {
|
||||
this._visible = visible;
|
||||
if (visible)
|
||||
this.emit('show');
|
||||
else
|
||||
this._close();
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(MprisPlayer.prototype);
|
||||
|
||||
const MediaSection = new Lang.Class({
|
||||
Name: 'MediaSection',
|
||||
Extends: MessageList.MessageListSection,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Media"));
|
||||
|
||||
this._players = new Map();
|
||||
|
||||
this._proxy = new DBusProxy(Gio.DBus.session,
|
||||
'org.freedesktop.DBus',
|
||||
'/org/freedesktop/DBus',
|
||||
Lang.bind(this, this._onProxyReady));
|
||||
},
|
||||
|
||||
_shouldShow: function() {
|
||||
return !this.empty && Calendar.isToday(this._date);
|
||||
},
|
||||
|
||||
_addPlayer: function(busName) {
|
||||
if (this._players.get(busName))
|
||||
return;
|
||||
|
||||
let player = new MprisPlayer(busName);
|
||||
player.connect('closed', Lang.bind(this,
|
||||
function() {
|
||||
this._players.delete(busName);
|
||||
}));
|
||||
player.connect('show', Lang.bind(this,
|
||||
function() {
|
||||
let message = new MediaMessage(player);
|
||||
this.addMessage(message, true);
|
||||
}));
|
||||
this._players.set(busName, player);
|
||||
},
|
||||
|
||||
_onProxyReady: function() {
|
||||
this._proxy.ListNamesRemote(Lang.bind(this,
|
||||
function([names]) {
|
||||
names.forEach(Lang.bind(this,
|
||||
function(name) {
|
||||
if (!name.startsWith(MPRIS_PLAYER_PREFIX))
|
||||
return;
|
||||
|
||||
this._addPlayer(name);
|
||||
}));
|
||||
}));
|
||||
this._proxy.connectSignal('NameOwnerChanged',
|
||||
Lang.bind(this, this._onNameOwnerChanged));
|
||||
},
|
||||
|
||||
_onNameOwnerChanged: function(proxy, sender, [name, oldOwner, newOwner]) {
|
||||
if (!name.startsWith(MPRIS_PLAYER_PREFIX))
|
||||
return;
|
||||
|
||||
if (newOwner && !oldOwner)
|
||||
this._addPlayer(name);
|
||||
}
|
||||
});
|
@ -212,13 +212,10 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
|
||||
// Filter out chat, presence, calls and invitation notifications from
|
||||
// Empathy, since we handle that information from telepathyClient.js
|
||||
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
||||
hints['category'] == 'x-empathy.im.room-invitation' ||
|
||||
hints['category'] == 'x-empathy.call.incoming' ||
|
||||
hints['category'] == 'x-empathy.transfer.incoming' ||
|
||||
hints['category'] == 'x-empathy.im.subscription-request' ||
|
||||
hints['category'] == 'presence.online' ||
|
||||
hints['category'] == 'presence.offline')) {
|
||||
//
|
||||
// Note that empathy uses im.received for one to one chats and
|
||||
// x-empathy.im.mentioned for multi-user, so we're good here
|
||||
if (appName == 'Empathy' && hints['category'] == 'im.received') {
|
||||
// Ignore replacesId since we already sent back a
|
||||
// NotificationClosed for that id.
|
||||
id = this._nextNotificationId++;
|
||||
@ -374,11 +371,11 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
}
|
||||
|
||||
if (hasDefaultAction) {
|
||||
notification.connect('clicked', Lang.bind(this, function() {
|
||||
notification.connect('activated', Lang.bind(this, function() {
|
||||
this._emitActionInvoked(ndata.id, 'default');
|
||||
}));
|
||||
} else {
|
||||
notification.connect('clicked', Lang.bind(this, function() {
|
||||
notification.connect('activated', Lang.bind(this, function() {
|
||||
source.open();
|
||||
}));
|
||||
}
|
||||
@ -551,16 +548,13 @@ const FdoNotificationDaemonSource = new Lang.Class({
|
||||
this.destroyNonResidentNotifications();
|
||||
},
|
||||
|
||||
_lastNotificationRemoved: function() {
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
openApp: function() {
|
||||
if (this.app == null)
|
||||
return;
|
||||
|
||||
this.app.activate();
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -648,7 +642,7 @@ const GtkNotificationDaemonNotification = new Lang.Class({
|
||||
this._activateAction(action.unpack(), actionTarget);
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
activate: function() {
|
||||
this._activateAction(this._defaultAction, this._defaultActionTarget);
|
||||
this.parent();
|
||||
},
|
||||
@ -715,11 +709,17 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||
activateAction: function(actionId, target) {
|
||||
let app = this._createApp();
|
||||
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
|
||||
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
open: function() {
|
||||
let app = this._createApp();
|
||||
app.ActivateRemote(getPlatformData());
|
||||
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
addNotification: function(notificationId, notificationParams, showBanner) {
|
||||
|
@ -15,8 +15,7 @@ const OsdMonitorLabel = new Lang.Class({
|
||||
Name: 'OsdMonitorLabel',
|
||||
|
||||
_init: function(monitor, label) {
|
||||
this._actor = new St.Widget({ opacity: 0,
|
||||
x_expand: true,
|
||||
this._actor = new St.Widget({ x_expand: true,
|
||||
y_expand: true });
|
||||
|
||||
this._monitor = monitor;
|
||||
@ -34,10 +33,6 @@ const OsdMonitorLabel = new Lang.Class({
|
||||
this._position();
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
Tweener.addTween(this._actor,
|
||||
{ opacity: 255,
|
||||
time: FADE_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
_position: function() {
|
||||
@ -52,15 +47,8 @@ const OsdMonitorLabel = new Lang.Class({
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
Tweener.addTween(this._actor,
|
||||
{ opacity: 0,
|
||||
time: FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._actor.destroy();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
})
|
||||
});
|
||||
this._actor.destroy();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -22,11 +22,13 @@ const LevelBar = new Lang.Class({
|
||||
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));
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this._bar = new St.Widget({ style_class: 'level-bar' });
|
||||
|
||||
this.actor.set_child(this._bar);
|
||||
|
||||
this.actor.connect('notify::width', () => { this.level = this.level; });
|
||||
},
|
||||
|
||||
get level() {
|
||||
@ -34,39 +36,44 @@ const LevelBar = new Lang.Class({
|
||||
},
|
||||
|
||||
set level(value) {
|
||||
let newValue = Math.max(0, Math.min(value, 100));
|
||||
if (newValue == this._level)
|
||||
return;
|
||||
this._level = newValue;
|
||||
this._bar.queue_repaint();
|
||||
this._level = Math.max(0, Math.min(value, 100));
|
||||
|
||||
let alloc = this.actor.get_allocation_box();
|
||||
let newWidth = Math.round((alloc.x2 - alloc.x1) * this._level / 100);
|
||||
if (newWidth != this._bar.width)
|
||||
this._bar.width = newWidth;
|
||||
}
|
||||
});
|
||||
|
||||
const OsdWindowConstraint = new Lang.Class({
|
||||
Name: 'OsdWindowConstraint',
|
||||
Extends: Clutter.Constraint,
|
||||
|
||||
_init: function(props) {
|
||||
this._minSize = 0;
|
||||
this.parent(props);
|
||||
},
|
||||
|
||||
_repaint: function() {
|
||||
let cr = this._bar.get_context();
|
||||
set minSize(v) {
|
||||
this._minSize = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
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());
|
||||
vfunc_update_allocation: function(actor, actorBox) {
|
||||
// Clutter will adjust the allocation for margins,
|
||||
// so add it to our minimum size
|
||||
let minSize = this._minSize + actor.margin_top + actor.margin_bottom;
|
||||
let [width, height] = actorBox.get_size();
|
||||
|
||||
let [w, h] = this._bar.get_surface_size();
|
||||
w *= (this._level / 100.);
|
||||
// Enforce a ratio of 1
|
||||
let size = Math.ceil(Math.max(minSize, height));
|
||||
actorBox.set_size(size, size);
|
||||
|
||||
if (w == 0)
|
||||
return;
|
||||
|
||||
cr.moveTo(radius, 0);
|
||||
if (w >= radius)
|
||||
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
|
||||
else
|
||||
cr.lineTo(w, 0);
|
||||
if (w >= radius)
|
||||
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
|
||||
else
|
||||
cr.lineTo(w, h);
|
||||
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
|
||||
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
// Recenter
|
||||
let [x, y] = actorBox.get_origin();
|
||||
actorBox.set_origin(Math.ceil(x + width / 2 - size / 2),
|
||||
Math.ceil(y + height / 2 - size / 2));
|
||||
}
|
||||
});
|
||||
|
||||
@ -74,7 +81,6 @@ const OsdWindow = new Lang.Class({
|
||||
Name: 'OsdWindow',
|
||||
|
||||
_init: function(monitorIndex) {
|
||||
this._popupSize = 0;
|
||||
this.actor = new St.Widget({ x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -84,19 +90,12 @@ const OsdWindow = new Lang.Class({
|
||||
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this._boxConstraint = new OsdWindowConstraint();
|
||||
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
||||
vertical: true });
|
||||
this._box.add_constraint(this._boxConstraint);
|
||||
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 });
|
||||
|
||||
@ -110,8 +109,11 @@ const OsdWindow = new Lang.Class({
|
||||
this._reset();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
Lang.bind(this, this._relayout));
|
||||
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||
themeContext.connect('notify::scale-factor',
|
||||
Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
Main.uiGroup.add_child(this.actor);
|
||||
},
|
||||
|
||||
@ -189,7 +191,7 @@ const OsdWindow = new Lang.Class({
|
||||
this.setLevel(null);
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
_relayout: function() {
|
||||
/* assume 110x110 on a 640x480 display and scale from there */
|
||||
let monitor = Main.layoutManager.monitors[this._monitorIndex];
|
||||
if (!monitor)
|
||||
@ -198,30 +200,12 @@ const OsdWindow = new Lang.Class({
|
||||
let scalew = monitor.width / 640.0;
|
||||
let scaleh = monitor.height / 480.0;
|
||||
let scale = Math.min(scalew, scaleh);
|
||||
this._popupSize = 110 * Math.max(1, scale);
|
||||
let popupSize = 110 * Math.max(1, scale);
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._icon.icon_size = this._popupSize / (2 * scaleFactor);
|
||||
this._icon.icon_size = popupSize / (2 * scaleFactor);
|
||||
this._box.translation_y = monitor.height / 4;
|
||||
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;
|
||||
|
||||
// minWidth/minHeight here are in real pixels,
|
||||
// but the theme takes measures in unscaled dimensions
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor);
|
||||
this._boxConstraint.minSize = popupSize;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -107,28 +107,20 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._overviewCreated = true;
|
||||
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
vertical: true });
|
||||
this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||
this._overview._delegate = this;
|
||||
|
||||
// The main Background actors are inside global.window_group which are
|
||||
// hidden when displaying the overview, so we create a new
|
||||
// one. Instances of this class share a single CoglTexture behind the
|
||||
// scenes which allows us to show the background with different
|
||||
// rendering options without duplicating the texture data.
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
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
|
||||
activities. See also note for "Activities" string. */
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true,
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
this._overview._delegate = this;
|
||||
|
||||
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||
this._backgroundGroup = new Meta.BackgroundGroup({ reactive: true });
|
||||
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
|
||||
this._bgManagers = [];
|
||||
|
||||
@ -151,8 +143,7 @@ const Overview = new Lang.Class({
|
||||
Main.layoutManager.overviewGroup.add_child(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; }));
|
||||
|
||||
this._stack.add_actor(this._overview);
|
||||
Main.layoutManager.overviewGroup.add_child(this._stack);
|
||||
Main.layoutManager.overviewGroup.add_child(this._overview);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
@ -161,6 +152,9 @@ const Overview = new Lang.Class({
|
||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||
};
|
||||
|
||||
|
||||
Main.layoutManager.overviewGroup.connect('scroll-event',
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
@ -257,9 +251,6 @@ const Overview = new Lang.Class({
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
|
||||
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
this._stack.add_actor(this._controls.indicatorActor);
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
@ -376,7 +367,7 @@ const Overview = new Lang.Class({
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._overview.add_action(action);
|
||||
this._backgroundGroup.add_action(action);
|
||||
},
|
||||
|
||||
_getDesktopClone: function() {
|
||||
@ -556,8 +547,8 @@ const Overview = new Lang.Class({
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this.viewSelector.show();
|
||||
|
||||
this._stack.opacity = 0;
|
||||
Tweener.addTween(this._stack,
|
||||
this._overview.opacity = 0;
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -622,7 +613,7 @@ const Overview = new Lang.Class({
|
||||
this.viewSelector.animateFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._stack,
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
|
@ -391,108 +391,6 @@ const DashSpacer = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||
this._sources.push(source);
|
||||
this._updateCount();
|
||||
},
|
||||
|
||||
_onSourceRemoved: function(tray, source) {
|
||||
this._sources.splice(this._sources.indexOf(source), 1);
|
||||
this._updateCount();
|
||||
},
|
||||
|
||||
_updateCount: function() {
|
||||
let count = 0;
|
||||
let hasChats = false;
|
||||
this._sources.forEach(Lang.bind(this,
|
||||
function(source) {
|
||||
count += source.indicatorCount;
|
||||
hasChats |= source.isChat;
|
||||
}));
|
||||
|
||||
this._count = count;
|
||||
this._label.text = ngettext("%d new message",
|
||||
"%d new messages",
|
||||
count).format(count);
|
||||
|
||||
this._icon.visible = hasChats;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let activePage = this._viewSelector.getActivePage();
|
||||
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
|
||||
|
||||
this.actor.visible = visible;
|
||||
}
|
||||
});
|
||||
|
||||
const ControlsLayout = new Lang.Class({
|
||||
Name: 'ControlsLayout',
|
||||
Extends: Clutter.BinLayout,
|
||||
@ -521,12 +419,8 @@ const ControlsManager = new Lang.Class({
|
||||
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
|
||||
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
|
||||
|
||||
this._indicator = new MessagesIndicator(this.viewSelector);
|
||||
this.indicatorActor = this._indicator.actor;
|
||||
|
||||
let layout = new ControlsLayout();
|
||||
this.actor = new St.Widget({ layout_manager: layout,
|
||||
reactive: true,
|
||||
x_expand: true, y_expand: true,
|
||||
clip_to_allocation: true });
|
||||
this._group = new St.BoxLayout({ name: 'overview-group',
|
||||
|
132
js/ui/panel.js
@ -25,12 +25,12 @@ const RemoteMenu = imports.ui.remoteMenu;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PANEL_ICON_SIZE = 24;
|
||||
const APP_MENU_ICON_MARGIN = 2;
|
||||
const PANEL_ICON_SIZE = 16;
|
||||
const APP_MENU_ICON_MARGIN = 0;
|
||||
|
||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||
|
||||
const SPINNER_ANIMATION_TIME = 0.2;
|
||||
const SPINNER_ANIMATION_TIME = 1.0;
|
||||
|
||||
// To make sure the panel corners blend nicely with the panel,
|
||||
// we draw background and borders the same way, e.g. drawing
|
||||
@ -95,6 +95,7 @@ const AppMenuButton = new Lang.Class({
|
||||
this._startingApps = [];
|
||||
|
||||
this._menuManager = panel.menuManager;
|
||||
this._gtkSettings = Gtk.Settings.get_default();
|
||||
this._targetApp = null;
|
||||
this._appMenuNotifyId = 0;
|
||||
this._actionGroupNotifyId = 0;
|
||||
@ -117,16 +118,20 @@ const AppMenuButton = new Lang.Class({
|
||||
this._iconBox = new St.Bin({ style_class: 'app-menu-icon' });
|
||||
this._container.add_actor(this._iconBox);
|
||||
|
||||
this._label = new St.Label();
|
||||
this._label = new St.Label({ y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this._container.add_actor(this._label);
|
||||
this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM);
|
||||
this._container.add_actor(this._arrow);
|
||||
|
||||
this._visible = !Main.overview.visible;
|
||||
this._visible = this._gtkSettings.gtk_shell_shows_app_menu &&
|
||||
!Main.overview.visible;
|
||||
if (!this._visible)
|
||||
this.actor.hide();
|
||||
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync));
|
||||
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync));
|
||||
this._showsAppMenuId = this._gtkSettings.connect('notify::gtk-shell-shows-app-menu',
|
||||
Lang.bind(this, this._sync));
|
||||
|
||||
this._stop = true;
|
||||
|
||||
@ -304,7 +309,9 @@ const AppMenuButton = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
let visible = (this._targetApp != null && !Main.overview.visibleTarget);
|
||||
let visible = (this._targetApp != null &&
|
||||
this._gtkSettings.gtk_shell_shows_app_menu &&
|
||||
!Main.overview.visibleTarget);
|
||||
if (visible)
|
||||
this.show();
|
||||
else
|
||||
@ -377,6 +384,10 @@ const AppMenuButton = new Lang.Class({
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
this._overviewShowingId = 0;
|
||||
}
|
||||
if (this._showsAppMenuId > 0) {
|
||||
this._gtkSettings.disconnect(this._showsAppMenuId);
|
||||
this._showsAppMenuId = 0;
|
||||
}
|
||||
if (this._switchWorkspaceNotifyId > 0) {
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
@ -448,7 +459,8 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
if (event.type() == Clutter.EventType.TOUCH_END ||
|
||||
event.type() == Clutter.EventType.BUTTON_RELEASE)
|
||||
Main.overview.toggle();
|
||||
if (Main.overview.shouldToggleByCornerOrButton())
|
||||
Main.overview.toggle();
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
@ -456,7 +468,8 @@ const ActivitiesButton = new Lang.Class({
|
||||
_onKeyRelease: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
||||
Main.overview.toggle();
|
||||
if (Main.overview.shouldToggleByCornerOrButton())
|
||||
Main.overview.toggle();
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
@ -641,14 +654,50 @@ const PanelCorner = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const AggregateLayout = new Lang.Class({
|
||||
Name: 'AggregateLayout',
|
||||
Extends: Clutter.BoxLayout,
|
||||
|
||||
_init: function(params) {
|
||||
if (!params)
|
||||
params = {};
|
||||
params['orientation'] = Clutter.Orientation.VERTICAL;
|
||||
this.parent(params);
|
||||
|
||||
this._sizeChildren = [];
|
||||
},
|
||||
|
||||
addSizeChild: function(actor) {
|
||||
this._sizeChildren.push(actor);
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
let themeNode = container.get_theme_node();
|
||||
let minWidth = themeNode.get_min_width();
|
||||
let natWidth = minWidth;
|
||||
|
||||
for (let i = 0; i < this._sizeChildren.length; i++) {
|
||||
let child = this._sizeChildren[i];
|
||||
let [childMin, childNat] = child.get_preferred_width(forHeight);
|
||||
minWidth = Math.max(minWidth, childMin);
|
||||
natWidth = Math.max(minWidth, childNat);
|
||||
}
|
||||
return [minWidth, natWidth];
|
||||
}
|
||||
});
|
||||
|
||||
const AggregateMenu = new Lang.Class({
|
||||
Name: 'AggregateMenu',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, _("Settings"), false);
|
||||
this.parent(0.0, C_("System menu in the top bar", "System"), false);
|
||||
this.menu.actor.add_style_class_name('aggregate-menu');
|
||||
|
||||
let menuLayout = new AggregateLayout();
|
||||
this.menu.box.set_layout_manager(menuLayout);
|
||||
|
||||
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
|
||||
this.actor.add_child(this._indicators);
|
||||
|
||||
@ -696,8 +745,12 @@ const AggregateMenu = new Lang.Class({
|
||||
this.menu.addMenuItem(this._location.menu);
|
||||
this.menu.addMenuItem(this._rfkill.menu);
|
||||
this.menu.addMenuItem(this._power.menu);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addMenuItem(this._system.menu);
|
||||
|
||||
menuLayout.addSizeChild(this._location.menu.actor);
|
||||
menuLayout.addSizeChild(this._rfkill.menu.actor);
|
||||
menuLayout.addSizeChild(this._power.menu.actor);
|
||||
menuLayout.addSizeChild(this._system.menu.actor);
|
||||
},
|
||||
});
|
||||
|
||||
@ -723,7 +776,7 @@ const Panel = new Lang.Class({
|
||||
|
||||
this.statusArea = {};
|
||||
|
||||
this.menuManager = new PopupMenu.PopupMenuManager(this, { actionMode: Shell.ActionMode.TOPBAR_POPUP });
|
||||
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
||||
|
||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||
this.actor.add_actor(this._leftBox);
|
||||
@ -751,7 +804,7 @@ const Panel = new Lang.Class({
|
||||
}));
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'focus-top-bar-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
|
||||
@ -877,9 +930,8 @@ const Panel = new Lang.Class({
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
toggleAppMenu: function() {
|
||||
let indicator = this.statusArea.appMenu;
|
||||
if (!indicator) // appMenu not supported by current session mode
|
||||
_toggleMenu: function(indicator) {
|
||||
if (!indicator) // menu not supported by current session mode
|
||||
return;
|
||||
|
||||
let menu = indicator.menu;
|
||||
@ -891,6 +943,26 @@ const Panel = new Lang.Class({
|
||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
toggleAppMenu: function() {
|
||||
this._toggleMenu(this.statusArea.appMenu);
|
||||
},
|
||||
|
||||
toggleCalendar: function() {
|
||||
this._toggleMenu(this.statusArea.dateMenu);
|
||||
},
|
||||
|
||||
closeCalendar: function() {
|
||||
let indicator = this.statusArea.dateMenu;
|
||||
if (!indicator) // calendar not supported by current session mode
|
||||
return;
|
||||
|
||||
let menu = indicator.menu;
|
||||
if (!indicator.actor.reactive)
|
||||
return;
|
||||
|
||||
menu.close();
|
||||
},
|
||||
|
||||
set boxOpacity(value) {
|
||||
let isReactive = value > 0;
|
||||
|
||||
@ -913,6 +985,14 @@ const Panel = new Lang.Class({
|
||||
this._updateBox(panel.center, this._centerBox);
|
||||
this._updateBox(panel.right, this._rightBox);
|
||||
|
||||
if (panel.left.indexOf('dateMenu') != -1)
|
||||
Main.messageTray.bannerAlignment = Clutter.ActorAlign.START;
|
||||
else if (panel.right.indexOf('dateMenu') != -1)
|
||||
Main.messageTray.bannerAlignment = Clutter.ActorAlign.END;
|
||||
// Default to center if there is no dateMenu
|
||||
else
|
||||
Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER;
|
||||
|
||||
if (this._sessionStyle)
|
||||
this._removeStyleClassName(this._sessionStyle);
|
||||
|
||||
@ -975,6 +1055,7 @@ const Panel = new Lang.Class({
|
||||
if (parent)
|
||||
parent.remove_actor(container);
|
||||
|
||||
|
||||
box.insert_child_at_index(container, position);
|
||||
if (indicator.menu)
|
||||
this.menuManager.addMenu(indicator.menu);
|
||||
@ -984,6 +1065,8 @@ const Panel = new Lang.Class({
|
||||
emitter.disconnect(destroyId);
|
||||
container.destroy();
|
||||
}));
|
||||
indicator.connect('menu-set', Lang.bind(this, this._onMenuSet));
|
||||
this._onMenuSet(indicator);
|
||||
},
|
||||
|
||||
addToStatusArea: function(role, indicator, position, box) {
|
||||
@ -1015,5 +1098,24 @@ const Panel = new Lang.Class({
|
||||
this.actor.remove_style_class_name(className);
|
||||
this._rightCorner.actor.remove_style_class_name(className);
|
||||
this._leftCorner.actor.remove_style_class_name(className);
|
||||
},
|
||||
|
||||
_onMenuSet: function(indicator) {
|
||||
if (!indicator.menu || indicator.menu._openChangedId > 0)
|
||||
return;
|
||||
|
||||
indicator.menu._openChangedId = indicator.menu.connect('open-state-changed',
|
||||
Lang.bind(this, function(menu, isOpen) {
|
||||
let boxAlignment;
|
||||
if (this._leftBox.contains(indicator.container))
|
||||
boxAlignment = Clutter.ActorAlign.START;
|
||||
else if (this._centerBox.contains(indicator.container))
|
||||
boxAlignment = Clutter.ActorAlign.CENTER;
|
||||
else if (this._rightBox.contains(indicator.container))
|
||||
boxAlignment = Clutter.ActorAlign.END;
|
||||
|
||||
if (boxAlignment == Main.messageTray.bannerAlignment)
|
||||
Main.messageTray.bannerBlocked = isOpen;
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
@ -128,6 +128,7 @@ const Button = new Lang.Class({
|
||||
Main.uiGroup.add_actor(this.menu.actor);
|
||||
this.menu.actor.hide();
|
||||
}
|
||||
this.emit('menu-set');
|
||||
},
|
||||
|
||||
_onEvent: function(actor, event) {
|
||||
|
@ -12,7 +12,6 @@ const BoxPointer = imports.ui.boxpointer;
|
||||
const GrabHelper = imports.ui.grabHelper;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Separator = imports.ui.separator;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const Ornament = {
|
||||
@ -170,10 +169,10 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
if (activeChanged) {
|
||||
this.active = active;
|
||||
if (active) {
|
||||
this.actor.add_style_class_name('active');
|
||||
this.actor.add_style_class_name('selected');
|
||||
this.actor.grab_key_focus();
|
||||
} else {
|
||||
this.actor.remove_style_class_name('active');
|
||||
this.actor.remove_style_class_name('selected');
|
||||
// Remove the CSS active state if the user press the button and
|
||||
// while holding moves to another menu item, so we don't paint all items.
|
||||
// The correct behaviour would be to set the new item with the CSS
|
||||
@ -264,7 +263,9 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
||||
Lang.bind(this, this._syncVisibility));
|
||||
this._syncVisibility();
|
||||
|
||||
this._separator = new St.Widget({ style_class: 'popup-separator-menu-item' });
|
||||
this._separator = new St.Widget({ style_class: 'popup-separator-menu-item',
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add(this._separator, { expand: true });
|
||||
},
|
||||
|
||||
@ -395,6 +396,7 @@ const PopupImageMenuItem = new Lang.Class({
|
||||
this.actor.add_child(this.label);
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
this.actor.add_child(this._icon, { align: St.Align.END });
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.setIcon(iconName);
|
||||
},
|
||||
@ -781,6 +783,16 @@ const PopupMenu = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
|
||||
let state = event.get_state();
|
||||
|
||||
// if user has a modifier down (except capslock)
|
||||
// then don't handle the key press here
|
||||
state &= ~Clutter.ModifierType.LOCK_MASK;
|
||||
state &= Clutter.ModifierType.MODIFIER_MASK;
|
||||
|
||||
if (state)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
this.toggle();
|
||||
@ -1058,11 +1070,6 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
let expander = new St.Bin({ style_class: 'popup-menu-item-expander' });
|
||||
this.actor.add(expander, { expand: true });
|
||||
|
||||
this.status = new St.Label({ style_class: 'popup-status-menu-item',
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_child(this.status);
|
||||
|
||||
this._triangle = arrowIcon(St.Side.RIGHT);
|
||||
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
|
||||
|
||||
@ -1149,6 +1156,16 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
this.actor.remove_style_pseudo_class ('active');
|
||||
this._setOpenState(!this._getOpenState());
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onTouchEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.TOUCH_END) {
|
||||
// Since we override the parent, we need to manage what the parent does
|
||||
// with the active style class
|
||||
this.actor.remove_style_pseudo_class ('active');
|
||||
this._setOpenState(!this._getOpenState());
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1159,6 +1176,8 @@ const PopupMenuManager = new Lang.Class({
|
||||
Name: 'PopupMenuManager',
|
||||
|
||||
_init: function(owner, grabParams) {
|
||||
grabParams = Params.parse(grabParams,
|
||||
{ actionMode: Shell.ActionMode.POPUP });
|
||||
this._owner = owner;
|
||||
this._grabHelper = new GrabHelper.GrabHelper(owner.actor, grabParams);
|
||||
this._menus = [];
|
||||
|
@ -63,7 +63,7 @@ const SearchProvider2Iface = '<node> \
|
||||
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
|
||||
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
|
||||
|
||||
function loadRemoteSearchProviders(callback) {
|
||||
function loadRemoteSearchProviders(searchSettings, callback) {
|
||||
let objectPaths = {};
|
||||
let loadedProviders = [];
|
||||
|
||||
@ -124,7 +124,6 @@ function loadRemoteSearchProviders(callback) {
|
||||
}
|
||||
}
|
||||
|
||||
let searchSettings = new Gio.Settings({ schema_id: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
if (searchSettings.get_boolean('disable-external')) {
|
||||
callback([]);
|
||||
return;
|
||||
@ -236,8 +235,10 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
|
||||
_getResultsFinished: function(results, error, callback) {
|
||||
if (error) {
|
||||
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
|
||||
if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||
return;
|
||||
|
||||
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
// rt is short for "reload theme"
|
||||
'rt': Lang.bind(this, function() {
|
||||
Main.reloadThemeResource();
|
||||
Main.loadTheme();
|
||||
})
|
||||
};
|
||||
@ -272,6 +273,10 @@ const RunDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_restart: function() {
|
||||
if (Meta.is_wayland_compositor()) {
|
||||
this._showError('Restart is not available on Wayland');
|
||||
return;
|
||||
}
|
||||
this._shouldFadeOut = false;
|
||||
this.close();
|
||||
Meta.restart(_("Restarting…"));
|
||||
|
@ -60,9 +60,6 @@ const CURTAIN_SLIDE_TIME = 0.3;
|
||||
const Clock = new Lang.Class({
|
||||
Name: 'ScreenShieldClock',
|
||||
|
||||
CLOCK_FORMAT_KEY: 'clock-format',
|
||||
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
|
||||
vertical: true });
|
||||
@ -274,7 +271,8 @@ const NotificationsBox = new Lang.Class({
|
||||
});
|
||||
|
||||
this._updateVisibility();
|
||||
this.emit('wake-up-screen');
|
||||
if (obj.sourceBox.visible)
|
||||
this.emit('wake-up-screen');
|
||||
}
|
||||
},
|
||||
|
||||
@ -351,7 +349,6 @@ const Arrow = new Lang.Class({
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
this.x_fill = this.y_fill = true;
|
||||
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
|
||||
this._drawingArea = new St.DrawingArea();
|
||||
this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
|
||||
@ -379,6 +376,22 @@ const Arrow = new Lang.Class({
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
vfunc_get_paint_volume: function(volume) {
|
||||
if (!this.parent(volume))
|
||||
return false;
|
||||
|
||||
if (!this._shadow)
|
||||
return true;
|
||||
|
||||
let shadow_box = new Clutter.ActorBox();
|
||||
this._shadow.get_box(this._drawingArea.get_allocation_box(), shadow_box);
|
||||
|
||||
volume.set_width(Math.max(shadow_box.x2 - shadow_box.x1, volume.get_width()));
|
||||
volume.set_height(Math.max(shadow_box.y2 - shadow_box.y1, volume.get_height()));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
vfunc_style_changed: function() {
|
||||
let node = this.get_theme_node();
|
||||
this._shadow = node.get_shadow('-arrow-shadow');
|
||||
@ -386,6 +399,8 @@ const Arrow = new Lang.Class({
|
||||
this._shadowHelper = St.ShadowHelper.new(this._shadow);
|
||||
else
|
||||
this._shadowHelper = null;
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
vfunc_paint: function() {
|
||||
@ -509,21 +524,22 @@ const ScreenShield = new Lang.Class({
|
||||
this._liftShield(true, 0);
|
||||
}));
|
||||
|
||||
this._inhibitor = null;
|
||||
this._aboutToSuspend = false;
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._loginManager.connect('prepare-for-sleep',
|
||||
Lang.bind(this, this._prepareForSleep));
|
||||
this._inhibitSuspend();
|
||||
|
||||
this._loginSession = null;
|
||||
this._loginManager.getCurrentSessionProxy(Lang.bind(this,
|
||||
function(sessionProxy) {
|
||||
this._loginSession = sessionProxy;
|
||||
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
|
||||
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
|
||||
this._loginSession.connect('g-properties-changed', Lang.bind(this, this._syncInhibitor));
|
||||
this._syncInhibitor();
|
||||
}));
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA });
|
||||
this._settings.connect('changed::' + LOCK_ENABLED_KEY, Lang.bind(this, this._syncInhibitor));
|
||||
|
||||
this._isModal = false;
|
||||
this._hasLockScreen = false;
|
||||
@ -549,6 +565,21 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this.idleMonitor = Meta.IdleMonitor.get_core();
|
||||
this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
|
||||
|
||||
this._syncInhibitor();
|
||||
},
|
||||
|
||||
_setActive: function(active) {
|
||||
let prevIsActive = this._isActive;
|
||||
this._isActive = active;
|
||||
|
||||
if (prevIsActive != this._isActive)
|
||||
this.emit('active-changed');
|
||||
|
||||
if (this._loginSession)
|
||||
this._loginSession.SetLockedHintRemote(active);
|
||||
|
||||
this._syncInhibitor();
|
||||
},
|
||||
|
||||
_createBackground: function(monitorIndex) {
|
||||
@ -666,31 +697,28 @@ const ScreenShield = new Lang.Class({
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_inhibitSuspend: function() {
|
||||
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
|
||||
Lang.bind(this, function(inhibitor) {
|
||||
this._inhibitor = inhibitor;
|
||||
}));
|
||||
},
|
||||
|
||||
_uninhibitSuspend: function() {
|
||||
if (this._inhibitor)
|
||||
this._inhibitor.close(null);
|
||||
this._inhibitor = null;
|
||||
_syncInhibitor: function() {
|
||||
let inhibit = (this._loginSession && this._loginSession.Active &&
|
||||
!this._isActive && this._settings.get_boolean(LOCK_ENABLED_KEY));
|
||||
if (inhibit) {
|
||||
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
|
||||
Lang.bind(this, function(inhibitor) {
|
||||
if (this._inhibitor)
|
||||
this._inhibitor.close(null);
|
||||
this._inhibitor = inhibitor;
|
||||
}));
|
||||
} else {
|
||||
if (this._inhibitor)
|
||||
this._inhibitor.close(null);
|
||||
this._inhibitor = null;
|
||||
}
|
||||
},
|
||||
|
||||
_prepareForSleep: function(loginManager, aboutToSuspend) {
|
||||
this._aboutToSuspend = aboutToSuspend;
|
||||
|
||||
if (aboutToSuspend) {
|
||||
if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||
this._uninhibitSuspend();
|
||||
return;
|
||||
}
|
||||
this.lock(true);
|
||||
if (this._settings.get_boolean(LOCK_ENABLED_KEY))
|
||||
this.lock(true);
|
||||
} else {
|
||||
this._inhibitSuspend();
|
||||
|
||||
this._wakeUpScreen();
|
||||
}
|
||||
},
|
||||
@ -700,7 +728,7 @@ const ScreenShield = new Lang.Class({
|
||||
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
|
||||
let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
|
||||
for (let i = 0; i < arrows.length; i++) {
|
||||
arrows.opacity = 0;
|
||||
arrows[i].opacity = 0;
|
||||
Tweener.addTween(arrows[i],
|
||||
{ opacity: 0,
|
||||
delay: unitaryDelay * (N_ARROWS - (i + 1)),
|
||||
@ -819,6 +847,7 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_activateFade: function(lightbox, time) {
|
||||
Main.uiGroup.set_child_above_sibling(lightbox.actor, null);
|
||||
lightbox.show(time);
|
||||
|
||||
if (this._becameActiveId == 0)
|
||||
@ -1085,15 +1114,7 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_completeLockScreenShown: function() {
|
||||
let prevIsActive = this._isActive;
|
||||
this._isActive = true;
|
||||
|
||||
if (prevIsActive != this._isActive)
|
||||
this.emit('active-changed');
|
||||
|
||||
if (this._aboutToSuspend)
|
||||
this._uninhibitSuspend();
|
||||
|
||||
this._setActive(true);
|
||||
this.emit('lock-screen-shown');
|
||||
},
|
||||
|
||||
@ -1187,8 +1208,7 @@ const ScreenShield = new Lang.Class({
|
||||
// gnome-settings-daemon will stop blanking the screen
|
||||
|
||||
this._activationTime = 0;
|
||||
this._isActive = false;
|
||||
this.emit('active-changed');
|
||||
this._setActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1231,9 +1251,8 @@ const ScreenShield = new Lang.Class({
|
||||
}
|
||||
|
||||
this._activationTime = 0;
|
||||
this._isActive = false;
|
||||
this._setActive(false);
|
||||
this._isLocked = false;
|
||||
this.emit('active-changed');
|
||||
this.emit('locked-changed');
|
||||
global.set_runtime_state(LOCKED_STATE_STR, null);
|
||||
},
|
||||
|
@ -283,8 +283,8 @@ const SelectArea = new Lang.Class({
|
||||
_getGeometry: function() {
|
||||
return { x: Math.min(this._startX, this._lastX),
|
||||
y: Math.min(this._startY, this._lastY),
|
||||
width: Math.abs(this._startX - this._lastX),
|
||||
height: Math.abs(this._startY - this._lastY) };
|
||||
width: Math.abs(this._startX - this._lastX) + 1,
|
||||
height: Math.abs(this._startY - this._lastY) + 1 };
|
||||
},
|
||||
|
||||
_onMotionEvent: function(actor, event) {
|
||||
@ -292,6 +292,8 @@ const SelectArea = new Lang.Class({
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
[this._lastX, this._lastY] = event.get_coords();
|
||||
this._lastX = Math.floor(this._lastX);
|
||||
this._lastY = Math.floor(this._lastY);
|
||||
let geometry = this._getGeometry();
|
||||
|
||||
this._rubberband.set_position(geometry.x, geometry.y);
|
||||
@ -302,6 +304,8 @@ const SelectArea = new Lang.Class({
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
[this._startX, this._startY] = event.get_coords();
|
||||
this._startX = Math.floor(this._startX);
|
||||
this._startY = Math.floor(this._startY);
|
||||
this._rubberband.set_position(this._startX, this._startY);
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
@ -205,6 +205,13 @@ const SearchResultsBase = new Lang.Class({
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
if (metas.some(function(meta) {
|
||||
return !meta.name || !meta.id;
|
||||
})) {
|
||||
log('Invalid result meta returned from search provider ' + this.provider.id);
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
metasNeeded.forEach(Lang.bind(this, function(resultId, i) {
|
||||
let meta = metas[i];
|
||||
@ -409,6 +416,7 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
this._searchSettings = new Gio.Settings({ schema_id: SEARCH_PROVIDERS_SCHEMA });
|
||||
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
|
||||
this._searchSettings.connect('changed::enabled', Lang.bind(this, this._reloadRemoteProviders));
|
||||
this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
|
||||
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
|
||||
|
||||
@ -427,7 +435,7 @@ const SearchResults = new Lang.Class({
|
||||
this._unregisterProvider(provider);
|
||||
}));
|
||||
|
||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
|
||||
RemoteSearch.loadRemoteSearchProviders(this._searchSettings, Lang.bind(this, function(providers) {
|
||||
providers.forEach(Lang.bind(this, this._registerProvider));
|
||||
}));
|
||||
},
|
||||
|
@ -49,9 +49,9 @@ function _setButtonsForChoices(dialog, choices) {
|
||||
function _setLabelsForMessage(dialog, message) {
|
||||
let labels = message.split('\n');
|
||||
|
||||
_setLabelText(dialog.subjectLabel, labels[0]);
|
||||
if (labels.length > 1)
|
||||
_setLabelText(dialog.descriptionLabel, labels[1]);
|
||||
_setLabelText(dialog.subjectLabel, labels.shift());
|
||||
if (labels.length > 0)
|
||||
_setLabelText(dialog.descriptionLabel, labels.join('\n'));
|
||||
}
|
||||
|
||||
function _createIcon(gicon) {
|
||||
@ -347,7 +347,7 @@ const ShellMountPasswordDialog = new Lang.Class({
|
||||
mainContentBox.add(this._messageBox,
|
||||
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
|
||||
|
||||
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
|
||||
let subject = new St.Label({ style_class: 'prompt-dialog-headline headline' });
|
||||
this._messageBox.add(subject,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
@ -137,6 +137,10 @@ const Slider = new Lang.Class({
|
||||
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
|
||||
}
|
||||
|
||||
// We need to emit 'drag-begin' before moving the handle to make
|
||||
// sure that no 'value-changed' signal is emitted before this one.
|
||||
this.emit('drag-begin');
|
||||
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
this._moveHandle(absX, absY);
|
||||
@ -224,6 +228,7 @@ const Slider = new Lang.Class({
|
||||
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
|
||||
this._value = Math.max(0, Math.min(this._value + delta, 1));
|
||||
this.actor.queue_repaint();
|
||||
this.emit('drag-begin');
|
||||
this.emit('value-changed', this._value);
|
||||
this.emit('drag-end');
|
||||
return Clutter.EVENT_STOP;
|
||||
|
@ -119,23 +119,46 @@ const ATIndicator = new Lang.Class({
|
||||
|
||||
_buildItem: function(string, schema, key) {
|
||||
let settings = new Gio.Settings({ schema_id: schema });
|
||||
settings.connect('changed::'+key, Lang.bind(this, function() {
|
||||
widget.setToggleState(settings.get_boolean(key));
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
|
||||
let widget = this._buildItemExtended(string,
|
||||
settings.get_boolean(key),
|
||||
settings.is_writable(key),
|
||||
function(enabled) {
|
||||
return settings.set_boolean(key, enabled);
|
||||
});
|
||||
settings.connect('changed::'+key, Lang.bind(this, function() {
|
||||
widget.setToggleState(settings.get_boolean(key));
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
return widget;
|
||||
},
|
||||
|
||||
_buildHCItem: function() {
|
||||
let interfaceSettings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA });
|
||||
let wmSettings = new Gio.Settings({ schema_id: WM_SCHEMA });
|
||||
interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() {
|
||||
let value = interfaceSettings.get_string(KEY_GTK_THEME);
|
||||
if (value == HIGH_CONTRAST_THEME) {
|
||||
highContrast.setToggleState(true);
|
||||
} else {
|
||||
highContrast.setToggleState(false);
|
||||
gtkTheme = value;
|
||||
}
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
|
||||
let value = interfaceSettings.get_string(KEY_ICON_THEME);
|
||||
if (value != HIGH_CONTRAST_THEME)
|
||||
iconTheme = value;
|
||||
});
|
||||
wmSettings.connect('changed::' + KEY_WM_THEME, function() {
|
||||
let value = wmSettings.get_string(KEY_WM_THEME);
|
||||
if (value != HIGH_CONTRAST_THEME)
|
||||
wmTheme = value;
|
||||
});
|
||||
|
||||
let gtkTheme = interfaceSettings.get_string(KEY_GTK_THEME);
|
||||
let iconTheme = interfaceSettings.get_string(KEY_ICON_THEME);
|
||||
let wmTheme = wmSettings.get_string(KEY_WM_THEME);
|
||||
@ -161,32 +184,18 @@ const ATIndicator = new Lang.Class({
|
||||
wmSettings.reset(KEY_WM_THEME);
|
||||
}
|
||||
});
|
||||
interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() {
|
||||
let value = interfaceSettings.get_string(KEY_GTK_THEME);
|
||||
if (value == HIGH_CONTRAST_THEME) {
|
||||
highContrast.setToggleState(true);
|
||||
} else {
|
||||
highContrast.setToggleState(false);
|
||||
gtkTheme = value;
|
||||
}
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
|
||||
let value = interfaceSettings.get_string(KEY_ICON_THEME);
|
||||
if (value != HIGH_CONTRAST_THEME)
|
||||
iconTheme = value;
|
||||
});
|
||||
wmSettings.connect('changed::' + KEY_WM_THEME, function() {
|
||||
let value = wmSettings.get_string(KEY_WM_THEME);
|
||||
if (value != HIGH_CONTRAST_THEME)
|
||||
wmTheme = value;
|
||||
});
|
||||
return highContrast;
|
||||
},
|
||||
|
||||
_buildFontItem: function() {
|
||||
let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA });
|
||||
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() {
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let active = (factor > 1.0);
|
||||
widget.setToggleState(active);
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let initial_setting = (factor > 1.0);
|
||||
@ -200,13 +209,6 @@ const ATIndicator = new Lang.Class({
|
||||
else
|
||||
settings.reset(KEY_TEXT_SCALING_FACTOR);
|
||||
});
|
||||
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() {
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let active = (factor > 1.0);
|
||||
widget.setToggleState(active);
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
return widget;
|
||||
}
|
||||
});
|
||||
|
@ -1,11 +1,8 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeBluetooth = imports.gi.GnomeBluetooth;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
@ -23,6 +20,8 @@ const RfkillManagerInterface = '<node> \
|
||||
|
||||
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
|
||||
|
||||
const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup';
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'BTIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
@ -32,6 +31,7 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'bluetooth-active-symbolic';
|
||||
this._hadSetupDevices = global.settings.get_boolean(HAD_BLUETOOTH_DEVICES_SETUP);
|
||||
|
||||
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
@ -44,13 +44,15 @@ const Indicator = new Lang.Class({
|
||||
}));
|
||||
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
|
||||
|
||||
// The Bluetooth menu only appears when Bluetooth is in use,
|
||||
// so just statically build it with a "Turn Off" menu item.
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
|
||||
this._item.icon.icon_name = 'bluetooth-active-symbolic';
|
||||
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
|
||||
this._proxy.BluetoothAirplaneMode = true;
|
||||
|
||||
this._toggleItem = new PopupMenu.PopupMenuItem('');
|
||||
this._toggleItem.connect('activate', Lang.bind(this, function() {
|
||||
this._proxy.BluetoothAirplaneMode = !this._proxy.BluetoothAirplaneMode;
|
||||
}));
|
||||
this._item.menu.addMenuItem(this._toggleItem);
|
||||
|
||||
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
@ -68,41 +70,75 @@ const Indicator = new Lang.Class({
|
||||
while (ret) {
|
||||
let isDefault = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.DEFAULT);
|
||||
if (isDefault)
|
||||
let isPowered = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.POWERED);
|
||||
if (isDefault && isPowered)
|
||||
return iter;
|
||||
ret = this._model.iter_next(iter);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_getNConnectedDevices: function() {
|
||||
// nDevices is the number of devices setup for the current default
|
||||
// adapter if one exists and is powered. If unpowered or unavailable,
|
||||
// nDevice is "1" if it had setup devices associated to it the last
|
||||
// time it was seen, and "-1" if not.
|
||||
//
|
||||
// nConnectedDevices is the number of devices connected to the default
|
||||
// adapter if one exists and is powered, or -1 if it's not available.
|
||||
_getNDevices: function() {
|
||||
let adapter = this._getDefaultAdapter();
|
||||
if (!adapter)
|
||||
return 0;
|
||||
return [ this._hadSetupDevices ? 1 : -1, -1 ];
|
||||
|
||||
let nConnectedDevices = 0;
|
||||
let nDevices = 0;
|
||||
let [ret, iter] = this._model.iter_children(adapter);
|
||||
while (ret) {
|
||||
let isConnected = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.CONNECTED);
|
||||
if (isConnected)
|
||||
nConnectedDevices++;
|
||||
|
||||
let isPaired = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.PAIRED);
|
||||
let isTrusted = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.TRUSTED);
|
||||
if (isPaired || isTrusted)
|
||||
nDevices++;
|
||||
ret = this._model.iter_next(iter);
|
||||
}
|
||||
return nDevices;
|
||||
|
||||
if (this._hadSetupDevices != (nDevices > 0)) {
|
||||
this._hadSetupDevices = !this._hadSetupDevices;
|
||||
global.settings.set_boolean(HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
|
||||
}
|
||||
|
||||
return [ nDevices, nConnectedDevices];
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let nDevices = this._getNConnectedDevices();
|
||||
let [ nDevices, nConnectedDevices ] = this._getNDevices();
|
||||
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
|
||||
this.menu.setSensitive(sensitive);
|
||||
this._indicator.visible = nDevices > 0;
|
||||
this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
|
||||
this._indicator.visible = nConnectedDevices > 0;
|
||||
|
||||
// Remember if there were setup devices and show the menu
|
||||
// if we've seen setup devices and we're not hard blocked
|
||||
if (nDevices > 0)
|
||||
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
|
||||
this._item.actor.visible = !this._proxy.BluetoothHardwareAirplaneMode;
|
||||
else
|
||||
this._item.status.text = _("Not Connected");
|
||||
this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
|
||||
|
||||
if (nConnectedDevices > 0)
|
||||
/* Translators: this is the number of connected bluetooth devices */
|
||||
this._item.label.text = ngettext("%d Connected", "%d Connected", nConnectedDevices).format(nConnectedDevices);
|
||||
else if (nConnectedDevices == -1)
|
||||
this._item.label.text = _("Off");
|
||||
else
|
||||
this._item.label.text = _("Not In Use");
|
||||
|
||||
this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _("Turn On") : _("Turn Off");
|
||||
},
|
||||
});
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -18,10 +19,6 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const SwitcherPopup = imports.ui.switcherPopup;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
|
||||
const KEY_INPUT_SOURCES = 'sources';
|
||||
const KEY_KEYBOARD_OPTIONS = 'xkb-options';
|
||||
|
||||
const INPUT_SOURCE_TYPE_XKB = 'xkb';
|
||||
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
|
||||
|
||||
@ -64,8 +61,8 @@ const InputSource = new Lang.Class({
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.emit('activate');
|
||||
activate: function(interactive) {
|
||||
this.emit('activate', !!interactive);
|
||||
},
|
||||
|
||||
_getXkbId: function() {
|
||||
@ -112,7 +109,7 @@ const InputSourcePopup = new Lang.Class({
|
||||
_finish : function() {
|
||||
this.parent();
|
||||
|
||||
this._items[this._selectedIndex].activate();
|
||||
this._items[this._selectedIndex].activate(true);
|
||||
},
|
||||
});
|
||||
|
||||
@ -142,6 +139,171 @@ const InputSourceSwitcher = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const InputSourceSettings = new Lang.Class({
|
||||
Name: 'InputSourceSettings',
|
||||
Abstract: true,
|
||||
|
||||
_emitInputSourcesChanged: function() {
|
||||
this.emit('input-sources-changed');
|
||||
},
|
||||
|
||||
_emitKeyboardOptionsChanged: function() {
|
||||
this.emit('keyboard-options-changed');
|
||||
},
|
||||
|
||||
_emitPerWindowChanged: function() {
|
||||
this.emit('per-window-changed');
|
||||
},
|
||||
|
||||
get inputSources() {
|
||||
return [];
|
||||
},
|
||||
|
||||
get mruSources() {
|
||||
return [];
|
||||
},
|
||||
|
||||
set mruSources(sourcesList) {
|
||||
// do nothing
|
||||
},
|
||||
|
||||
get keyboardOptions() {
|
||||
return [];
|
||||
},
|
||||
|
||||
get perWindow() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(InputSourceSettings.prototype);
|
||||
|
||||
const InputSourceSystemSettings = new Lang.Class({
|
||||
Name: 'InputSourceSystemSettings',
|
||||
Extends: InputSourceSettings,
|
||||
|
||||
_BUS_NAME: 'org.freedesktop.locale1',
|
||||
_BUS_PATH: '/org/freedesktop/locale1',
|
||||
_BUS_IFACE: 'org.freedesktop.locale1',
|
||||
_BUS_PROPS_IFACE: 'org.freedesktop.DBus.Properties',
|
||||
|
||||
_init: function() {
|
||||
this._layouts = '';
|
||||
this._variants = '';
|
||||
this._options = '';
|
||||
|
||||
this._reload();
|
||||
|
||||
Gio.DBus.system.signal_subscribe(this._BUS_NAME,
|
||||
this._BUS_PROPS_IFACE,
|
||||
'PropertiesChanged',
|
||||
this._BUS_PATH,
|
||||
null,
|
||||
Gio.DBusSignalFlags.NONE,
|
||||
Lang.bind(this, this._reload));
|
||||
},
|
||||
|
||||
_reload: function() {
|
||||
Gio.DBus.system.call(this._BUS_NAME,
|
||||
this._BUS_PATH,
|
||||
this._BUS_PROPS_IFACE,
|
||||
'GetAll',
|
||||
new GLib.Variant('(s)', [this._BUS_IFACE]),
|
||||
null, Gio.DBusCallFlags.NONE, -1, null,
|
||||
Lang.bind(this, function(conn, result) {
|
||||
let props;
|
||||
try {
|
||||
props = conn.call_finish(result).deep_unpack()[0];
|
||||
} catch(e) {
|
||||
log('Could not get properties from ' + this._BUS_NAME);
|
||||
return;
|
||||
}
|
||||
let layouts = props['X11Layout'].unpack();
|
||||
let variants = props['X11Variant'].unpack();
|
||||
let options = props['X11Options'].unpack();
|
||||
|
||||
if (layouts != this._layouts ||
|
||||
variants != this._variants) {
|
||||
this._layouts = layouts;
|
||||
this._variants = variants;
|
||||
this._emitInputSourcesChanged();
|
||||
}
|
||||
if (options != this._options) {
|
||||
this._options = options;
|
||||
this._emitKeyboardOptionsChanged();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
get inputSources() {
|
||||
let sourcesList = [];
|
||||
let layouts = this._layouts.split(',');
|
||||
let variants = this._variants.split(',');
|
||||
|
||||
for (let i = 0; i < layouts.length && !!layouts[i]; i++) {
|
||||
let id = layouts[i];
|
||||
if (!!variants[i])
|
||||
id += '+' + variants[i];
|
||||
sourcesList.push({ type: INPUT_SOURCE_TYPE_XKB, id: id });
|
||||
}
|
||||
return sourcesList;
|
||||
},
|
||||
|
||||
get keyboardOptions() {
|
||||
return this._options.split(',');
|
||||
}
|
||||
});
|
||||
|
||||
const InputSourceSessionSettings = new Lang.Class({
|
||||
Name: 'InputSourceSessionSettings',
|
||||
Extends: InputSourceSettings,
|
||||
|
||||
_DESKTOP_INPUT_SOURCES_SCHEMA: 'org.gnome.desktop.input-sources',
|
||||
_KEY_INPUT_SOURCES: 'sources',
|
||||
_KEY_MRU_SOURCES: 'mru-sources',
|
||||
_KEY_KEYBOARD_OPTIONS: 'xkb-options',
|
||||
_KEY_PER_WINDOW: 'per-window',
|
||||
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema_id: this._DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + this._KEY_INPUT_SOURCES, Lang.bind(this, this._emitInputSourcesChanged));
|
||||
this._settings.connect('changed::' + this._KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._emitKeyboardOptionsChanged));
|
||||
this._settings.connect('changed::' + this._KEY_PER_WINDOW, Lang.bind(this, this._emitPerWindowChanged));
|
||||
},
|
||||
|
||||
_getSourcesList: function(key) {
|
||||
let sourcesList = [];
|
||||
let sources = this._settings.get_value(key);
|
||||
let nSources = sources.n_children();
|
||||
|
||||
for (let i = 0; i < nSources; i++) {
|
||||
let [type, id] = sources.get_child_value(i).deep_unpack();
|
||||
sourcesList.push({ type: type, id: id });
|
||||
}
|
||||
return sourcesList;
|
||||
},
|
||||
|
||||
get inputSources() {
|
||||
return this._getSourcesList(this._KEY_INPUT_SOURCES);
|
||||
},
|
||||
|
||||
get mruSources() {
|
||||
return this._getSourcesList(this._KEY_MRU_SOURCES);
|
||||
},
|
||||
|
||||
set mruSources(sourcesList) {
|
||||
let sources = GLib.Variant.new('a(ss)', sourcesList);
|
||||
this._settings.set_value(this._KEY_MRU_SOURCES, sources);
|
||||
},
|
||||
|
||||
get keyboardOptions() {
|
||||
return this._settings.get_strv(this._KEY_KEYBOARD_OPTIONS);
|
||||
},
|
||||
|
||||
get perWindow() {
|
||||
return this._settings.get_boolean(this._KEY_PER_WINDOW);
|
||||
}
|
||||
});
|
||||
|
||||
const InputSourceManager = new Lang.Class({
|
||||
Name: 'InputSourceManager',
|
||||
|
||||
@ -155,11 +317,11 @@ const InputSourceManager = new Lang.Class({
|
||||
this._ibusSources = {};
|
||||
|
||||
this._currentSource = null;
|
||||
this._backupSource = null;
|
||||
|
||||
// All valid input sources currently in the gsettings
|
||||
// KEY_INPUT_SOURCES list ordered by most recently used
|
||||
this._mruSources = [];
|
||||
this._mruSourcesBackup = null;
|
||||
this._keybindingAction =
|
||||
Main.wm.addKeybinding('switch-input-source',
|
||||
new Gio.Settings({ schema_id: "org.gnome.desktop.wm.keybindings" }),
|
||||
@ -172,9 +334,12 @@ const InputSourceManager = new Lang.Class({
|
||||
Meta.KeyBindingFlags.IS_REVERSED,
|
||||
Shell.ActionMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._settings = new Gio.Settings({ schema_id: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
|
||||
this._settings.connect('changed::' + KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._keyboardOptionsChanged));
|
||||
if (Main.sessionMode.isGreeter)
|
||||
this._settings = new InputSourceSystemSettings();
|
||||
else
|
||||
this._settings = new InputSourceSessionSettings();
|
||||
this._settings.connect('input-sources-changed', Lang.bind(this, this._inputSourcesChanged));
|
||||
this._settings.connect('keyboard-options-changed', Lang.bind(this, this._keyboardOptionsChanged));
|
||||
|
||||
this._xkbInfo = KeyboardManager.getXkbInfo();
|
||||
this._keyboardManager = KeyboardManager.getKeyboardManager();
|
||||
@ -192,13 +357,13 @@ const InputSourceManager = new Lang.Class({
|
||||
this._focusWindowNotifyId = 0;
|
||||
this._overviewShowingId = 0;
|
||||
this._overviewHiddenId = 0;
|
||||
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
|
||||
this._settings.connect('per-window-changed', Lang.bind(this, this._sourcesPerWindowChanged));
|
||||
this._sourcesPerWindowChanged();
|
||||
this._disableIBus = false;
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
this._keyboardManager.setKeyboardOptions(this._settings.get_strv(KEY_KEYBOARD_OPTIONS));
|
||||
this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions);
|
||||
this._inputSourcesChanged();
|
||||
},
|
||||
|
||||
@ -229,7 +394,7 @@ const InputSourceManager = new Lang.Class({
|
||||
while (!(is = this._inputSources[nextIndex]))
|
||||
nextIndex += 1;
|
||||
|
||||
is.activate();
|
||||
is.activate(true);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -242,8 +407,7 @@ const InputSourceManager = new Lang.Class({
|
||||
// effect without considerable work to consolidate the usage
|
||||
// of pushModal/popModal and grabHelper. See
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
|
||||
if (Main.actionMode == Shell.ActionMode.MESSAGE_TRAY ||
|
||||
Main.actionMode == Shell.ActionMode.TOPBAR_POPUP) {
|
||||
if (Main.actionMode == Shell.ActionMode.POPUP) {
|
||||
this._modifiersSwitcher();
|
||||
return;
|
||||
}
|
||||
@ -254,10 +418,29 @@ const InputSourceManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_keyboardOptionsChanged: function() {
|
||||
this._keyboardManager.setKeyboardOptions(this._settings.get_strv(KEY_KEYBOARD_OPTIONS));
|
||||
this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions);
|
||||
this._keyboardManager.reapply();
|
||||
},
|
||||
|
||||
_updateMruSettings: function() {
|
||||
// If IBus is not ready we don't have a full picture of all
|
||||
// the available sources, so don't update the setting
|
||||
if (!this._ibusReady)
|
||||
return;
|
||||
|
||||
// If IBus is temporarily disabled, don't update the setting
|
||||
if (this._disableIBus)
|
||||
return;
|
||||
|
||||
let sourcesList = [];
|
||||
for (let i = 0; i < this._mruSources.length; ++i) {
|
||||
let source = this._mruSources[i];
|
||||
sourcesList.push([source.type, source.id]);
|
||||
}
|
||||
|
||||
this._settings.mruSources = sourcesList;
|
||||
},
|
||||
|
||||
_currentInputSourceChanged: function(newSource) {
|
||||
let oldSource;
|
||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||
@ -274,7 +457,7 @@ const InputSourceManager = new Lang.Class({
|
||||
this._changePerWindowSource();
|
||||
},
|
||||
|
||||
_activateInputSource: function(is) {
|
||||
_activateInputSource: function(is, interactive) {
|
||||
KeyboardManager.holdKeyboard();
|
||||
this._keyboardManager.apply(is.xkbId);
|
||||
|
||||
@ -292,11 +475,59 @@ const InputSourceManager = new Lang.Class({
|
||||
|
||||
this._ibusManager.setEngine(engine, KeyboardManager.releaseKeyboard);
|
||||
this._currentInputSourceChanged(is);
|
||||
|
||||
if (interactive)
|
||||
this._updateMruSettings();
|
||||
},
|
||||
|
||||
_updateMruSources: function() {
|
||||
let sourcesList = [];
|
||||
for (let i in this._inputSources)
|
||||
sourcesList.push(this._inputSources[i]);
|
||||
|
||||
this._keyboardManager.setUserLayouts(sourcesList.map(function(x) { return x.xkbId; }));
|
||||
|
||||
if (!this._disableIBus && this._mruSourcesBackup) {
|
||||
this._mruSources = this._mruSourcesBackup;
|
||||
this._mruSourcesBackup = null;
|
||||
}
|
||||
|
||||
// Initialize from settings when we have no MRU sources list
|
||||
if (this._mruSources.length == 0) {
|
||||
let mruSettings = this._settings.mruSources;
|
||||
for (let i = 0; i < mruSettings.length; i++) {
|
||||
let mruSettingSource = mruSettings[i];
|
||||
let mruSource = null;
|
||||
|
||||
for (let j = 0; j < sourcesList.length; j++) {
|
||||
let source = sourcesList[j];
|
||||
if (source.type == mruSettingSource.type &&
|
||||
source.id == mruSettingSource.id) {
|
||||
mruSource = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mruSource)
|
||||
this._mruSources.push(mruSource);
|
||||
}
|
||||
}
|
||||
|
||||
let mruSources = [];
|
||||
for (let i = 0; i < this._mruSources.length; i++) {
|
||||
for (let j = 0; j < sourcesList.length; j++)
|
||||
if (this._mruSources[i].type == sourcesList[j].type &&
|
||||
this._mruSources[i].id == sourcesList[j].id) {
|
||||
mruSources = mruSources.concat(sourcesList.splice(j, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._mruSources = mruSources.concat(sourcesList);
|
||||
},
|
||||
|
||||
_inputSourcesChanged: function() {
|
||||
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||
let nSources = sources.n_children();
|
||||
let sources = this._settings.inputSources;
|
||||
let nSources = sources.length;
|
||||
|
||||
this._inputSources = {};
|
||||
this._ibusSources = {};
|
||||
@ -305,7 +536,8 @@ const InputSourceManager = new Lang.Class({
|
||||
for (let i = 0; i < nSources; i++) {
|
||||
let displayName;
|
||||
let shortName;
|
||||
let [type, id] = sources.get_child_value(i).deep_unpack();
|
||||
let type = sources[i].type;
|
||||
let id = sources[i].id;
|
||||
let exists = false;
|
||||
|
||||
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||
@ -367,37 +599,10 @@ const InputSourceManager = new Lang.Class({
|
||||
|
||||
this.emit('sources-changed');
|
||||
|
||||
let sourcesList = [];
|
||||
for (let i in this._inputSources)
|
||||
sourcesList.push(this._inputSources[i]);
|
||||
this._updateMruSources();
|
||||
|
||||
this._keyboardManager.setUserLayouts(sourcesList.map(function(x) { return x.xkbId; }));
|
||||
|
||||
let mruSources = [];
|
||||
for (let i = 0; i < this._mruSources.length; i++) {
|
||||
for (let j = 0; j < sourcesList.length; j++)
|
||||
if (this._mruSources[i].type == sourcesList[j].type &&
|
||||
this._mruSources[i].id == sourcesList[j].id) {
|
||||
mruSources = mruSources.concat(sourcesList.splice(j, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._mruSources = mruSources.concat(sourcesList);
|
||||
|
||||
if (this._mruSources.length > 0) {
|
||||
if (!this._disableIBus && this._backupSource) {
|
||||
for (let i = 0; i < this._mruSources.length; i++) {
|
||||
if (this._mruSources[i].type == this._backupSource.type &&
|
||||
this._mruSources[i].id == this._backupSource.id) {
|
||||
let currentSource = this._mruSources.splice(i, 1);
|
||||
this._mruSources = currentSource.concat(this._mruSources);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._backupSource = null;
|
||||
}
|
||||
this._mruSources[0].activate();
|
||||
}
|
||||
if (this._mruSources.length > 0)
|
||||
this._mruSources[0].activate(false);
|
||||
|
||||
// All ibus engines are preloaded here to reduce the launching time
|
||||
// when users switch the input sources.
|
||||
@ -462,27 +667,30 @@ const InputSourceManager = new Lang.Class({
|
||||
if (this._disableIBus)
|
||||
return;
|
||||
this._disableIBus = true;
|
||||
this._backupSource = this._currentSource;
|
||||
this._mruSourcesBackup = this._mruSources.slice();
|
||||
} else {
|
||||
if (!this._disableIBus)
|
||||
return;
|
||||
this._disableIBus = false;
|
||||
}
|
||||
// If this._mruSources is not cleared before this.reload() is called,
|
||||
// the order is different from the original one as IM sources will
|
||||
// be appended to XKB sources.
|
||||
this._mruSources = [];
|
||||
this.reload();
|
||||
},
|
||||
|
||||
_getNewInputSource: function(current) {
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
if (is.type == current.type &&
|
||||
is.id == current.id)
|
||||
return is;
|
||||
let sourceIndexes = Object.keys(this._inputSources);
|
||||
if (sourceIndexes.length == 0)
|
||||
return null;
|
||||
|
||||
if (current) {
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
if (is.type == current.type &&
|
||||
is.id == current.id)
|
||||
return is;
|
||||
}
|
||||
}
|
||||
return this._currentSource;
|
||||
|
||||
return this._inputSources[sourceIndexes[0]];
|
||||
},
|
||||
|
||||
_getCurrentWindow: function() {
|
||||
@ -497,20 +705,17 @@ const InputSourceManager = new Lang.Class({
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (!window._inputSources) {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
} else if (window._inputSources == this._inputSources) {
|
||||
window._currentSource.activate();
|
||||
} else {
|
||||
if (window._inputSources != this._inputSources) {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._getNewInputSource(window._currentSource);
|
||||
window._currentSource.activate();
|
||||
}
|
||||
|
||||
if (window._currentSource)
|
||||
window._currentSource.activate(false);
|
||||
},
|
||||
|
||||
_sourcesPerWindowChanged: function() {
|
||||
this._sourcesPerWindow = this._settings.get_boolean('per-window');
|
||||
this._sourcesPerWindow = this._settings.perWindow;
|
||||
|
||||
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
|
||||
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
|
||||
@ -627,7 +832,10 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let is = this._inputSourceManager.inputSources[i];
|
||||
|
||||
let menuItem = new LayoutMenuItem(is.displayName, is.shortName);
|
||||
menuItem.connect('activate', Lang.bind(is, is.activate));
|
||||
menuItem.connect('activate', function() {
|
||||
is.activate(true);
|
||||
});
|
||||
|
||||
let indicatorLabel = new St.Label({ text: is.shortName,
|
||||
visible: false });
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
@ -7,12 +8,18 @@ const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const LOCATION_SCHEMA = 'org.gnome.system.location';
|
||||
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
|
||||
const ENABLED = 'enabled';
|
||||
|
||||
const APP_PERMISSIONS_TABLE = 'gnome';
|
||||
const APP_PERMISSIONS_ID = 'geolocation';
|
||||
|
||||
const GeoclueAccuracyLevel = {
|
||||
NONE: 0,
|
||||
COUNTRY: 1,
|
||||
@ -22,6 +29,15 @@ const GeoclueAccuracyLevel = {
|
||||
EXACT: 8
|
||||
};
|
||||
|
||||
function accuracyLevelToString(accuracyLevel) {
|
||||
for (let key in GeoclueAccuracyLevel) {
|
||||
if (GeoclueAccuracyLevel[key] == accuracyLevel)
|
||||
return key;
|
||||
}
|
||||
|
||||
return 'NONE';
|
||||
}
|
||||
|
||||
var GeoclueIface = '<node> \
|
||||
<interface name="org.freedesktop.GeoClue2.Manager"> \
|
||||
<property name="InUse" type="b" access="read"/> \
|
||||
@ -46,6 +62,26 @@ var AgentIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var PermissionStoreIface = '<node> \
|
||||
<interface name="org.freedesktop.impl.portal.PermissionStore"> \
|
||||
<method name="Lookup"> \
|
||||
<arg name="table" type="s" direction="in"/> \
|
||||
<arg name="id" type="s" direction="in"/> \
|
||||
<arg name="permissions" type="a{sas}" direction="out"/> \
|
||||
<arg name="data" type="v" direction="out"/> \
|
||||
</method> \
|
||||
<method name="Set"> \
|
||||
<arg name="table" type="s" direction="in"/> \
|
||||
<arg name="create" type="b" direction="in"/> \
|
||||
<arg name="id" type="s" direction="in"/> \
|
||||
<arg name="app_permissions" type="a{sas}" direction="in"/> \
|
||||
<arg name="data" type="v" direction="in"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PermissionStore = Gio.DBusProxy.makeProxyWrapper(PermissionStoreIface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LocationIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
@ -62,13 +98,13 @@ const Indicator = new Lang.Class({
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'find-location-symbolic';
|
||||
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true);
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem('', true);
|
||||
this._item.icon.icon_name = 'find-location-symbolic';
|
||||
|
||||
this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this);
|
||||
this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent');
|
||||
|
||||
this._item.status.text = _("Enabled");
|
||||
this._item.label.text = _("Location Enabled");
|
||||
this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction));
|
||||
this._item.menu.addSettingsAction(_("Privacy Settings"), 'gnome-privacy-panel.desktop');
|
||||
|
||||
@ -83,64 +119,66 @@ const Indicator = new Lang.Class({
|
||||
this._onSessionUpdated();
|
||||
this._onMaxAccuracyLevelChanged();
|
||||
this._connectToGeoclue();
|
||||
this._connectToPermissionStore();
|
||||
},
|
||||
|
||||
get MaxAccuracyLevel() {
|
||||
return this._getMaxAccuracyLevel();
|
||||
},
|
||||
|
||||
// We (and geoclue) have currently no way to reliably identifying apps so
|
||||
// for now, lets just authorize all apps as long as they provide a valid
|
||||
// desktop ID. We also ensure they don't get more accuracy than global max.
|
||||
AuthorizeApp: function(desktop_id, reqAccuracyLevel) {
|
||||
var appSystem = Shell.AppSystem.get_default();
|
||||
var app = appSystem.lookup_app(desktop_id + ".desktop");
|
||||
if (app == null) {
|
||||
return [false, 0];
|
||||
}
|
||||
AuthorizeAppAsync: function(params, invocation) {
|
||||
let [desktopId, reqAccuracyLevel] = params;
|
||||
|
||||
let allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._getMaxAccuracyLevel());
|
||||
return [true, allowedAccuracyLevel];
|
||||
let authorizer = new AppAuthorizer(desktopId,
|
||||
reqAccuracyLevel,
|
||||
this._permStoreProxy,
|
||||
this._getMaxAccuracyLevel());
|
||||
|
||||
authorizer.authorize(Lang.bind(this, function(accuracyLevel) {
|
||||
let ret = (accuracyLevel != GeoclueAccuracyLevel.NONE);
|
||||
invocation.return_value(GLib.Variant.new('(bu)',
|
||||
[ret, accuracyLevel]));
|
||||
}));
|
||||
},
|
||||
|
||||
_syncIndicator: function() {
|
||||
if (this._proxy == null) {
|
||||
if (this._managerProxy == null) {
|
||||
this._indicator.visible = false;
|
||||
this._item.actor.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._indicator.visible = this._proxy.InUse;
|
||||
this._indicator.visible = this._managerProxy.InUse;
|
||||
this._item.actor.visible = this._indicator.visible;
|
||||
this._updateMenuLabels();
|
||||
},
|
||||
|
||||
_connectToGeoclue: function() {
|
||||
if (this._proxy != null || this._connecting)
|
||||
if (this._managerProxy != null || this._connecting)
|
||||
return false;
|
||||
|
||||
this._connecting = true;
|
||||
new GeoclueManager(Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
'/org/freedesktop/GeoClue2/Manager',
|
||||
Lang.bind(this, this._onProxyReady));
|
||||
Lang.bind(this, this._onManagerProxyReady));
|
||||
return true;
|
||||
},
|
||||
|
||||
_onProxyReady: function(proxy, error) {
|
||||
_onManagerProxyReady: function(proxy, error) {
|
||||
if (error != null) {
|
||||
log(error.message);
|
||||
this._connecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._proxy = proxy;
|
||||
this._propertiesChangedId = this._proxy.connect('g-properties-changed',
|
||||
this._managerProxy = proxy;
|
||||
this._propertiesChangedId = this._managerProxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._onGeocluePropsChanged));
|
||||
|
||||
this._syncIndicator();
|
||||
|
||||
this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered));
|
||||
this._managerProxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered));
|
||||
},
|
||||
|
||||
_onAgentRegistered: function(result, error) {
|
||||
@ -153,10 +191,10 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_onGeoclueVanished: function() {
|
||||
if (this._propertiesChangedId) {
|
||||
this._proxy.disconnect(this._propertiesChangedId);
|
||||
this._managerProxy.disconnect(this._propertiesChangedId);
|
||||
this._propertiesChangedId = 0;
|
||||
}
|
||||
this._proxy = null;
|
||||
this._managerProxy = null;
|
||||
|
||||
this._syncIndicator();
|
||||
},
|
||||
@ -173,10 +211,11 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_updateMenuLabels: function() {
|
||||
if (this._settings.get_boolean(ENABLED)) {
|
||||
this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled");
|
||||
this._item.label.text = this._indicator.visible ? _("Location In Use")
|
||||
: _("Location Enabled");
|
||||
this._onOffAction.label.text = _("Disable");
|
||||
} else {
|
||||
this._item.status.text = _("Disabled");
|
||||
this._item.label.text = _("Location Disabled");
|
||||
this._onOffAction.label.text = _("Enable");
|
||||
}
|
||||
},
|
||||
@ -210,9 +249,206 @@ const Indicator = new Lang.Class({
|
||||
let unpacked = properties.deep_unpack();
|
||||
if ("InUse" in unpacked)
|
||||
this._syncIndicator();
|
||||
}
|
||||
},
|
||||
|
||||
_connectToPermissionStore: function() {
|
||||
this._permStoreProxy = null;
|
||||
new PermissionStore(Gio.DBus.session,
|
||||
'org.freedesktop.impl.portal.PermissionStore',
|
||||
'/org/freedesktop/impl/portal/PermissionStore',
|
||||
Lang.bind(this, this._onPermStoreProxyReady));
|
||||
},
|
||||
|
||||
_onPermStoreProxyReady: function(proxy, error) {
|
||||
if (error != null) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this._permStoreProxy = proxy;
|
||||
},
|
||||
});
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
const AppAuthorizer = new Lang.Class({
|
||||
Name: 'LocationAppAuthorizer',
|
||||
|
||||
_init: function(desktopId,
|
||||
reqAccuracyLevel,
|
||||
permStoreProxy,
|
||||
maxAccuracyLevel) {
|
||||
this.desktopId = desktopId;
|
||||
this.reqAccuracyLevel = reqAccuracyLevel;
|
||||
this._permStoreProxy = permStoreProxy;
|
||||
this._maxAccuracyLevel = maxAccuracyLevel;
|
||||
|
||||
this._accuracyLevel = GeoclueAccuracyLevel.NONE;
|
||||
},
|
||||
|
||||
authorize: function(onAuthDone) {
|
||||
this._onAuthDone = onAuthDone;
|
||||
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
this._app = appSystem.lookup_app(this.desktopId + ".desktop");
|
||||
if (this._app == null || this._permStoreProxy == null) {
|
||||
this._completeAuth();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._permStoreProxy.LookupRemote(APP_PERMISSIONS_TABLE,
|
||||
APP_PERMISSIONS_ID,
|
||||
Lang.bind(this,
|
||||
this._onPermLookupDone));
|
||||
},
|
||||
|
||||
_onPermLookupDone: function(result, error) {
|
||||
if (error != null) {
|
||||
if (error.domain == Gio.DBusError) {
|
||||
// Likely no xdg-app installed, just authorize the app
|
||||
this._accuracyLevel = this.reqAccuracyLevel;
|
||||
this._permStoreProxy = null;
|
||||
this._completeAuth();
|
||||
} else {
|
||||
// Currently xdg-app throws an error if we lookup for
|
||||
// unknown ID (which would be the case first time this code
|
||||
// runs) so we continue with user authorization as normal
|
||||
// and ID is added to the store if user says "yes".
|
||||
log(error.message);
|
||||
this._permissions = {};
|
||||
this._userAuthorizeApp();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[this._permissions] = result;
|
||||
let permission = this._permissions[this.desktopId];
|
||||
|
||||
if (permission == null) {
|
||||
this._userAuthorizeApp();
|
||||
} else {
|
||||
let [levelStr] = permission || ['NONE'];
|
||||
this._accuracyLevel = GeoclueAccuracyLevel[levelStr] ||
|
||||
GeoclueAccuracyLevel.NONE;
|
||||
this._completeAuth();
|
||||
}
|
||||
},
|
||||
|
||||
_userAuthorizeApp: function() {
|
||||
let name = this._app.get_name();
|
||||
let appInfo = this._app.get_app_info();
|
||||
let reason = appInfo.get_string("X-Geoclue-Reason");
|
||||
|
||||
this._showAppAuthDialog(name, reason);
|
||||
},
|
||||
|
||||
_showAppAuthDialog: function(name, reason) {
|
||||
this._dialog = new GeolocationDialog(name,
|
||||
reason,
|
||||
this.reqAccuracyLevel);
|
||||
|
||||
let responseId = this._dialog.connect('response', Lang.bind(this,
|
||||
function(dialog, level) {
|
||||
this._dialog.disconnect(responseId);
|
||||
this._accuracyLevel = level;
|
||||
this._completeAuth();
|
||||
}));
|
||||
|
||||
this._dialog.open();
|
||||
},
|
||||
|
||||
_completeAuth: function() {
|
||||
if (this._accuracyLevel != GeoclueAccuracyLevel.NONE) {
|
||||
this._accuracyLevel = clamp(this._accuracyLevel,
|
||||
0,
|
||||
this._maxAccuracyLevel);
|
||||
}
|
||||
this._saveToPermissionStore();
|
||||
|
||||
this._onAuthDone(this._accuracyLevel);
|
||||
},
|
||||
|
||||
_saveToPermissionStore: function() {
|
||||
if (this._permStoreProxy == null)
|
||||
return;
|
||||
|
||||
let levelStr = accuracyLevelToString(this._accuracyLevel);
|
||||
let dateStr = Math.round(Date.now() / 1000).toString();
|
||||
this._permissions[this.desktopId] = [levelStr, dateStr];
|
||||
|
||||
let data = GLib.Variant.new('av', {});
|
||||
|
||||
this._permStoreProxy.SetRemote(APP_PERMISSIONS_TABLE,
|
||||
true,
|
||||
APP_PERMISSIONS_ID,
|
||||
this._permissions,
|
||||
data,
|
||||
function (result, error) {
|
||||
if (error != null)
|
||||
log(error.message);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const GeolocationDialog = new Lang.Class({
|
||||
Name: 'GeolocationDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(name, reason, reqAccuracyLevel) {
|
||||
this.parent({ styleClass: 'geolocation-dialog' });
|
||||
this.reqAccuracyLevel = reqAccuracyLevel;
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'geolocation-dialog-main-layout' });
|
||||
this.contentLayout.add_actor(mainContentBox);
|
||||
|
||||
let icon = new St.Icon({ style_class: 'geolocation-dialog-icon',
|
||||
icon_name: 'find-location-symbolic',
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
mainContentBox.add_actor(icon);
|
||||
|
||||
let messageBox = new St.BoxLayout({ style_class: 'geolocation-dialog-content',
|
||||
vertical: true });
|
||||
mainContentBox.add_actor(messageBox);
|
||||
|
||||
this._title = new St.Label({ style_class: 'geolocation-dialog-title headline' });
|
||||
messageBox.add_actor(this._title);
|
||||
|
||||
this._reason = new St.Label({ style_class: 'geolocation-dialog-reason' });
|
||||
messageBox.add_actor(this._reason);
|
||||
|
||||
this._privacyNote = new St.Label();
|
||||
messageBox.add_actor(this._privacyNote);
|
||||
|
||||
let button = this.addButton({ label: _("Deny Access"),
|
||||
action: Lang.bind(this, this._onDenyClicked),
|
||||
key: Clutter.KEY_Escape });
|
||||
this.addButton({ label: _("Grant Access"),
|
||||
action: Lang.bind(this, this._onGrantClicked) });
|
||||
|
||||
this.setInitialKeyFocus(button);
|
||||
|
||||
/* Translators: %s is an application name */
|
||||
this._title.text = _("Give %s access to your location?").format(name);
|
||||
|
||||
this._privacyNote.text = _("Location access can be changed at any time from the privacy settings.");
|
||||
|
||||
if (reason)
|
||||
this._reason.text = reason;
|
||||
this._reason.visible = (reason != null);
|
||||
},
|
||||
|
||||
_onGrantClicked: function() {
|
||||
this.emit('response', this.reqAccuracyLevel);
|
||||
this.close();
|
||||
},
|
||||
|
||||
_onDenyClicked: function() {
|
||||
this.emit('response', GeoclueAccuracyLevel.NONE);
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(GeolocationDialog.prototype);
|
||||
|
@ -5,6 +5,7 @@ const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
const NMGtk = imports.gi.NMGtk;
|
||||
@ -257,16 +258,8 @@ const NMConnectionSection = new Lang.Class({
|
||||
this._radioSection.actor.visible = (nItems > 1);
|
||||
this._labelSection.actor.visible = (nItems == 1);
|
||||
|
||||
this.item.status.text = this._getStatus();
|
||||
this.item.label.text = this._getStatus();
|
||||
this.item.icon.icon_name = this._getMenuIcon();
|
||||
|
||||
// desc can be undefined at cold-plug, before we called
|
||||
// NMGtk.disambiguate_device_names() at least once
|
||||
let desc = this._getDescription();
|
||||
if (desc)
|
||||
this.item.label.text = desc;
|
||||
else
|
||||
this.item.label.text = '';
|
||||
},
|
||||
|
||||
_getMenuIcon: function() {
|
||||
@ -355,6 +348,7 @@ const NMConnectionDevice = new Lang.Class({
|
||||
this.parent(client);
|
||||
this._device = device;
|
||||
this._settings = settings;
|
||||
this._description = '';
|
||||
|
||||
this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect));
|
||||
this._deactivateItem = this._radioSection.addAction(_("Turn Off"), Lang.bind(this, this.deactivateConnection));
|
||||
@ -454,38 +448,44 @@ const NMConnectionDevice = new Lang.Class({
|
||||
|
||||
switch(this._device.state) {
|
||||
case NetworkManager.DeviceState.DISCONNECTED:
|
||||
return _("Off");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Off").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.ACTIVATED:
|
||||
return _("Connected");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Connected").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.UNMANAGED:
|
||||
/* Translators: this is for network devices that are physically present but are not
|
||||
under NetworkManager's control (and thus cannot be used in the menu) */
|
||||
return _("Unmanaged");
|
||||
under NetworkManager's control (and thus cannot be used in the menu);
|
||||
%s is a network identifier */
|
||||
return _("%s Unmanaged").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.DEACTIVATING:
|
||||
return _("Disconnecting");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Disconnecting").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.PREPARE:
|
||||
case NetworkManager.DeviceState.CONFIG:
|
||||
case NetworkManager.DeviceState.IP_CONFIG:
|
||||
case NetworkManager.DeviceState.IP_CHECK:
|
||||
case NetworkManager.DeviceState.SECONDARIES:
|
||||
return _("Connecting");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Connecting").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.NEED_AUTH:
|
||||
/* Translators: this is for network connections that require some kind of key or password */
|
||||
return _("Authentication required");
|
||||
/* Translators: this is for network connections that require some kind of key or password; %s is a network identifier */
|
||||
return _("%s Requires Authentication").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.UNAVAILABLE:
|
||||
// This state is actually a compound of various states (generically unavailable,
|
||||
// firmware missing), that are exposed by different properties (whose state may
|
||||
// or may not updated when we receive state-changed).
|
||||
if (this._device.firmware_missing) {
|
||||
/* Translators: this is for devices that require some kind of firmware or kernel
|
||||
module, which is missing */
|
||||
return _("Firmware missing");
|
||||
module, which is missing; %s is a network identifier */
|
||||
return _("Firmware Missing For %s").format(this._getDescription());
|
||||
}
|
||||
/* Translators: this is for a network device that cannot be activated (for example it
|
||||
is disabled by rfkill, or it has no coverage */
|
||||
return _("Unavailable");
|
||||
is disabled by rfkill, or it has no coverage; %s is a network identifier */
|
||||
return _("%s Unavailable").format(this._getDescription());
|
||||
case NetworkManager.DeviceState.FAILED:
|
||||
return _("Connection failed");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Connection Failed").format(this._getDescription());
|
||||
default:
|
||||
log('Device state invalid, is %d'.format(this._device.state));
|
||||
return 'invalid';
|
||||
@ -585,11 +585,12 @@ const NMDeviceModem = new Lang.Class({
|
||||
|
||||
_getStatus: function() {
|
||||
if (!this._client.wwan_hardware_enabled)
|
||||
return _("Hardware Disabled");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Hardware Disabled").format(this._getDescription());
|
||||
else if (!this._client.wwan_enabled)
|
||||
/* Translators: this is for a network device that cannot be activated
|
||||
because it's disabled by rfkill (airplane mode) */
|
||||
return _("Disabled");
|
||||
because it's disabled by rfkill (airplane mode); %s is a network identifier */
|
||||
return _("%s Disabled").format(this._getDescription());
|
||||
else if (this._device.state == NetworkManager.DeviceState.ACTIVATED &&
|
||||
this._mobileDevice && this._mobileDevice.operator_name)
|
||||
return this._mobileDevice.operator_name;
|
||||
@ -621,7 +622,7 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
_init: function(client, device, settings) {
|
||||
this.parent(client, device, settings);
|
||||
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Bluetooth Settings"), device));
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
@ -629,7 +630,7 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
},
|
||||
|
||||
getConnectLabel: function() {
|
||||
return _("Use as Internet connection");
|
||||
return _("Connect to Internet");
|
||||
},
|
||||
|
||||
getIndicatorIcon: function() {
|
||||
@ -752,10 +753,9 @@ const NMWirelessDialog = new Lang.Class({
|
||||
this._updateSensitivity();
|
||||
this._syncView();
|
||||
|
||||
if (accessPoints.length == 0) {
|
||||
/* If there are no visible access points, request a scan */
|
||||
this._device.request_scan_simple(null);
|
||||
}
|
||||
this._scanTimeoutId = Mainloop.timeout_add_seconds(15, Lang.bind(this, this._onScanTimeout));
|
||||
GLib.Source.set_name_by_id(this._scanTimeoutId, '[gnome-shell] this._onScanTimeout');
|
||||
this._onScanTimeout();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -780,9 +780,19 @@ const NMWirelessDialog = new Lang.Class({
|
||||
this._airplaneModeChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._scanTimeoutId) {
|
||||
Mainloop.source_remove(this._scanTimeoutId);
|
||||
this._scanTimeoutId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_onScanTimeout: function() {
|
||||
this._device.request_scan_simple(null);
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
},
|
||||
|
||||
_activeApChanged: function() {
|
||||
if (this._activeNetwork)
|
||||
this._activeNetwork.item.setActive(false);
|
||||
@ -877,7 +887,7 @@ const NMWirelessDialog = new Lang.Class({
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
|
||||
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
|
||||
this._noNetworksSpinner = new Animation.AnimatedIcon(file, 24, 24);
|
||||
this._noNetworksSpinner = new Animation.AnimatedIcon(file, 16, 16);
|
||||
this._noNetworksBox.add_actor(this._noNetworksSpinner.actor);
|
||||
this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label',
|
||||
text: _("No Networks") }));
|
||||
@ -917,10 +927,7 @@ const NMWirelessDialog = new Lang.Class({
|
||||
key: Clutter.Escape });
|
||||
this._connectButton = this.addButton({ action: Lang.bind(this, this._connect),
|
||||
label: _("Connect"),
|
||||
key: Clutter.Return },
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
x_align: St.Align.END });
|
||||
key: Clutter.Return });
|
||||
},
|
||||
|
||||
_connect: function() {
|
||||
@ -1282,9 +1289,8 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On");
|
||||
this._toggleItem.actor.visible = this._client.wireless_hardware_enabled;
|
||||
|
||||
this.item.status.text = this._getStatus();
|
||||
this.item.icon.icon_name = this._getMenuIcon();
|
||||
this.item.label.text = this._description;
|
||||
this.item.label.text = this._getStatus();
|
||||
},
|
||||
|
||||
setDeviceDescription: function(desc) {
|
||||
@ -1296,18 +1302,23 @@ const NMDeviceWireless = new Lang.Class({
|
||||
let ap = this._device.active_access_point;
|
||||
|
||||
if (this._isHotSpotMaster())
|
||||
return _("Hotspot Active");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Hotspot Active").format(this._description);
|
||||
else if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
|
||||
this._device.state < NetworkManager.DeviceState.ACTIVATED)
|
||||
return _("Connecting");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Connecting").format(this._description);
|
||||
else if (ap)
|
||||
return ssidToLabel(ap.get_ssid());
|
||||
else if (!this._client.wireless_hardware_enabled)
|
||||
return _("Hardware Disabled");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Hardware Disabled").format(this._description);
|
||||
else if (!this._client.wireless_enabled)
|
||||
return _("Off");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Off").format(this._description);
|
||||
else if (this._device.state == NetworkManager.DeviceState.DISCONNECTED)
|
||||
return _("Not Connected");
|
||||
/* Translators: %s is a network identifier */
|
||||
return _("%s Not Connected").format(this._description);
|
||||
else
|
||||
return '';
|
||||
},
|
||||
@ -1509,7 +1520,7 @@ const NMVPNSection = new Lang.Class({
|
||||
return item.getName();
|
||||
}
|
||||
|
||||
return _("Off");
|
||||
return _("VPN Off");
|
||||
},
|
||||
|
||||
_getMenuIcon: function() {
|
||||
@ -1631,6 +1642,8 @@ const NMApplet = new Lang.Class({
|
||||
this._readConnections();
|
||||
this._readDevices();
|
||||
this._syncNMState();
|
||||
this._syncMainConnection();
|
||||
this._syncVPNConnections();
|
||||
|
||||
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
|
||||
@ -2007,13 +2020,24 @@ const NMApplet = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateIcon: function() {
|
||||
if (!this._client.networking_enabled || !this._mainConnection) {
|
||||
if (!this._client.networking_enabled) {
|
||||
this._primaryIndicator.visible = false;
|
||||
} else {
|
||||
let dev = this._mainConnection._primaryDevice;
|
||||
this._primaryIndicator.visible = (dev != null);
|
||||
if (dev)
|
||||
let dev = null;
|
||||
if (this._mainConnection)
|
||||
dev = this._mainConnection._primaryDevice;
|
||||
|
||||
let state = this._client.get_state();
|
||||
let connected = state == NetworkManager.State.CONNECTED_GLOBAL;
|
||||
this._primaryIndicator.visible = (dev != null) || connected;
|
||||
if (dev) {
|
||||
this._primaryIndicator.icon_name = dev.getIndicatorIcon();
|
||||
} else if (connected) {
|
||||
if (this._client.connectivity == NetworkManager.ConnectivityState.FULL)
|
||||
this._primaryIndicator.icon_name = 'network-wired-symbolic';
|
||||
else
|
||||
this._primaryIndicator.icon_name = 'network-wired-no-route-symbolic';
|
||||
}
|
||||
}
|
||||
|
||||
this._vpnIndicator.icon_name = this._vpnSection.getIndicatorIcon();
|
||||
|
@ -1,6 +1,8 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Lang = imports.lang;
|
||||
const UPower = imports.gi.UPowerGlib;
|
||||
|
||||
@ -25,6 +27,8 @@ const DisplayDeviceInterface = '<node> \
|
||||
|
||||
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface);
|
||||
|
||||
const SHOW_BATTERY_PERCENTAGE = 'show-battery-percentage';
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'PowerIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
@ -32,7 +36,15 @@ const Indicator = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed::' + SHOW_BATTERY_PERCENTAGE,
|
||||
Lang.bind(this, this._sync));
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._percentageLabel = new St.Label({ y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.indicators.add(this._percentageLabel, { expand: true, y_fill: true });
|
||||
this.indicators.add_style_class_name('power-status');
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
@ -83,12 +95,12 @@ const Indicator = new Lang.Class({
|
||||
|
||||
if (this._proxy.State == UPower.DeviceState.DISCHARGING) {
|
||||
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
|
||||
return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, this._proxy.Percentage);
|
||||
return _("%d\u2236%02d Remaining (%d\u2009%%)").format(hours, minutes, this._proxy.Percentage);
|
||||
}
|
||||
|
||||
if (this._proxy.State == UPower.DeviceState.CHARGING) {
|
||||
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
|
||||
return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, this._proxy.Percentage);
|
||||
return _("%d\u2236%02d Until Full (%d\u2009%%)").format(hours, minutes, this._proxy.Percentage);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -99,10 +111,12 @@ const Indicator = new Lang.Class({
|
||||
let visible = this._proxy.IsPresent;
|
||||
if (visible) {
|
||||
this._item.actor.show();
|
||||
this._percentageLabel.visible = this._desktopSettings.get_boolean(SHOW_BATTERY_PERCENTAGE);
|
||||
} else {
|
||||
// If there's no battery, then we use the power icon.
|
||||
this._item.actor.hide();
|
||||
this._indicator.icon_name = 'system-shutdown-symbolic';
|
||||
this._percentageLabel.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -111,13 +125,15 @@ const Indicator = new Lang.Class({
|
||||
this._indicator.icon_name = icon;
|
||||
this._item.icon.icon_name = icon;
|
||||
|
||||
// The status label
|
||||
this._item.status.text = this._getStatus();
|
||||
|
||||
// The sub-menu heading
|
||||
if (this._proxy.Type == UPower.DeviceKind.UPS)
|
||||
this._item.label.text = _("UPS");
|
||||
// The icon label
|
||||
let label
|
||||
if (this._proxy.State == UPower.DeviceState.FULLY_CHARGED)
|
||||
label = _("%d\u2009%%").format(100);
|
||||
else
|
||||
this._item.label.text = _("Battery");
|
||||
label = _("%d\u2009%%").format(this._proxy.Percentage);
|
||||
this._percentageLabel.clutter_text.set_markup('<span size="smaller">' + label + '</span>');
|
||||
|
||||
// The status label
|
||||
this._item.label.text = this._getStatus();
|
||||
},
|
||||
});
|
||||
|
@ -85,9 +85,8 @@ const Indicator = new Lang.Class({
|
||||
// The menu only appears when airplane mode is on, so just
|
||||
// statically build it as if it was on, rather than dynamically
|
||||
// changing the menu contents.
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true);
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode On"), true);
|
||||
this._item.icon.icon_name = 'airplane-mode-symbolic';
|
||||
this._item.status.text = _("On");
|
||||
this._offItem = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
|
||||
this._manager.airplaneMode = false;
|
||||
}));
|
||||
|
@ -6,6 +6,7 @@ const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
@ -17,15 +18,24 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const DISABLE_RESTART_KEY = 'disable-restart-buttons';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
|
||||
const SENSOR_BUS_NAME = 'net.hadess.SensorProxy';
|
||||
const SENSOR_OBJECT_PATH = '/net/hadess/SensorProxy';
|
||||
|
||||
const SensorProxyInterface = '<node> \
|
||||
<interface name="net.hadess.SensorProxy"> \
|
||||
<property name="HasAccelerometer" type="b" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const SensorProxy = Gio.DBusProxy.makeProxyWrapper(SensorProxyInterface);
|
||||
|
||||
const AltSwitcher = new Lang.Class({
|
||||
Name: 'AltSwitcher',
|
||||
|
||||
@ -95,14 +105,13 @@ const Indicator = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._screenSaverSettings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA });
|
||||
this._loginScreenSettings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
|
||||
this._privacySettings = new Gio.Settings({ schema_id: PRIVACY_SCHEMA });
|
||||
this._orientationSettings = new Gio.Settings({ schema_id: 'org.gnome.settings-daemon.peripherals.touchscreen' });
|
||||
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._monitorManager = Meta.MonitorManager.get();
|
||||
this._haveShutdown = true;
|
||||
this._haveSuspend = true;
|
||||
|
||||
@ -129,6 +138,7 @@ const Indicator = new Lang.Class({
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._updateSwitchUser();
|
||||
this._updateMultiUser();
|
||||
this._updateLockScreen();
|
||||
|
||||
// Whether shutdown is available or not depends on both lockdown
|
||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||
@ -147,23 +157,34 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._orientationSettings.connect('changed::orientation-lock',
|
||||
Lang.bind(this, this._updateOrientationLock));
|
||||
this._orientationExists = false;
|
||||
Gio.DBus.session.watch_name('org.gnome.SettingsDaemon.Orientation',
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
Lang.bind(this, function() {
|
||||
this._orientationExists = true;
|
||||
this._updateOrientationLock();
|
||||
}),
|
||||
Lang.bind(this, function() {
|
||||
this._orientationExists = false;
|
||||
this._updateOrientationLock();
|
||||
}));
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._updateOrientationLock));
|
||||
Gio.DBus.system.watch_name(SENSOR_BUS_NAME,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
Lang.bind(this, this._sensorProxyAppeared),
|
||||
Lang.bind(this, function() {
|
||||
this._sensorProxy = null;
|
||||
this._updateOrientationLock();
|
||||
}));
|
||||
this._updateOrientationLock();
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sensorProxyAppeared: function() {
|
||||
this._sensorProxy = new SensorProxy(Gio.DBus.system, SENSOR_BUS_NAME, SENSOR_OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._sensorProxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._updateOrientationLock));
|
||||
this._updateOrientationLock();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateActionsVisibility: function() {
|
||||
let visible = (this._settingsAction.visible ||
|
||||
this._orientationLockAction.visible ||
|
||||
@ -234,13 +255,23 @@ const Indicator = new Lang.Class({
|
||||
let file = Gio.File.new_for_path(iconFile);
|
||||
let gicon = new Gio.FileIcon({ file: file });
|
||||
this._switchUserSubMenu.icon.gicon = gicon;
|
||||
|
||||
this._switchUserSubMenu.icon.add_style_class_name('user-icon');
|
||||
this._switchUserSubMenu.icon.remove_style_class_name('default-icon');
|
||||
} else {
|
||||
this._switchUserSubMenu.icon.icon_name = 'avatar-default-symbolic';
|
||||
|
||||
this._switchUserSubMenu.icon.add_style_class_name('default-icon');
|
||||
this._switchUserSubMenu.icon.remove_style_class_name('user-icon');
|
||||
}
|
||||
},
|
||||
|
||||
_updateOrientationLock: function() {
|
||||
this._orientationLockAction.visible = this._orientationExists;
|
||||
if (this._sensorProxy)
|
||||
this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer &&
|
||||
this._monitorManager.get_is_builtin_display_on();
|
||||
else
|
||||
this._orientationLockAction.visible = false;
|
||||
|
||||
let locked = this._orientationSettings.get_boolean('orientation-lock');
|
||||
let icon = this._orientationLockAction.child;
|
||||
@ -275,14 +306,17 @@ const Indicator = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._updateSuspend();
|
||||
}));
|
||||
this._loginManager.canSuspend(Lang.bind(this,
|
||||
function(canSuspend, needsAuth) {
|
||||
this._haveSuspend = canSuspend;
|
||||
this._suspendNeedsAuth = needsAuth;
|
||||
this._updateSuspend();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspend: function() {
|
||||
let disabled = Main.sessionMode.isLocked ||
|
||||
let disabled = (Main.sessionMode.isLocked &&
|
||||
this._suspendNeedsAuth) ||
|
||||
(Main.sessionMode.isGreeter &&
|
||||
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
|
||||
this._suspendAction.visible = this._haveSuspend && !disabled;
|
||||
@ -324,6 +358,9 @@ const Indicator = new Lang.Class({
|
||||
this._switchUserSubMenu.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
this._switchUserSubMenu.menu.addSettingsAction(_("Account Settings"),
|
||||
'gnome-user-accounts-panel.desktop');
|
||||
|
||||
this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu));
|
||||
this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu));
|
||||
|
||||
|
@ -6,6 +6,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
@ -117,6 +118,7 @@ const UserWidgetLabel = new Lang.Class({
|
||||
this._currentLabel = this._realNameLabel;
|
||||
else
|
||||
this._currentLabel = this._userNameLabel;
|
||||
this.label_actor = this._currentLabel;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = 0;
|
||||
@ -158,6 +160,9 @@ const UserWidget = new Lang.Class({
|
||||
this._label = new UserWidgetLabel(user);
|
||||
this.actor.add_child(this._label);
|
||||
|
||||
this._label.bind_property('label-actor', this.actor, 'label-actor',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUser));
|
||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUser));
|
||||
this._updateUser();
|
||||
|
@ -152,11 +152,11 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
|
||||
_("Windows"), 'emblem-documents-symbolic');
|
||||
_("Windows"), 'focus-windows-symbolic');
|
||||
|
||||
this.appDisplay = new AppDisplay.AppDisplay();
|
||||
this._appsPage = this._addPage(this.appDisplay.actor,
|
||||
_("Applications"), 'view-grid-symbolic');
|
||||
_("Applications"), 'view-app-grid-symbolic');
|
||||
|
||||
this._searchResults = new Search.SearchResults();
|
||||
this._searchPage = this._addPage(this._searchResults.actor,
|
||||
@ -214,10 +214,13 @@ const ViewSelector = new Lang.Class({
|
||||
Shell.ActionMode.OVERVIEW,
|
||||
Lang.bind(Main.overview, Main.overview.toggle));
|
||||
|
||||
let gesture;
|
||||
|
||||
gesture = new EdgeDragAction.EdgeDragAction(St.Side.LEFT,
|
||||
Shell.ActionMode.NORMAL);
|
||||
let side;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
side = St.Side.RIGHT;
|
||||
else
|
||||
side = St.Side.LEFT;
|
||||
let gesture = new EdgeDragAction.EdgeDragAction(side,
|
||||
Shell.ActionMode.NORMAL);
|
||||
gesture.connect('activated', Lang.bind(this, function() {
|
||||
if (Main.overview.visible)
|
||||
Main.overview.hide();
|
||||
@ -462,6 +465,12 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_shouldTriggerSearch: function(symbol) {
|
||||
if (symbol == Clutter.Multi_key)
|
||||
return true;
|
||||
|
||||
if (symbol == Clutter.BackSpace && this._searchActive)
|
||||
return true;
|
||||
|
||||
let unicode = Clutter.keysym_to_unicode(symbol);
|
||||
if (unicode == 0)
|
||||
return false;
|
||||
@ -469,7 +478,7 @@ const ViewSelector = new Lang.Class({
|
||||
if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0)
|
||||
return true;
|
||||
|
||||
return symbol == Clutter.BackSpace && this._searchActive;
|
||||
return false;
|
||||
},
|
||||
|
||||
startSearch: function(event) {
|
||||
@ -552,7 +561,9 @@ const ViewSelector = new Lang.Class({
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
let source = event.get_source();
|
||||
if (source != this._text && this._text.text == '' &&
|
||||
if (source != this._text &&
|
||||
this._text.text == '' &&
|
||||
!this._text.has_preedit () &&
|
||||
!Main.layoutManager.keyboardBox.contains(source)) {
|
||||
// the user clicked outside after activating the entry, but
|
||||
// with no search term entered and no keyboard button pressed
|
||||
|
@ -11,7 +11,8 @@ const WindowAttentionHandler = new Lang.Class({
|
||||
|
||||
_init : function() {
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
||||
this._windowDemandsAttentionId = global.display.connect('window-demands-attention',
|
||||
Lang.bind(this, this._onWindowDemandsAttention));
|
||||
},
|
||||
|
||||
_getTitleAndBanner: function(app, window) {
|
||||
@ -39,7 +40,7 @@ const WindowAttentionHandler = new Lang.Class({
|
||||
let [title, banner] = this._getTitleAndBanner(app, window);
|
||||
|
||||
let notification = new MessageTray.Notification(source, title, banner);
|
||||
notification.connect('clicked', function() {
|
||||
notification.connect('activated', function() {
|
||||
source.open();
|
||||
});
|
||||
notification.setForFeedback(true);
|
||||
|