Compare commits
1967 Commits
Author | SHA1 | Date | |
---|---|---|---|
2698d0db29 | |||
2fa7230133 | |||
658243eae4 | |||
7282bc8859 | |||
07511cb21a | |||
f71f767a3c | |||
176daa1469 | |||
5487f8cf93 | |||
944762ac83 | |||
c66068c435 | |||
d479c936b8 | |||
c5a3f784dd | |||
529d012865 | |||
8a1b83417a | |||
6c64c0873c | |||
d2c6149923 | |||
9f98f6f02d | |||
2f7c0b1985 | |||
f61548234a | |||
6eef830ba4 | |||
88a9b7648c | |||
b7678493f9 | |||
a9ec8a354a | |||
a42d35deab | |||
2008feb7da | |||
c41424b57b | |||
3bd5563a7e | |||
eff8ec00c4 | |||
1256af7b9a | |||
b38ecaf925 | |||
bd5d945fb2 | |||
15063ef3d5 | |||
3b8a125732 | |||
9860b1c677 | |||
a0e340f06e | |||
57d3bec95c | |||
be10f3c2b5 | |||
f7af96dbb2 | |||
468a855f04 | |||
429f9e1d15 | |||
5de91197ae | |||
bc6b4b01ea | |||
e89ce7358a | |||
bdf0a6fd0b | |||
783abd4f5f | |||
28aa9201f0 | |||
378df515d0 | |||
04d68c6e36 | |||
5308d12239 | |||
15cac0157c | |||
fc696bc054 | |||
5a7b7b7389 | |||
9c9cf6f5ab | |||
0155739bef | |||
d0902fa28b | |||
3aa0d455c9 | |||
835d4e4b58 | |||
6075332f2f | |||
c5d8484e19 | |||
575ab0d609 | |||
b48a7d5cfd | |||
9af107feff | |||
70b5db16d3 | |||
5072ea7e47 | |||
13b83c95b7 | |||
e48dbe65f1 | |||
14fb51e6d7 | |||
b42af9aa99 | |||
ca2ee22827 | |||
13a2b28bf1 | |||
04bf75d137 | |||
76c589702f | |||
c11f89e72a | |||
cfacb05461 | |||
c4ba31cb23 | |||
db0eacddc5 | |||
62972f5138 | |||
6fe263f19f | |||
b6a87acb71 | |||
784b04b191 | |||
3af9f636af | |||
16c0585b95 | |||
fc9a96ac85 | |||
9dfc3af9d7 | |||
8dc63932fc | |||
066e5cddb5 | |||
27ffad2148 | |||
41db363b06 | |||
184ac11c93 | |||
673aa3c5f2 | |||
2dd1c04bf3 | |||
4df1222ce7 | |||
208121646c | |||
f47a11f792 | |||
36fc3a5c96 | |||
ed12c9d611 | |||
4153feeb15 | |||
349c642d10 | |||
4728105f43 | |||
1f46a0dc26 | |||
7ff7ced504 | |||
a43ee41b0b | |||
ec7ade4ee6 | |||
9b846cbb83 | |||
2cc7fd07f8 | |||
72d54d9915 | |||
b390b82e9d | |||
1ef9ee13a3 | |||
6822795bb8 | |||
187dec2816 | |||
31f75e4607 | |||
92083eaf76 | |||
6b40c3974d | |||
c528401b62 | |||
8a17f512f4 | |||
e725f8a0fe | |||
525d3c2619 | |||
aba46720c0 | |||
2fb1d707fe | |||
dd06aa98ad | |||
cfb66ca153 | |||
0b4660702b | |||
9760227c66 | |||
5faeaa2028 | |||
cd58f9f9e5 | |||
00338bbc4b | |||
31d14a2fb0 | |||
49fa0ddebc | |||
d50c3e6c67 | |||
b39f56e813 | |||
ead0286ca6 | |||
6eb05af306 | |||
6b1e632621 | |||
29cb10fed8 | |||
a88433dba6 | |||
7e5f1fe411 | |||
00c78d33e4 | |||
50fbf9982e | |||
71ad39b6e3 | |||
3d329cdf2d | |||
21af5c8f73 | |||
716d7bb93e | |||
98f4b99446 | |||
830e701d13 | |||
57d9e7d5aa | |||
5fc16bb05b | |||
cb08bd2e2e | |||
961dba7faa | |||
41f933b89e | |||
d4dc1c87c5 | |||
e58fbeea17 | |||
2492dc50db | |||
b4f5f1e461 | |||
69347fff09 | |||
5c439f4e9c | |||
c84236ed73 | |||
ae1a3a0cb2 | |||
b3fea016f1 | |||
7c93639e59 | |||
affcd5c7de | |||
e00eb06f66 | |||
5ecc204d57 | |||
2a5eed1eb4 | |||
d517c13d7a | |||
bb88265d78 | |||
858694f4cc | |||
a46321baa0 | |||
9808e8ab0d | |||
7bd7b53845 | |||
056cfc9dc6 | |||
b7b60d103e | |||
14fd0eb73e | |||
ca73017100 | |||
123fb350ce | |||
1a27d7dfc0 | |||
b0c6d44515 | |||
6092e01428 | |||
e20ff5ae45 | |||
621a0d70e2 | |||
855b238ec5 | |||
96f44e1959 | |||
242dc8cddd | |||
b8a2004872 | |||
63f08bf2c2 | |||
4734a9ac18 | |||
81cd1e6c40 | |||
0b9cdea7d2 | |||
f63fceb2ea | |||
f0f7165ab6 | |||
2511f60d39 | |||
c0f868dd56 | |||
a112bfdaa2 | |||
4535a70f08 | |||
b59529e579 | |||
393c23820a | |||
e6a3958e45 | |||
59a7fdd2c9 | |||
c3cab28c9b | |||
4c55a6f436 | |||
c10e4c3117 | |||
3b70094151 | |||
a370697385 | |||
8a7c0313f6 | |||
00201f7e6c | |||
5e3111bad8 | |||
a327c10c60 | |||
938bc07e3c | |||
d053d45a6d | |||
5200da6eec | |||
b0523860d9 | |||
1a1215527f | |||
360c018c9e | |||
7bed964ab2 | |||
989d0a5682 | |||
b121c25184 | |||
e0d127b3e4 | |||
a0c56c74e1 | |||
f4b61a7f24 | |||
b2400fb883 | |||
f25832d1b6 | |||
cf363171aa | |||
38c8569d16 | |||
9791d15f39 | |||
80e7f5832b | |||
6e46ddaad3 | |||
8499e4aab2 | |||
5eae1851a1 | |||
c48a246ccb | |||
f084011a61 | |||
bd40cf194c | |||
2434af7e8a | |||
ced7fa9f01 | |||
28b559e812 | |||
76d776245b | |||
0d9f70492e | |||
b58f502dd6 | |||
5f367248c5 | |||
c2065cc3e2 | |||
034408971d | |||
490206b5b2 | |||
d8540819af | |||
d1a9aec526 | |||
e4b681a5a5 | |||
52036871d1 | |||
4fc6a804f5 | |||
08d2ca300a | |||
dc2ec0a8f9 | |||
2cfed952bb | |||
a7da137778 | |||
5ae2f87ce9 | |||
c4f6619fbd | |||
65edbc1424 | |||
1f2d7fa28f | |||
2407a0c4e2 | |||
7f8bfcc939 | |||
fae4cb9e56 | |||
063bbb02f2 | |||
15e1470c50 | |||
f602993aa9 | |||
271508c0a8 | |||
50f96d1c9c | |||
809544800b | |||
e757b06987 | |||
6f2da1baf1 | |||
f30dcad8a7 | |||
7dc235511e | |||
644c210fe2 | |||
68e8f98671 | |||
1228db6ac1 | |||
86596749fa | |||
76229a3601 | |||
95abdeb919 | |||
df0151d338 | |||
ee2f12fe81 | |||
10c104529f | |||
cbc8ec6508 | |||
8e7758e280 | |||
9aefbd189c | |||
b9d50584d8 | |||
e5f7390c09 | |||
797b1ff8bb | |||
09b738045c | |||
4fd690333a | |||
5ef5e25a7f | |||
87e8770cbc | |||
cac9d120be | |||
1ae0fadbf4 | |||
65497e089a | |||
2d763bd033 | |||
4ebf396cf5 | |||
6faa50d496 | |||
6fcd6298c8 | |||
5c16be5dbb | |||
e3abb6f178 | |||
70736be4eb | |||
1c3e7330f3 | |||
6e4c89b310 | |||
73b4a0ef5f | |||
d88002c4ed | |||
025c63c045 | |||
b189cbd46e | |||
a21ddb5914 | |||
e04a4c3923 | |||
ae0821e07b | |||
a508bece36 | |||
2c9969a8f4 | |||
dd9fc91f6f | |||
39d9838cc1 | |||
1397c7c624 | |||
c09fcba94f | |||
c77b38fc4f | |||
aef9b733e5 | |||
348208617b | |||
2c34c8e20f | |||
d19fa731d6 | |||
59bb1cc387 | |||
56909d0646 | |||
04da29c939 | |||
307f7a3024 | |||
e80bfa39f5 | |||
29714922ea | |||
88192114ac | |||
9d78208b76 | |||
d817bf0395 | |||
4d51056226 | |||
a607174a25 | |||
37d6a624b7 | |||
0c807bddaf | |||
a0470bfc66 | |||
b9463d23e8 | |||
04debd1623 | |||
9d31576cf5 | |||
8daca28a90 | |||
9899604261 | |||
687e1eabed | |||
eb09f34114 | |||
fbeb446ed7 | |||
92033ce0f5 | |||
9171bab5e5 | |||
f9819eb7b0 | |||
85728f0d15 | |||
9396849d56 | |||
6f5e5672bb | |||
b936e60876 | |||
fa4bd91213 | |||
81eeef7d3c | |||
a7b5134820 | |||
92a01c67ba | |||
71c23613b5 | |||
4f876995de | |||
9cf4a76196 | |||
9efe5287e4 | |||
8c4b34de4e | |||
93e3559dc3 | |||
caa0f63e1f | |||
a4e29e1244 | |||
599f2f43e3 | |||
3a453c5f73 | |||
98b313c75f | |||
8b796d80a7 | |||
719a8908a6 | |||
fed007ecae | |||
990443465f | |||
1f183b8a4e | |||
96556eb959 | |||
3ffeeac577 | |||
d7929a2340 | |||
667019a8c1 | |||
d9b46b4782 | |||
e249218a9d | |||
a2b1946b01 | |||
96a80f7ba0 | |||
9955fbf4f8 | |||
14966b0cd0 | |||
6e1a8f16a8 | |||
d106191e6a | |||
418cf6281e | |||
bdeb7d86b6 | |||
a0e0cc1038 | |||
78f6dec73b | |||
f6458f215f | |||
c7e0d547c4 | |||
fa54cfa0c3 | |||
31ea3f737c | |||
2a8a8065a8 | |||
d3ba002313 | |||
f18fd8d959 | |||
d54f7b13fb | |||
9c8b75206c | |||
15273c7f22 | |||
0ea8217c55 | |||
1735f28f5a | |||
8de4070f7f | |||
45e64f453f | |||
62dc5f2ac6 | |||
86c85a752e | |||
3de0ebf7fd | |||
1496ba0bbd | |||
f5974f6793 | |||
928ea3bb01 | |||
b7acb1d488 | |||
360d94dd67 | |||
671a813135 | |||
c005417f9b | |||
b5ec68bda3 | |||
b4ed29094d | |||
284ab031b8 | |||
6ad5064c09 | |||
3a8e723a36 | |||
dd3484b93f | |||
205773b700 | |||
f2039070e6 | |||
cf58a7eafd | |||
487749c25b | |||
48fb16b570 | |||
f94369dd6e | |||
fc0bd3b9e8 | |||
19946f1d19 | |||
562f56130a | |||
99f97adfc6 | |||
5ad7db722d | |||
a7d344d287 | |||
0ac215f9de | |||
4342155748 | |||
b561694bf0 | |||
cae9a8d608 | |||
88fbdba018 | |||
046067565a | |||
11299d9913 | |||
42ab233b08 | |||
4a92d7d1b2 | |||
2502ca6ccc | |||
651030ba93 | |||
8dfea9566e | |||
8cc54ce2a2 | |||
3abfcda8b5 | |||
cf0ae8f182 | |||
d90bf5c6dc | |||
955b550e95 | |||
b3cd46a5c8 | |||
3fdc8bfa3d | |||
e8f96a6e16 | |||
dc15df1aa7 | |||
05f5fac35b | |||
5bfcc5392d | |||
d2e830cce3 | |||
1e942be639 | |||
576009bad0 | |||
f71108a214 | |||
7109bd52f2 | |||
0f6effa263 | |||
8b0301ed00 | |||
4f56fb125e | |||
6487cd8c6f | |||
06e5c25383 | |||
36e5ae4a25 | |||
245e43ea8c | |||
52871c781a | |||
f994ada576 | |||
147a6e49dc | |||
be24ee435c | |||
9fac285b69 | |||
3ed5f9cd15 | |||
17e70ff8ec | |||
6dab119650 | |||
f80eb89d57 | |||
f6c2902fe4 | |||
1e890a8a0a | |||
aa1a84e677 | |||
9395f310d6 | |||
b99bb3d4bb | |||
4f66f096ff | |||
7f5f2284f3 | |||
59f9f4fca1 | |||
55284e418c | |||
20d4ffde6e | |||
78e5d4df9d | |||
1118ec9653 | |||
7d6c85be42 | |||
0192a6cb12 | |||
0e01a81219 | |||
36edf20273 | |||
a1bf19dbdf | |||
0ad739e78b | |||
ff9509b901 | |||
7e496b1979 | |||
448517032e | |||
a171e92e6c | |||
06e9bf9b0a | |||
7d9ec8cea0 | |||
5b4553ff0c | |||
d4ce7aef59 | |||
8b6df2e23f | |||
e2ff5846df | |||
24efeff788 | |||
4f359e62df | |||
e6fd2bed4d | |||
9fb6510135 | |||
7206b61838 | |||
e8ab0b3e8f | |||
843788580e | |||
4ceb3d890d | |||
3f8995b25e | |||
37e0cefc79 | |||
f026741dbb | |||
044b121e01 | |||
d57c3b4f89 | |||
960f7d5f2e | |||
2f61381651 | |||
ba6e931e21 | |||
440aa0d369 | |||
f42d4b5fa2 | |||
5d9fa2c484 | |||
f5c86fa171 | |||
f9019ce62d | |||
ee485e1728 | |||
8f2a6f8387 | |||
908bf3b117 | |||
6a739afd25 | |||
506490e32d | |||
846771f2a1 | |||
759b7584e0 | |||
e5cb224598 | |||
e32df1b405 | |||
db9f91b687 | |||
0941357068 | |||
d8993c52d0 | |||
6424b2dd03 | |||
2a4eb3ed1a | |||
c9fa00cce1 | |||
18eedbc02d | |||
ef9f63fe59 | |||
f8ce788425 | |||
6c1bd95643 | |||
2ed7ee8f71 | |||
2e63709450 | |||
6611d639a8 | |||
fe124e6ab3 | |||
fee0a8527d | |||
2f6b00403f | |||
dbe2c117e3 | |||
c4b1ccb6d6 | |||
2c70ee7e43 | |||
f7826616b8 | |||
9f476a12dd | |||
6f3cf0ae50 | |||
cd024e21f0 | |||
c4c470c1f3 | |||
dc9ad8df80 | |||
74d6225993 | |||
e1e0c5035d | |||
adf6d0eb82 | |||
90df435345 | |||
83d57211db | |||
0a8eeb2827 | |||
0c324c42f4 | |||
3adf54a952 | |||
8076c66a4c | |||
f9c583a636 | |||
30d536b19c | |||
1957899146 | |||
b52c83d88a | |||
27ff388413 | |||
d4306f7768 | |||
5d0a57c97d | |||
a87ba467ae | |||
7df7cd01eb | |||
098d805a8b | |||
4a21034a00 | |||
7904e359f2 | |||
0c8a94beb8 | |||
c13a573792 | |||
f9b42e12ae | |||
5c2031b768 | |||
49643882d4 | |||
175ddaa3a1 | |||
cc1d6e97b8 | |||
79b96ab301 | |||
44d9316023 | |||
5b0c9a74fb | |||
3429fc3e4c | |||
d11d8d5353 | |||
1742bd6ded | |||
9f6e118ea0 | |||
717bbd3bb5 | |||
e8ebe4de14 | |||
0ff614ccd4 | |||
d0a77b7e0c | |||
2815889090 | |||
1317956663 | |||
a2303d8895 | |||
2161c793dc | |||
a2f943db8f | |||
23a31b9c00 | |||
2591d1b94b | |||
300f97f102 | |||
7e3f6c3066 | |||
601d232064 | |||
8854ac84ad | |||
0302c3fbd4 | |||
6b6fdc6cfe | |||
7126ce86ec | |||
760bf52f75 | |||
6d791d31de | |||
21adc98d70 | |||
6fd7a56568 | |||
fba427fcaf | |||
7753361ffb | |||
c1590d9ed7 | |||
23c1138a58 | |||
2acb097662 | |||
449d116af2 | |||
110240981d | |||
ac5c6de929 | |||
51bdc44352 | |||
bafe34696d | |||
114f6f577f | |||
1f30670c1d | |||
10884ef7f5 | |||
5e12e5f42a | |||
e7a2e1f268 | |||
cc6744055f | |||
e6ba7c6e40 | |||
ea4855e908 | |||
5c7da4b0e6 | |||
9659d934ac | |||
de93677271 | |||
6ef3c628e6 | |||
f2af0be9ac | |||
ef7b74a104 | |||
906368d916 | |||
809cbf58c6 | |||
a5d60050a2 | |||
2c130c8668 | |||
c0ff02d9c7 | |||
cb627db392 | |||
33bd6bc75b | |||
ea5bf109cb | |||
b150a2842e | |||
2a46c39019 | |||
5ca5f026c5 | |||
15f22add79 | |||
131fa6a359 | |||
2d184e1842 | |||
95a55a2c1c | |||
8eecbb5c17 | |||
abfdcaa1b5 | |||
8ee74e5661 | |||
ff31ccdd30 | |||
f6645a41d2 | |||
daceb8105f | |||
4beba796d7 | |||
70c34baafb | |||
e71c016477 | |||
633bbd8a9e | |||
4c82df56e9 | |||
0d62ec5b03 | |||
38f943ef81 | |||
e421953fcd | |||
f39098a4f2 | |||
b203a95a78 | |||
2bb1a6792a | |||
e30d45a2b0 | |||
afcd90ae27 | |||
94c1d5a18c | |||
4254fa3d38 | |||
3b293e91e3 | |||
3d6320295e | |||
7654f1ca3e | |||
e62c66b153 | |||
638507caff | |||
107f5de58e | |||
b1451523ca | |||
f7a95b5edc | |||
be290fafe7 | |||
b7ae74edb9 | |||
b3041ae9fc | |||
7499b04638 | |||
bf2d2071fc | |||
488820daec | |||
77c15b76b5 | |||
e71129aa68 | |||
6f5b700833 | |||
9627864ca8 | |||
2a8625ffae | |||
ed82c3763c | |||
22266899dc | |||
9dfd1bfa41 | |||
409af28cb7 | |||
4fe604bfe8 | |||
49df72ceda | |||
2db029bcdb | |||
7fd128eabc | |||
5e3d8dd3eb | |||
785be2f327 | |||
8cf9baa132 | |||
09e3aed770 | |||
452ac297ab | |||
7ebb5c6a10 | |||
00e0d24a6a | |||
8a269041fb | |||
9f48adcff9 | |||
48e7d732b9 | |||
30048b74d1 | |||
c866b0dbfd | |||
1c79f18b13 | |||
6d704be88b | |||
5af389c087 | |||
475dde4193 | |||
8c534163e1 | |||
657887b241 | |||
10da35cbef | |||
00f15c1075 | |||
19f92df8ab | |||
8639cf8060 | |||
2501d29e9d | |||
8109dd684e | |||
5a259dd6b0 | |||
2ed28211ed | |||
9d0eaa216f | |||
58477282fe | |||
6b016c2528 | |||
b98c5f94ee | |||
ad8bdb929a | |||
5030d59fcc | |||
ec52928736 | |||
7f479e18e6 | |||
7999e9020d | |||
43ba93a817 | |||
b37e02c90a | |||
be5df17a4a | |||
ffbc1fd190 | |||
457fb604dd | |||
09c81f79f6 | |||
0725a7d836 | |||
f957ae71c3 | |||
011c9d1beb | |||
aed589a98d | |||
3830e90642 | |||
3710b88ab9 | |||
5c990f103e | |||
2890fc9d7d | |||
0223507a2e | |||
59412a9405 | |||
110d58bbc3 | |||
eda17defb2 | |||
f563fb124e | |||
14d0a96999 | |||
f0474ffccc | |||
11ce6845f2 | |||
70d610b5e4 | |||
f5ca649977 | |||
8954d33019 | |||
2e6205bc05 | |||
e0dea63079 | |||
7c244b01c6 | |||
3f5edf7c3e | |||
92d8d65543 | |||
cebd8e14e9 | |||
16e92a7ca3 | |||
59e2710137 | |||
f1ca96bbf0 | |||
ec01f5d5ee | |||
2a800e4ce0 | |||
ca2e09fe8b | |||
7e343f11f2 | |||
db20a54861 | |||
ec4f6b7f91 | |||
08a69cdcbd | |||
aa9c095c8e | |||
b257029d3e | |||
cb9062f818 | |||
3a1f623ea3 | |||
47d46e367e | |||
e5534e86ab | |||
e0ca572572 | |||
ca4b86e7ca | |||
f177bd0b51 | |||
11086e8ef8 | |||
1237aef727 | |||
79bfea5970 | |||
50f8ae6fc7 | |||
73ec86797b | |||
703417a760 | |||
3013a87bd2 | |||
833cb2556b | |||
8f4f8fccdb | |||
38e4ab1cea | |||
366a3e2c35 | |||
fcb175ebb3 | |||
a76cc79f88 | |||
1a6d74fcb2 | |||
eb351b1882 | |||
43caace1b6 | |||
167ed7c35b | |||
69735940ec | |||
1a7fad129d | |||
6931e6cf0f | |||
87e021cd2e | |||
d3f5d94afe | |||
a3d8b5e526 | |||
5e46abfa03 | |||
f4740bac65 | |||
dc10e61a20 | |||
82c8aad157 | |||
4ba1f26e4d | |||
f8805e8311 | |||
c815979f2a | |||
54e5ffcac1 | |||
3fd0502cb9 | |||
8aed51180f | |||
e616877fd2 | |||
df15ee4074 | |||
4cf3acd0ec | |||
77ea16e18f | |||
85d993386a | |||
14a7da767b | |||
2f3df71cc1 | |||
dc13e72b89 | |||
2beff9896a | |||
43af6f4792 | |||
04d1a35cb5 | |||
f075b36d4f | |||
c21b1e5fe0 | |||
042c1fd54b | |||
ee6086373b | |||
382310fe51 | |||
d52cb510fc | |||
c1de2788b1 | |||
41dc9e0894 | |||
d3b0d23d8f | |||
f96dcaccbe | |||
2672f205fd | |||
1ba21ed367 | |||
79408d359c | |||
12c76e53cb | |||
ac44426557 | |||
684e5bec15 | |||
70d19530c4 | |||
e34ab6c5ae | |||
b3cb9d8459 | |||
df3fa973ad | |||
1f4895428d | |||
417941a1bf | |||
973a6b21a2 | |||
b50604d070 | |||
a1ecf459b3 | |||
6ea80eadd6 | |||
9dafaa2c0c | |||
50e5736c03 | |||
46fcfb7629 | |||
8a86540090 | |||
785ab8192b | |||
3c386e0c50 | |||
bec48492ab | |||
13a10f8afd | |||
00fe97a55a | |||
9e665e4903 | |||
efc128e681 | |||
6851c5443a | |||
baf08dd688 | |||
67115b3e9b | |||
4ca2697271 | |||
4696bfbb80 | |||
18c62a1987 | |||
d60a4e97d6 | |||
43883a6945 | |||
58ff5c6cc6 | |||
3902e8bff0 | |||
5d6f37017b | |||
ecbf2f1429 | |||
56dc2eb96e | |||
2154a22c90 | |||
3fafe0da07 | |||
983535224e | |||
88effdd9c3 | |||
e9a4843eb0 | |||
c50132b29f | |||
e92a6b3c6c | |||
66a36bf591 | |||
39559f0a09 | |||
712623946f | |||
47ce72e011 | |||
7f8c5c4f64 | |||
64fdff6ace | |||
fa2b413095 | |||
46b7a95ec8 | |||
55922c0568 | |||
1c927868d8 | |||
0a1f0e58d0 | |||
9024c5d7ac | |||
370de395f7 | |||
8ebd2ff9eb | |||
83a1c7283d | |||
695f84e14f | |||
54f3036f86 | |||
446583400e | |||
3776c50986 | |||
4ff0697ee7 | |||
cd43c4983b | |||
0f065ebce6 | |||
a2e0e0ad06 | |||
d721fc2c17 | |||
becaeafeac | |||
99c97707ac | |||
75ed427e04 | |||
d1e35d11d0 | |||
ee0102e86e | |||
776b71bad5 | |||
37da87d9e1 | |||
ea14d74141 | |||
4b60a8deb8 | |||
7cca0a9cd8 | |||
f347aba01b | |||
8c40ded498 | |||
c267a7a7f9 | |||
0959adcfe3 | |||
7d12f47bf8 | |||
dff673533d | |||
816a29d582 | |||
27fbe4cd83 | |||
2ee6f49a93 | |||
0ea2d98768 | |||
b5dc78a968 | |||
ebe7890a49 | |||
ef4231b9c0 | |||
7e2bab48c9 | |||
eebab34b62 | |||
05d613f3a3 | |||
5c6b1fd0cc | |||
f8eab659d9 | |||
4a214d8238 | |||
44b4741e35 | |||
95b9863a70 | |||
4dfe3d21e1 | |||
3568e6d42d | |||
c3ae4542b2 | |||
3031036cb0 | |||
11dc510ae4 | |||
79c25d1dc8 | |||
1fd17b5027 | |||
7a6b2abfeb | |||
fe8e62c6a8 | |||
e937fd07e5 | |||
c668d16a00 | |||
c33e2d1971 | |||
0711a70398 | |||
cb5095370e | |||
8f71920622 | |||
3da0e9e86a | |||
e8c8fecb35 | |||
8bb2c425c6 | |||
1f6d807882 | |||
8d1ed9f88a | |||
d766f865f3 | |||
c540cb5e16 | |||
b36029f6c4 | |||
a8e6bfcb3d | |||
9c7222daea | |||
ab5c0bcfcf | |||
1a2dcf213e | |||
6f8e7f07f3 | |||
0ac4787f1a | |||
16bbe9782b | |||
5594c119f5 | |||
a909017a2b | |||
9a7914eee9 | |||
98c461dcd8 | |||
5991c8dca3 | |||
b9226fbcbd | |||
b6375d3e40 | |||
8dc9ceb2bc | |||
6cb713b0e3 | |||
dafa27fccd | |||
190f2f82ac | |||
43767ab2b9 | |||
3042e647ff | |||
a004ad6088 | |||
addb247c33 | |||
8f129e1c9d | |||
341b6a1290 | |||
6a9b1996e4 | |||
a5f2d289bd | |||
e3a8228065 | |||
fd04c59e63 | |||
f8b1a84b81 | |||
b53d18fe1c | |||
d0a94768a6 | |||
80fde70995 | |||
cbb56b6128 | |||
cbd2188c04 | |||
2b30afa618 | |||
1a65374e21 | |||
3e94f6bc3c | |||
1dbbb4f91c | |||
b3b847fadd | |||
ff25a5e251 | |||
aaf61cbbbe | |||
ca7c4edd41 | |||
028e83181b | |||
31c3783c7c | |||
f63371dadb | |||
9f07a3dd4a | |||
06262a903e | |||
e01e79d9fb | |||
2c627cad10 | |||
b33e51be45 | |||
b17f5b678d | |||
c6a6ae594a | |||
9f02129af3 | |||
6037350380 | |||
5de38cd9f6 | |||
7950c4afdd | |||
e304854fa5 | |||
d7f54213f1 | |||
12a71be076 | |||
855f0dbcad | |||
03db0d630e | |||
5a35c57866 | |||
d11027e8c8 | |||
aa733b5e53 | |||
355ec9ceca | |||
aa30c6a323 | |||
ad5572f275 | |||
9082b4df48 | |||
c3179583c3 | |||
4db87839a6 | |||
b018d49dc3 | |||
83362226cf | |||
1e3796967e | |||
8d7fa54af4 | |||
59cd9b4220 | |||
59dfb5866d | |||
a29ceaa8ec | |||
782e11b96c | |||
fd3be5b7de | |||
0b9a5c3441 | |||
7431c4d611 | |||
ec78dd60fc | |||
b479eec758 | |||
413ace2eb1 | |||
d8390ef77f | |||
e7e56e175a | |||
e875b9cdca | |||
66197b18b6 | |||
ed991f79b1 | |||
48b70f358d | |||
56adbda2dc | |||
7dbc78c95f | |||
3ffa1e35e8 | |||
114fb4219b | |||
4d7d66bc1f | |||
e134d2e5a5 | |||
cca84761a5 | |||
c2a5d6f61b | |||
4449007bb4 | |||
30cd1e84bc | |||
dcf872b485 | |||
c9d51ca775 | |||
96c9f8058b | |||
a3f4bca14e | |||
e55f2dd3b9 | |||
247566ca1d | |||
aa120e0902 | |||
5c7992beef | |||
6a615f30dc | |||
bfea41b771 | |||
a2465e0670 | |||
57044ea9a0 | |||
1392be2952 | |||
9e503aa69c | |||
a004389f25 | |||
d474261912 | |||
144e959cab | |||
626488e659 | |||
5bae5574f0 | |||
ce14d6d9db | |||
519addf6ac | |||
fd87468e36 | |||
75e49610cb | |||
414fe75d02 | |||
26031eb761 | |||
be801dff5f | |||
5fd47ed742 | |||
f7f2f50435 | |||
e0bb15e572 | |||
0e4171a87c | |||
34cb92ff4c | |||
22eea750f3 | |||
c3afe1a83a | |||
904ceba6b2 | |||
a28d639c3b | |||
c0652bcd8f | |||
54dc0fd123 | |||
c22a00afee | |||
2c3ec7846f | |||
46db9edacc | |||
4e6fa56c87 | |||
8970e17911 | |||
ac6c808124 | |||
26d3b1929e | |||
a29507e452 | |||
2c073fb005 | |||
d9c3b83d1e | |||
f5e58c500f | |||
5e865f5bc4 | |||
01a1255967 | |||
dd80f39049 | |||
19e4c953ef | |||
ef218c95fc | |||
eec9dba855 | |||
f46a165886 | |||
ec47bd1604 | |||
bfd4016c1c | |||
63cdb1848e | |||
10a0762fe7 | |||
76472b86ed | |||
edb96cde70 | |||
e06ecb8f0c | |||
2791d948e9 | |||
360e6e790a | |||
d0807c8276 | |||
75d0362cd8 | |||
4f7c554d8d | |||
8b81f23caf | |||
0098c2b7f7 | |||
f0e03b5e82 | |||
be2f1001a5 | |||
cf08b4d56a | |||
04074f883f | |||
14d3235f1a | |||
e00c1cbd20 | |||
3df3f0d9dc | |||
985db40547 | |||
c9fa0fdff0 | |||
fe69ea305b | |||
ff9088e42b | |||
f556cdf0ca | |||
c671ff74c6 | |||
04570ac783 | |||
6ab25cd791 | |||
a04350f7ce | |||
b7018de7e0 | |||
d212d57466 | |||
c4e7d8ed8c | |||
464813ecbb | |||
9812771dcd | |||
6f605598de | |||
85bc8ccccc | |||
e756c2dbce | |||
e82fe14f00 | |||
de65739c01 | |||
e1ec89a133 | |||
bdb3410d9d | |||
168e0b5a42 | |||
f906cfe5f6 | |||
9faac81a37 | |||
b90e7eb95c | |||
1e286e43ad | |||
539993b4f4 | |||
1363d30f79 | |||
3a48daaa64 | |||
d2b4a65e65 | |||
1ead290c23 | |||
6658660355 | |||
85ab019987 | |||
460cda2aa1 | |||
2d913578e1 | |||
34831796f6 | |||
8754b2767c | |||
04fb688f7d | |||
58dbd285fc | |||
cf6f149888 | |||
8d017ceaf1 | |||
02428019fa | |||
b4464929cb | |||
3ea22f8b0e | |||
9745e97e14 | |||
6c6e182ecc | |||
7973dd45b7 | |||
a1837dde68 | |||
4448b65a18 | |||
2231c23c4d | |||
f17fc43d6e | |||
f9dbe56785 | |||
8845a2170c | |||
a4b1ebd8c3 | |||
66adeef9bd | |||
20769f68a7 | |||
e92719b98d | |||
59246babea | |||
23e86d7dd5 | |||
c99e8eb29d | |||
7949397958 | |||
1d1359b58f | |||
8915bb4892 | |||
6a117ac12f | |||
67689f1a6d | |||
48b83f1ffd | |||
7da0f398a5 | |||
7e277fdd4a | |||
5d10d8566b | |||
a10295f584 | |||
46cf9faa11 | |||
971341bb53 | |||
2b2a235a49 | |||
970b9deeaa | |||
fd256b624c | |||
698fb64be9 | |||
9561f77b17 | |||
c3d3d346d4 | |||
e43fe98263 | |||
04dbf15d9b | |||
de72065a4a | |||
a1bb0ec738 | |||
1a33cd9584 | |||
00279dbd04 | |||
eb759cf22f | |||
ae16da4e81 | |||
965287e724 | |||
3d5312e8d2 | |||
bc91b7dcae | |||
4d77eb94ff | |||
1b8d03f945 | |||
de2dcfeb99 | |||
c7196a519f | |||
0d82ce5210 | |||
3ce9ad05b3 | |||
ab75faac74 | |||
9e25e13218 | |||
69e1503c6d | |||
7eaf231e56 | |||
a347a72091 | |||
560daec913 | |||
d9e968d863 | |||
556d5d181e | |||
4e4092f9e8 | |||
fd62aba71c | |||
ef0aa65774 | |||
6b5f9a647a | |||
01c07fbea1 | |||
81929c2a92 | |||
d9ff8e3122 | |||
0c4692ae58 | |||
df56ff4f09 | |||
96cdc9c4eb | |||
3b4ad5cd7d | |||
317c6b77f3 | |||
e5f5a2adaa | |||
594c3174ab | |||
5b7e4bb4a7 | |||
f7c0f826d4 | |||
66af7de6d6 | |||
d1815a36d0 | |||
61de3de909 | |||
36888a34d6 | |||
646435ee3e | |||
cbb8d5b0dc | |||
ebee01b355 | |||
0e3795b2f3 | |||
de73128d47 | |||
281b0a3e63 | |||
c414f9ac9d | |||
77242cfec0 | |||
8ebbba6eb8 | |||
0804cefbee | |||
2404d2935d | |||
e6f5e21b5d | |||
c303c6b5c1 | |||
f58e8f2a35 | |||
ededba0c6d | |||
e112fa92fe | |||
7524210d1f | |||
201dc05416 | |||
2b34978993 | |||
447246da74 | |||
1cf2bb6646 | |||
de1eafb564 | |||
22f0099a5d | |||
300eb87016 | |||
aa38b16368 | |||
266bfdf739 | |||
19ef6b0421 | |||
75570b7995 | |||
3290bfae68 | |||
0805d7a35f | |||
11278a0814 | |||
86de6f5861 | |||
498b023989 | |||
5265884af9 | |||
cdbe0bbf38 | |||
feef35a8ca | |||
3ff51da529 | |||
496e9f7b16 | |||
0b30dc29a5 | |||
15b4d29e70 | |||
9eee4b7687 | |||
196f6c241a | |||
c9296191a8 | |||
c7b4022283 | |||
a7ecc4cdd6 | |||
df5298d59c | |||
b79d17332e | |||
675572a41a | |||
0078fe9349 | |||
dc004f6eb7 | |||
07f1a05ab4 | |||
54292a99af | |||
f5933c8cb8 | |||
6700f86145 | |||
b31d22488e | |||
6382eeb8fd | |||
b07345a55c | |||
06febdce22 | |||
c31d9d5e3d | |||
cda8a545f1 | |||
f850e92524 | |||
cc9d53e038 | |||
7b048fc092 | |||
4ba4a501fd | |||
2b2cce6896 | |||
c1f51a7bf3 | |||
465556f0d8 | |||
db7ac5208a | |||
cfcd1bc014 | |||
e63f7e8779 | |||
7911154bad | |||
ec0730f3e5 | |||
76005f5adf | |||
94493cde35 | |||
a1f68720e5 | |||
022376dd56 | |||
934e5aacab | |||
35b142f23f | |||
41c3795a7b | |||
95d7099133 | |||
33dc9abb9b | |||
5e4edac14d | |||
cb5ae92986 | |||
ebbd295ebe | |||
65d23fb9a3 | |||
ad6d986172 | |||
985641cc2e | |||
de0a714081 | |||
3f942302d1 | |||
96396163cf | |||
0c736c4561 | |||
33ad9d1035 | |||
d57658d059 | |||
031206cf1f | |||
12c7cc278d | |||
9d3750b9b8 | |||
ba4b9f229e | |||
850fe98cbb | |||
8a9e3e0df2 | |||
a277569d31 | |||
7ed9516884 | |||
ecff2fa2b7 | |||
e49b94658c | |||
e4f1572a3a | |||
b5b13322d8 | |||
c25e7f3c41 | |||
f6a2c92bfa | |||
ed17418101 | |||
5264f39209 | |||
de69c719fb | |||
ab3173487d | |||
a3fcb8c284 | |||
ba92cfa064 | |||
6bee51ed33 | |||
122bca49ea | |||
19318a1eeb | |||
a7a46bbe1c | |||
3d26224180 | |||
940ddb104c | |||
5617f91281 | |||
a9a863aab4 | |||
ca26347dea | |||
41a14e808e | |||
6452501275 | |||
b61ada72cc | |||
ace42d845c | |||
850b6f28e5 | |||
3c81e9f0e7 | |||
e20ea19f34 | |||
ce041a3190 | |||
3a01aaf7fb | |||
ec4a2aae95 | |||
de8a66d4ce | |||
dc3d3acb3b | |||
48d6eb168f | |||
6190d6c962 | |||
76616dc98c | |||
82c2f5221d | |||
a3bbb7be14 | |||
9e1a2cfeac | |||
c6fabe504a | |||
61a17d7fab | |||
865cfa5211 | |||
e2b857adae | |||
f3924ccd91 | |||
86e3e59530 | |||
02e4726ba6 | |||
66e470e073 | |||
afaa5c24d6 | |||
518282e169 | |||
d955adbbad | |||
83d3225e57 | |||
6fa45975bf | |||
e6087efb40 | |||
4ce2f80a2f | |||
0862d1c804 | |||
333e380340 | |||
58f77a19ed | |||
f2d883dab2 | |||
7ba8c7c2b5 | |||
0bf6c93faa | |||
b9f0158278 | |||
25ee41f344 | |||
5436634829 | |||
915524e1ab | |||
d579cd1605 | |||
cb5941ec55 | |||
a5ac183d86 | |||
a36de92bb9 | |||
01f9d551f1 | |||
399df66b18 | |||
723a1c843a | |||
e333263fd6 | |||
507df9eea1 | |||
a9a3687ea0 | |||
f05c649c61 | |||
a2dfba1842 | |||
c199da4dfa | |||
ee9033e12f | |||
54788d750e | |||
78e894c6f2 | |||
32107ba8b5 | |||
6122f65e7a | |||
43fd29f9bf | |||
54c624b356 | |||
8c33adfd29 | |||
2e8881b77c | |||
64aa729edd | |||
ccf95b738d | |||
b2847fedd3 | |||
8befcb9bba | |||
988fc52303 | |||
7293ddb22c | |||
f23c118e81 | |||
f68b3be35a | |||
3d95e7bb11 | |||
337c484f01 | |||
5d98e2bf04 | |||
6f300d0cc6 | |||
3422e1dca7 | |||
963c6ae567 | |||
6304169926 | |||
221afde55e | |||
0ae87270ad | |||
9d33baec70 | |||
0f37b22cdb | |||
47afd87e84 | |||
700c06023e | |||
4fea5b5ca3 | |||
521bddc1cc | |||
bdfff20ec2 | |||
bdd05aba3b | |||
89c2538ff1 | |||
7680819108 | |||
d79e8b84c9 | |||
731317230a | |||
5046938913 | |||
0e8fd45559 | |||
6099a5dbc3 | |||
2daa98a694 | |||
5d2c6496fa | |||
817dbbe73f | |||
29c89c82f8 | |||
0b714bd479 | |||
8c94a5afb9 | |||
aeb117c9d1 | |||
a2d4f133b7 | |||
b833aff3c8 | |||
6601e4ddba | |||
815da2d0ec | |||
ddd35b3653 | |||
8a32894c83 | |||
49d8e6da40 | |||
8089f24c81 | |||
b6aab53d10 | |||
55a4517cd1 | |||
b095319a16 | |||
5ea5806730 | |||
bfbf812148 | |||
168e9eeac1 | |||
dd79c1a79a | |||
fe3402589b | |||
74dcaff21c | |||
0a7968a2e5 | |||
00091a2acb | |||
c5aa834b6a | |||
b1bde46694 | |||
49e4fa494e | |||
4622c52b71 | |||
e9ac5dd5f4 | |||
7570c43d11 | |||
4332e7ec49 | |||
9f26f1e225 | |||
0c0319c415 | |||
c16dbd7607 | |||
7b1f10a5fe | |||
dce74749b7 | |||
1ba88b8c42 | |||
9c50b57d46 | |||
900bd3ee97 | |||
b4affe00a7 | |||
ca49c84bc1 | |||
2cddf60226 | |||
d9c3951f02 | |||
aa5997d975 | |||
c51acf7c2a | |||
348044bc8a | |||
7dbdf2aa07 | |||
c933731ead | |||
e7da715994 | |||
6d82aefad4 | |||
8a11ab7d96 | |||
f465086405 | |||
70313a8b79 | |||
71679c38be | |||
addd943074 | |||
0b8470421c | |||
bcf7c0f006 | |||
2b87bb015c | |||
07e10fa03e | |||
bf992989c7 | |||
00400e354d | |||
19a49d34ce | |||
e8badac282 | |||
282a60fcab | |||
e76a28ded5 | |||
b22543ab66 | |||
d5165cdc08 | |||
1c0549f468 | |||
a7316b0594 | |||
634eeaf74c | |||
1299e196be | |||
92c325230d | |||
827a466a5c | |||
89b38b1361 | |||
140de6dd60 | |||
e64ff5832e | |||
17b1543d44 | |||
043e79a570 | |||
5d036e3d54 | |||
9d2a638988 | |||
7943993fcb | |||
026ddc2d9b | |||
384c7e2c17 | |||
700f706428 | |||
0a4deb2a9b | |||
5b8abe6809 | |||
2c5005c1ea | |||
c2b3022163 | |||
644abb2dc9 | |||
48b53c30f7 | |||
bf3818eb22 | |||
b3358aeed7 | |||
a7d4c7d8de | |||
369c1b0a41 | |||
21636f3f29 | |||
bf428312d7 | |||
6218209dcb | |||
1b7ead3455 | |||
3a252a1b41 | |||
fc7e6470b5 | |||
3813a03117 | |||
e59c29993c | |||
92d2ebc3f9 | |||
67ef448471 | |||
207abe9a2c | |||
05863227a6 | |||
ac05cb323c | |||
066d44636a | |||
3dbf06420d | |||
1983097d3a | |||
1c60aa58ae | |||
1626d9f9a4 | |||
c7182589d2 | |||
ceb17dc713 | |||
46c0360b03 | |||
0ebddfcf50 | |||
36d20eb1b8 | |||
db4b266874 | |||
63cf470e07 | |||
1f87eb4157 | |||
9bb9999b46 | |||
60e6349963 | |||
581d1c5db1 | |||
cf0b6dda25 | |||
8a5faa3d2e | |||
09607787cc | |||
0aad74a670 | |||
985f28bbea | |||
22e1abbaff | |||
21e8097b9c | |||
14d267c246 | |||
9420174477 | |||
fb4878bb7c | |||
c856cbb523 | |||
2e42eb6bad | |||
4f87e86603 | |||
3c6737f738 | |||
b2bc73c3fe | |||
30ca25e978 | |||
ecdd0875df | |||
d05d748fe2 | |||
8db193a172 | |||
c064973c9d | |||
01c66eaf0c | |||
c2fdec188e | |||
d7e2b0a771 | |||
06354a8c9a | |||
7c25dead17 | |||
f4d3153e91 | |||
7030d59b2f | |||
6eae036ac3 | |||
b6b6ed0e2f | |||
51b5825665 | |||
1c2629595e | |||
e91f4e88b5 | |||
bdb7dbdd00 | |||
7092521253 | |||
47f7fcd4fe | |||
93a004b016 | |||
2fad9d73d4 | |||
252eb24522 | |||
b7964e9efb | |||
23e7a9e710 | |||
36804a60c9 | |||
73270345f5 | |||
e1ffe06709 | |||
3dd240bdbb | |||
b58425d7d7 | |||
a197ce6f53 | |||
9dcdaf05b5 | |||
c827cccdf3 | |||
6805f2d71e | |||
0ea690a2f2 | |||
d68ff69c7a | |||
f1f2bc28a2 | |||
5f0389c07c | |||
c2f304f3bc | |||
ce9c1a1f7a | |||
203382e007 | |||
d2ba9eb967 | |||
59ebec25f3 | |||
b864b03a65 | |||
c7a37660ce | |||
02aae631d8 | |||
d542f63d3f | |||
74694a6e23 | |||
a5baeac428 | |||
e23e04953c | |||
fa9f923697 | |||
797e201946 | |||
156a642d28 | |||
96379b7517 | |||
9b5bb62aa7 | |||
895745ac14 | |||
6cde2d8db4 | |||
a277f8e0e1 | |||
cf5c5d06e1 | |||
722f45fa58 | |||
c97390b9c6 | |||
124c461a56 | |||
4ac352637c | |||
00cf62acfb | |||
ee0c2a1152 | |||
f32ab20267 | |||
1316f93b21 | |||
52d72fe8a1 | |||
0406aaa591 | |||
8f56660cfc | |||
402cc6b90c | |||
b8c14ad64e | |||
1f9c83d88b | |||
319667a25c | |||
b047a37a80 | |||
31af220483 | |||
40d51ea59f | |||
284cf83935 | |||
5ba04a7478 | |||
2b87051022 | |||
a901f2dc5d | |||
bd6f1f2c6d | |||
ca612872a6 | |||
feb33a6a28 | |||
ff92d962f3 | |||
79ca0d579c | |||
c61ac862ba | |||
64ce622f83 | |||
772638c78e | |||
24badb46fe | |||
87d54b37e4 | |||
98aa61e2a4 | |||
bea5c6f4e6 | |||
7d29e691a4 | |||
b0d161faad | |||
4c74fa81d1 | |||
556a3e08db | |||
e3fb77c051 | |||
3ee07d0e82 | |||
72c486cb3e | |||
e37574510e | |||
f4b58f35ba | |||
01696f19e8 | |||
1f5a27d5c5 | |||
81476dedcb | |||
90b08acbf1 | |||
f967fd21f8 | |||
8d854d5f1a | |||
fde5932b45 | |||
eb84227f78 | |||
550d595034 | |||
24cc4b49d6 | |||
5f130d1925 | |||
4f05787338 | |||
a98db33c18 | |||
a5d78f2943 | |||
46ebe9ffc5 | |||
e2b80658ca | |||
0a586c5c92 | |||
aa5d352a06 | |||
760da64a4c | |||
714ffc5ef1 | |||
fd4d645687 | |||
24ad59ea37 | |||
15f881f967 | |||
d5285674ae | |||
bb862e20c0 | |||
be3eb308b9 | |||
e7f0b1dc59 | |||
336cec8b2a | |||
fad0b96f24 | |||
d2aab9d6a6 | |||
4005863e3d | |||
70cdb67f31 | |||
a9aec6956d | |||
d871eda6be | |||
49d620a414 | |||
2e2e3281da | |||
37cbfe29cf | |||
a1f88fc17f | |||
0065da61bd | |||
d2b0706c40 | |||
a95e585e39 | |||
7d39fa76dd | |||
ddf27c1a84 | |||
87e46f3ff1 | |||
33a6fda6c3 | |||
7cc1bdb35d | |||
60557f4e0f | |||
914441218a | |||
e322d98886 | |||
ba1e5f8f71 | |||
517075c605 | |||
540e970170 | |||
d0fd5641c1 | |||
caaa21dec0 | |||
22c606326f | |||
00ed2973b2 | |||
3837fc0a87 | |||
07b95d3436 | |||
1f5dd9c397 | |||
007736a234 | |||
c2a9f7fbb2 | |||
b18cc8de86 | |||
30e4f80894 | |||
dd8a53d5e0 | |||
0f01928402 | |||
a8b081661c | |||
c892610f27 | |||
72dad591fa | |||
ea19790828 | |||
cc2f5d19c8 | |||
64b2c5d1b4 | |||
a9f728d2a7 | |||
3736d81d8f | |||
fbcea03ab3 | |||
f19ee78fb2 | |||
b47fd6df31 | |||
786beccca5 | |||
916c62a702 | |||
11234c7cfc | |||
80eac7370e | |||
ac78a1e1c0 | |||
34c6ff9645 | |||
f6749fb204 | |||
89fe43f70c | |||
e2c66ce48a | |||
eb0d803617 | |||
53d9ea7a2c | |||
0fbdd0b67f | |||
f248aa69dc | |||
9f1ed13a38 | |||
9400d8f6db | |||
c7a4b307af | |||
0bac3a5dd7 | |||
14b92a4897 | |||
66bd8b553f | |||
e80462a2c3 | |||
b990ed2c23 | |||
097e56f4ab | |||
7a4b6138c1 | |||
bc918d0d18 | |||
8b08d8bf2d | |||
d92c97f755 | |||
6a367917f7 | |||
b67138b5ae | |||
0e5177c329 | |||
c6ed3cdb61 | |||
fd99d13f04 | |||
36c3ce9333 | |||
aee28616a9 | |||
70830560ae | |||
df6cd46bd6 | |||
dce797f4d9 | |||
f3232901d8 | |||
d3e4f44d37 | |||
d81958a074 | |||
92ee17493c | |||
f9e456bb47 | |||
740388c778 | |||
1c0c42e8e7 | |||
e2726f3e38 | |||
ed465a6ffe | |||
fd8f3df2cd | |||
0c2037875a | |||
fbf6e032d0 | |||
c8020e6559 | |||
68b7e8437b | |||
6528f8366f | |||
88eb246b60 | |||
bed50688d2 | |||
44686bac3e | |||
ca575ef0ae | |||
c20503028a | |||
4516e4cc3b | |||
b2ec340f9e | |||
570a029f27 | |||
ebe72e197d | |||
ce629b09b2 | |||
97c2db1cfd | |||
c5804c1929 | |||
92276c5e70 | |||
62c0088dd8 | |||
e8498adaf1 | |||
c7fa719cc3 | |||
41f0e133a9 | |||
7705a65beb | |||
604e8f4f8a | |||
de0116d8c8 | |||
8d968e5c9b | |||
758e573483 | |||
eab4f4c963 | |||
245c58842b | |||
e508635c6e | |||
138b8cf874 | |||
d446b657f3 | |||
019dd2e1b0 | |||
602da771f6 | |||
5de8a0a84b | |||
d2198925e1 | |||
44e02003ad | |||
e8bfd990e4 | |||
d1fc87577a | |||
8b4c1a80d0 | |||
6ca0d4a5ef | |||
d0cd6ba47d | |||
aa2a63bd84 | |||
daa380fb0e | |||
61e2e04f13 | |||
db7e4ddc04 | |||
c4aa277b19 | |||
ab29ce872a | |||
57beb0ade1 | |||
e3d0b6f90f | |||
e2aa954cb5 | |||
05c285a945 | |||
27b34992c6 | |||
4886238761 | |||
42d46aed90 | |||
a622aba7eb | |||
831099cca5 | |||
b8a54faf94 | |||
80ff6ff797 | |||
2f27b94757 | |||
b2f33e2895 | |||
d1d4142052 | |||
46caf6d673 | |||
2864c360bc | |||
e5dfc6323a | |||
5bc042ba6f | |||
c63fe5ee24 | |||
ee6bc33cea | |||
5c730dc53d | |||
5a3de8d663 | |||
fad88dd517 | |||
39dd24310d | |||
b13809d0c7 | |||
87559414a3 | |||
b5b5759829 | |||
d254e2e1f2 | |||
458b0b22fc | |||
cd30128af8 | |||
d61cdd8cea | |||
0d0e545979 | |||
6c5e96c33a | |||
bae2359b54 | |||
8cbbb456f0 | |||
dfd39461cf | |||
60d8683ae7 | |||
f2cc5cf152 | |||
d4a26fbf4b | |||
025784fd83 | |||
4e89a5edde | |||
b3936ecadf | |||
2c9e6bb589 | |||
73261a4a66 | |||
3d0dd38045 | |||
8bcbf3030f | |||
1bc7edc5d8 | |||
0673720db9 | |||
730a0d4c5a | |||
9147dee0de | |||
12746a1949 | |||
bdd65fe755 | |||
21e2280825 | |||
e9d2a429eb | |||
b67dfb9edf | |||
55308917f9 | |||
1b64b09532 | |||
74dd298891 | |||
5a85fc0e55 | |||
26991988cb | |||
15563444cf | |||
3bcdba6e1d | |||
ef56a78544 | |||
049a561466 | |||
b40b19997a | |||
46505a8314 | |||
78fb102002 | |||
c6e924f788 | |||
f6508b51a2 | |||
b0ae596de8 | |||
1d311e7916 | |||
0c19f71c96 | |||
8d6ab32b9a | |||
6c1a2d531f | |||
c6e9f9742b | |||
d23aaf3cea | |||
017fde91ad | |||
31ffc5a85d | |||
62b65a25d8 | |||
882fe48d80 |
14
.gitignore
vendored
@ -16,11 +16,16 @@ config.log
|
||||
config.status
|
||||
config
|
||||
configure
|
||||
data/50-gnome-shell-*.xml
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
data/gschemas.compiled
|
||||
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
|
||||
docs/reference/*/*.args
|
||||
docs/reference/*/*.bak
|
||||
docs/reference/*/*.hierarchy
|
||||
@ -33,6 +38,7 @@ docs/reference/*/*.txt
|
||||
docs/reference/*/*.types
|
||||
docs/reference/*/html/
|
||||
docs/reference/*/xml/
|
||||
docs/reference/shell/doc-gen-*
|
||||
gtk-doc.make
|
||||
js/misc/config.js
|
||||
intltool-extract.in
|
||||
@ -40,12 +46,14 @@ intltool-merge.in
|
||||
intltool-update.in
|
||||
libtool
|
||||
m4/
|
||||
man/gnome-shell.1
|
||||
omf.make
|
||||
po/*.gmo
|
||||
po/gnome-shell.pot
|
||||
po/*.header
|
||||
po/*.sed
|
||||
po/*.sin
|
||||
po/.intltool-merge-cache
|
||||
po/Makefile.in.in
|
||||
po/Makevars.template
|
||||
po/POTFILES
|
||||
@ -58,13 +66,17 @@ src/*-enum-types.[ch]
|
||||
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
|
||||
src/gnome-shell-extension-tool
|
||||
src/gnome-shell-extension-prefs
|
||||
src/gnome-shell-hotplug-sniffer
|
||||
src/gnome-shell-jhbuild
|
||||
src/gnome-shell-perf-helper
|
||||
src/gnome-shell-perf-tool
|
||||
src/gnome-shell-real
|
||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||
src/run-js-test
|
||||
@ -74,6 +86,8 @@ src/test-theme
|
||||
src/st.h
|
||||
src/stamp-st.h
|
||||
src/stamp-st.h.tmp
|
||||
src/st-scroll-view-fade-generated.c
|
||||
src/stamp-st-scroll-view-fade-generated.c
|
||||
stamp-h1
|
||||
tests/run-test.sh
|
||||
xmldocs.make
|
||||
|
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "src/gvc"]
|
||||
path = src/gvc
|
||||
url = git://git.gnome.org/libgnome-volume-control
|
331
HACKING
Normal file
@ -0,0 +1,331 @@
|
||||
Coding guide
|
||||
============
|
||||
|
||||
Our goal is to have all JavaScript code in GNOME follow a consistent style. In
|
||||
a dynamic language like JavaScript, it is essential to be rigorous about style
|
||||
(and unit tests), or you rapidly end up with a spaghetti-code mess.
|
||||
|
||||
A quick note
|
||||
------------
|
||||
|
||||
Life isn't fun if you can't break the rules. If a rule seems unnecessarily
|
||||
restrictive while you're coding, ignore it, and let the patch reviewer decide
|
||||
what to do.
|
||||
|
||||
Indentation and whitespace
|
||||
--------------------------
|
||||
|
||||
Use four-space indents. Braces are on the same line as their associated
|
||||
statements. You should only omit braces if *both* sides of the statement are
|
||||
on one line.
|
||||
|
||||
* One space after the `function` keyword. No space between the function name
|
||||
* in a declaration or a call. One space before the parens in the `if`
|
||||
* statements, or `while`, or `for` loops.
|
||||
|
||||
function foo(a, b) {
|
||||
let bar;
|
||||
|
||||
if (a > b)
|
||||
bar = do_thing(a);
|
||||
else
|
||||
bar = do_thing(b);
|
||||
|
||||
if (var == 5) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
print(i);
|
||||
}
|
||||
} else {
|
||||
print(20);
|
||||
}
|
||||
}
|
||||
|
||||
Semicolons
|
||||
----------
|
||||
|
||||
JavaScript allows omitting semicolons at the end of lines, but don't. Always
|
||||
end statements with a semicolon.
|
||||
|
||||
js2-mode
|
||||
--------
|
||||
|
||||
If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
|
||||
while. emacs now has a built-in JavaScript mode, js-mode, based on
|
||||
espresso-mode. It is the de facto emacs mode for JavaScript.
|
||||
|
||||
File naming and creation
|
||||
------------------------
|
||||
|
||||
For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
|
||||
|
||||
We only use C where gjs/gobject-introspection is not available for the task, or
|
||||
where C would be cleaner. To work around limitations in
|
||||
gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
|
||||
|
||||
Like many other GNOME projects, we prefix our C source filenames with the
|
||||
library name followed by a dash, e.g. `shell-app-system.c`. Create a
|
||||
`-private.h` header when you want to share code internally in the
|
||||
library. These headers are not installed, distributed or introspected.
|
||||
|
||||
Imports
|
||||
-------
|
||||
|
||||
Use UpperCamelCase when importing modules to distinguish them from ordinary
|
||||
variables, e.g.
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
|
||||
Imports should be categorized into one of two places. The top-most import block
|
||||
should contain only "environment imports". These are either modules from
|
||||
gobject-introspection or modules added by gjs itself.
|
||||
|
||||
The second block of imports should contain only "application imports". These
|
||||
are the JS code that is in the gnome-shell codebase,
|
||||
e.g. `imports.ui.popupMenu`.
|
||||
|
||||
Each import block should be sorted alphabetically. Don't import modules you
|
||||
don't use.
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
The alphabetical ordering should be done independently of the location of the
|
||||
location. Never reference `imports` in actual code.
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
We use CONSTANTS_CASE to define constants. All constants should be directly
|
||||
under the imports:
|
||||
|
||||
const MY_DBUS_INTERFACE = 'org.my.Interface';
|
||||
|
||||
Variable declaration
|
||||
--------------------
|
||||
|
||||
Always use either `const` or `let` when defining a variable.
|
||||
|
||||
// Iterating over an array
|
||||
for (let i = 0; i < arr.length; ++i) {
|
||||
let item = arr[i];
|
||||
}
|
||||
|
||||
// Iterating over an object's properties
|
||||
for (let prop in someobj) {
|
||||
...
|
||||
}
|
||||
|
||||
If you use "var" then the variable is added to function scope, not block scope.
|
||||
See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
|
||||
|
||||
Classes
|
||||
-------
|
||||
|
||||
There are many approaches to classes in JavaScript. We use our own class framework
|
||||
(sigh), which is built in gjs. The advantage is that it supports inheriting from
|
||||
GObjects, although this feature isn't used very often in the Shell itself.
|
||||
|
||||
const IconLabelMenuItem = new Lang.Class({
|
||||
Name: 'IconLabelMenuItem',
|
||||
Extends: PopupMenu.PopupMenuBaseItem,
|
||||
|
||||
_init: function(icon, label) {
|
||||
this.parent({ reactive: false });
|
||||
this.addActor(icon);
|
||||
this.addActor(label);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
log("menu opened!");
|
||||
}
|
||||
});
|
||||
|
||||
* 'Name' is required. 'Extends' is optional. If you leave it out, you will
|
||||
automatically inherit from Object.
|
||||
|
||||
* Leave a blank line between the "class header" (Name, Extends, and other
|
||||
things) and the "class body" (methods). Leave a blank line between each
|
||||
method.
|
||||
|
||||
* No space before the colon, one space after.
|
||||
|
||||
* No trailing comma after the last item.
|
||||
|
||||
* Make sure to use a semicolon after the closing paren to the class. It's
|
||||
still a giant function call, even though it may resemble a more
|
||||
conventional syntax.
|
||||
|
||||
GObject Introspection
|
||||
---------------------
|
||||
|
||||
GObject Introspection is a powerful feature that allows us to have native
|
||||
bindings for almost any library built around GObject. If a library requires
|
||||
you to inherit from a type to use it, you can do so:
|
||||
|
||||
const MyClutterActor = new Lang.Class({
|
||||
Name: 'MyClutterActor',
|
||||
Extends: Clutter.Actor,
|
||||
|
||||
vfunc_get_preferred_width: function(actor, forHeight) {
|
||||
return [100, 100];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(actor, forWidth) {
|
||||
return [100, 100];
|
||||
},
|
||||
|
||||
vfunc_paint: function(actor) {
|
||||
let alloc = this.get_allocation_box();
|
||||
Cogl.set_source_color4ub(255, 0, 0, 255);
|
||||
Cogl.rectangle(alloc.x1, alloc.y1,
|
||||
alloc.x2, alloc.y2);
|
||||
}
|
||||
});
|
||||
|
||||
Translatable strings, `environment.js`
|
||||
--------------------------------------
|
||||
|
||||
We use gettext to translate the GNOME Shell into all the languages that GNOME
|
||||
supports. The `gettext` function is aliased globally as `_`, you do not need to
|
||||
explicitly import it. This is done through some magic in the
|
||||
[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
|
||||
file. If you can't find a method that's used, it's probably either in gjs itself
|
||||
or installed on the global object from the Environment.
|
||||
|
||||
Use 'single quotes' for programming strings that should not be translated
|
||||
and "double quotes" for strings that the user may see. This allows us to
|
||||
quickly find untranslated or mistranslated strings by grepping through the
|
||||
sources for double quotes without a gettext call around them.
|
||||
|
||||
`actor` and `_delegate`
|
||||
-----------------------
|
||||
|
||||
gjs allows us to set so-called "expando properties" on introspected objects,
|
||||
allowing us to treat them like any other. Because the Shell was built before
|
||||
you could inherit from GTypes natively in JS, we usually have a wrapper class
|
||||
that has a property called `actor`. We call this wrapper class the "delegate".
|
||||
|
||||
We sometimes use expando properties to set a property called `_delegate` on
|
||||
the actor itself:
|
||||
|
||||
const MyClass = new Lang.Class({
|
||||
Name: 'MyClass',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Button({ text: "This is a button" });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
},
|
||||
|
||||
_onClicked: function(actor) {
|
||||
actor.set_label("You clicked the button!");
|
||||
}
|
||||
});
|
||||
|
||||
The 'delegate' property is important for anything which trying to get the
|
||||
delegate object from an associated actor. For instance, the drag and drop
|
||||
system calls the `handleDragOver` function on the delegate of a "drop target"
|
||||
when the user drags an item over it. If you do not set the `_delegate`
|
||||
property, your actor will not be able to be dropped onto.
|
||||
|
||||
Functional style
|
||||
----------------
|
||||
|
||||
JavaScript Array objects offer a lot of common functional programming
|
||||
capabilities such as forEach, map, filter and so on. You can use these when
|
||||
they make sense, but please don't have a spaghetti mess of function programming
|
||||
messed in a procedural style. Use your best judgment.
|
||||
|
||||
Closures
|
||||
--------
|
||||
|
||||
`this` will not be captured in a closure, it is relative to how the closure is
|
||||
invoked, not to the value of this where the closure is created, because "this"
|
||||
is a keyword with a value passed in at function invocation time, it is not a
|
||||
variable that can be captured in closures.
|
||||
|
||||
All closures should be wrapped with a Lang.bind.
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
let closure = Lang.bind(this, function() { this._fnorbate(); });
|
||||
|
||||
A more realistic example would be connecting to a signal on a method of a
|
||||
prototype:
|
||||
|
||||
const Lang = imports.lang;
|
||||
const FnorbLib = imports.fborbLib;
|
||||
|
||||
const MyClass = new Lang.Class({
|
||||
_init: function() {
|
||||
let fnorb = new FnorbLib.Fnorb();
|
||||
fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
|
||||
},
|
||||
|
||||
_onFnorbFrobate: function(fnorb) {
|
||||
this._updateFnorb();
|
||||
}
|
||||
});
|
||||
|
||||
Object literal syntax
|
||||
---------------------
|
||||
|
||||
In JavaScript, these are equivalent:
|
||||
|
||||
foo = { 'bar': 42 };
|
||||
foo = { bar: 42 };
|
||||
|
||||
and so are these:
|
||||
|
||||
var b = foo['bar'];
|
||||
var b = foo.bar;
|
||||
|
||||
If your usage of an object is like an object, then you're defining "member
|
||||
variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
|
||||
42 }` `foo.bar`.
|
||||
|
||||
If your usage of an object is like a hash table (and thus conceptually the keys
|
||||
can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
|
||||
}`, `foo['bar']`.
|
||||
|
||||
Getters, setters, and Tweener
|
||||
-----------------------------
|
||||
|
||||
Getters and setters should be used when you are dealing with an API that is
|
||||
designed around setting properties, like Tweener. If you want to animate an
|
||||
arbitrary property, create a getter and setter, and use Tweener to animate the
|
||||
property.
|
||||
|
||||
const ANIMATION_TIME = 2000;
|
||||
|
||||
const MyClass = new Lang.Class({
|
||||
Name: 'MyClass',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout();
|
||||
this._position = 0;
|
||||
},
|
||||
|
||||
get position() {
|
||||
return this._position;
|
||||
},
|
||||
|
||||
set position(value) {
|
||||
this._position = value;
|
||||
this.actor.set_position(value, value);
|
||||
}
|
||||
});
|
||||
|
||||
let myThing = new MyClass();
|
||||
Tweener.addTween(myThing,
|
||||
{ position: 100,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
10
Makefile.am
@ -1,7 +1,11 @@
|
||||
# Point to our macro directory and pick up user flags from the environment
|
||||
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
||||
|
||||
SUBDIRS = data js src browser-plugin tests po man docs
|
||||
SUBDIRS = data js src browser-plugin tests po docs
|
||||
|
||||
if ENABLE_MAN
|
||||
SUBDIRS += man
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
.project \
|
||||
@ -12,7 +16,9 @@ EXTRA_DIST = \
|
||||
# These are files checked into Git that we don't want to distribute
|
||||
DIST_EXCLUDE = \
|
||||
.gitignore \
|
||||
.gitmodules \
|
||||
gnome-shell.doap \
|
||||
HACKING \
|
||||
MAINTAINERS \
|
||||
tools/build/*
|
||||
|
||||
@ -20,4 +26,4 @@ distcheck-hook:
|
||||
@echo "Checking disted files against files in git"
|
||||
@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
|
||||
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-man
|
||||
|
846
NEWS
@ -1,3 +1,845 @@
|
||||
3.7.3
|
||||
=====
|
||||
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
|
||||
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
|
||||
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
|
||||
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
|
||||
* Improve login process indication [Stéphane; #687113]
|
||||
* Omit empty categories in apps view [Stéphane; #687970]
|
||||
* Style panel differently according to mode [Florian; #684573]
|
||||
* Make it possible to hide the user name [Matthias; #688577]
|
||||
* Consolidate and improve chat connection notifications [Giovanni; #687213]
|
||||
* Improve notification scrollbar appearance [Carlos; #688393]
|
||||
* Fade scroll view fade near scrolling edges [Jasper; #689249]
|
||||
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
|
||||
* Don't close message tray after using context menus [Giovanni; #689296]
|
||||
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
|
||||
* Remember state of 'Remember Password' checkbox [Ron; #688039]
|
||||
* Improve timestamp format in chat notifications [Carlos; #680989]
|
||||
* Improve style of missed-messages counter [Carlos; #686472]
|
||||
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
|
||||
* Add window-based Alt-Tab popup [Florian; #688913]
|
||||
* Support external session mode definitions [Florian; #689304]
|
||||
* Support session-mode-specific extensions [Florian; #689305]
|
||||
* Support 'parentMode' property in session modes [Florian; #689308]
|
||||
* Support a new org.gnome.ShellSearchProvider2 DBus interface
|
||||
[Cosimo; #689735, #690009]
|
||||
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
|
||||
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
|
||||
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
|
||||
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
|
||||
* Skip diacritical marks in search terms [Aleksander; #648587]
|
||||
* Expose all engine options in input sources [Giovanni, Rui; #682318]
|
||||
* Add input source switcher popup [Rui; #682315]
|
||||
* Add minimal support for InfiniBand in network menu [Dan; #677150]
|
||||
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
|
||||
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
|
||||
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
|
||||
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
|
||||
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
|
||||
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
|
||||
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
|
||||
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
|
||||
#690241, #690312, #690175, #687955, #650843, #688234, #690427
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
|
||||
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
|
||||
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
|
||||
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
|
||||
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
|
||||
|
||||
Translations:
|
||||
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
|
||||
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
|
||||
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
|
||||
Daniel Mustieles [es], Dušan Kazik [sk]
|
||||
|
||||
3.7.2
|
||||
=====
|
||||
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
|
||||
* gdm: Move logo into the panel [Florian; #685852]
|
||||
* Hide notifications when closed button is clicked [Jasper, Florian; #682237]
|
||||
* Tweak screenShield animations [Rui; #686745]
|
||||
* Restore Fittsability of summary items in message tray [Florian; #686474]
|
||||
* Save screencasts as recent item [Ray; #680647]
|
||||
* overview: Resize window captions on content change [Giovanni, Alex; #620874]
|
||||
* App search: Match GenericName too [Matthias; #687121]
|
||||
* runDialog: Better match style of other modal dialogs [Florian, Allan; #687127]
|
||||
* Improve the button insensitive style [Stéphane; #687110]
|
||||
* network: Don't use a global switch for all VPN connections [Giovanni; #682929]
|
||||
* appMenu: Update on icon theme changes [Florian; #687224]
|
||||
* Show 'Log out' in more situations [Matthias; #686736]
|
||||
* Add a setting to force the 'Log out' menuitem [Matthias; #686057]
|
||||
* overview: Improve styling of search box [Stéphane; #686479]
|
||||
* Implement 'disable-user-list' in login screen [Ray; #660660]
|
||||
* Fix auto-scroll to bottom in chat notifications [Sjoerd; #686571]
|
||||
* Show feedback notifications when user is busy [Stéphane; #662900]
|
||||
* Disable login button when there is no input [Stéphane; #687112]
|
||||
* Use non-linear overview shade for background [Giovanni, Pierre-Eric; #669798]
|
||||
* Reduce blocking in compositor thread [Simon, Jasper; #687465]
|
||||
* network: new country-specific type to gather providers [Aleksander; #687356]
|
||||
* Update man page [Matthias; #680601]
|
||||
* st-entry: Change the pointer cursor on enter/leave events [Thomas; #687130]
|
||||
* screenShield: Blur and desaturate the background [Giovanni, Cosimo; #682536]
|
||||
* Change height of chat notifications to have more context [Carlos; #665255]
|
||||
* screenShield: Account for motion velocity when hiding [Giovanni; #682537]
|
||||
* screenShield: hide the cursor while the lock screen is on [Giovanni; #682535]
|
||||
* Support remote search provider settings [Cosimo; #687491]
|
||||
* unlockDialog: Improve label of confirmation button [Stéphane; #687656]
|
||||
* userMenu: Rename "System Settings" item to "Settings" [Elad; #687738]
|
||||
* messageTray: Add keybinding to focus current notification [Stéphane; #652082]
|
||||
* Remove shell-screen-grabber [Neil; #685915]
|
||||
* main: Stop using Metacity's keybinding files [Florian; #687672]
|
||||
* Bluetooth: Remove ObexFTP functionality [Bastien; #688160]
|
||||
* a11y: Also set WM theme when HighContrast is switched on [Cosimo; #688256]
|
||||
* network: Rework multiple NIC support [Giovanni; #677142]
|
||||
* Rework keybindings to allow selective blocking/processing [Florian; #688202]
|
||||
* recorder: Show indicator on primary monitor [Adel; #688470]
|
||||
* recorder: Set frame duration to fix broken video headers [Adel; #688487]
|
||||
* Misc. bugfixes and cleanups [Florian, Jasper, Giovanni, Matthew, Stéphane,
|
||||
Allan, Daiki, Owen, Alejandro, Jean-François, Cosimo, Sebastian, Adel, Alban;
|
||||
#686484, #686728, #686805, #686574, #686763, #682428, #687132, #685239,
|
||||
#687189, #687226, #658091, #670687, #687457, #687242, #687287, #687020,
|
||||
#686583, #661194, #687491, #657315, #687958, #683986, #688089, #687708,
|
||||
#686530, #684810, #688181, #688475, #688557, #688507, #638351]
|
||||
|
||||
Contributors:
|
||||
Elad Alfassa, Matthew Barnes, Alban Browaeys, Giovanni Campagna,
|
||||
Cosimo Cecchi, Matthias Clasen, Allan Day, Stéphane Démurget,
|
||||
Jean-François Fortin Tam, Adel Gadllah, Alex Hultman, Sebastian Keller,
|
||||
Rui Matos, Simon McVittie, Aleksander Morgado, Florian Müllner,
|
||||
Bastien Nocera, Pierre-Eric Pelloux-Prayer, Alejandro Piñeiro, Neil Roberts,
|
||||
Sjoerd Simons, Carlos Soriano Sánchez, Jasper St. Pierre, Ray Strode,
|
||||
Owen Taylor, Daiki Ueno, Thomas Wood
|
||||
|
||||
Translations:
|
||||
Dušan Kazik [sk], Pavol Klačanský [sk], Piotr Drąg [pl], Yuri Myasoedov [ru],
|
||||
Marek Černocký [cs], Kjartan Maraas [nb], Wolfgang Stöggl [de],
|
||||
Yaron Shahrabani [he], Fran Diéguez [gl], Mattias Põldaru [et]
|
||||
|
||||
3.7.1
|
||||
=====
|
||||
* Add shortcut to open application view directly [Jeremy; #685738]
|
||||
* Expose '<Super>F10' shortcut in System Settings [Florian; #672909]
|
||||
* Clean up timestamp format in chat notifications [Carlos; #680989]
|
||||
* loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247]
|
||||
* Update textures automatically on file changes [Florian; #679268]
|
||||
* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
|
||||
* Add missing translations for GSetting schema [Giovanni; #686413]
|
||||
* Hide workspace switcher completely when it's not necessary [Seif; #686483]
|
||||
* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
|
||||
* Port to GnomeIdleMonitor [Jasper; #682224]
|
||||
* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
|
||||
* Allow testing GDM login dialog from the session [Giovanni; #683725]
|
||||
* Use all available space for windows in window picker [Jasper, Pierre-Eric;
|
||||
#582650]
|
||||
* Use logind for suspend if available [Florian; #686482]
|
||||
* Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426,
|
||||
#686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy,
|
||||
Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer,
|
||||
Carlos Soriano, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be],
|
||||
Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja],
|
||||
Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el]
|
||||
|
||||
3.6.1
|
||||
=====
|
||||
* dash: Make padding even on the top/bottom of the dash [Jasper; #684619]
|
||||
* Fix a crash when dragging search results [Jasper; #684888]
|
||||
* workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641]
|
||||
* Really hide 'Show Keyboard Layout' on the lock screen [Matthias]
|
||||
* Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355]
|
||||
* Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian;
|
||||
#684633, #685914]
|
||||
* Disable hotplug sniffer on remote filesystems [Jasper; #684093]
|
||||
* userMenu: Remove 'Switch Session' item [Florian; #685062]
|
||||
* unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444]
|
||||
* messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342]
|
||||
* Fix crash on dragging windows between workspaces [Ryan; #681399]
|
||||
* userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536]
|
||||
* gdm: Fix key-focus on first user [Adel; #684650]
|
||||
* Make grid button insensitive when dragging non-favorites [Jasper; #685313]
|
||||
* Calendar: hide all actions when on the login screen [Matthias; #685142]
|
||||
* Adapt unlock dialog layout for the login screen [Florian; #685201]
|
||||
* Make focus-follows-mouse work better with Shell UI [Florian; #678169]
|
||||
* Improve look of screen shield [Jasper; #685919]
|
||||
* Fix keynav in the login screen [Florian; #684730]
|
||||
* dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050]
|
||||
* unlockDialog: Reset UI on verification failure [Giovanni; #685441]
|
||||
* Show unlock dialog on primary monitor when using keynav [Giovanni; #685855]
|
||||
* Fix height changes of entries when entering text [Florian; #685534]
|
||||
* Fix show-apps label after successful drags [Florian; #684627]
|
||||
* Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063
|
||||
|
||||
Contributors:
|
||||
Jasper St. Pierre
|
||||
Matthias Clasen
|
||||
Owen Taylor
|
||||
Olivier Blin
|
||||
Florian Müllner
|
||||
Ryan Lortie
|
||||
Adel Gadllah
|
||||
Tanner Doshier
|
||||
Tim Lunn
|
||||
Giovanni Campagna
|
||||
|
||||
Translations:
|
||||
Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da],
|
||||
Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB],
|
||||
Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga],
|
||||
Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt],
|
||||
Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da],
|
||||
Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr],
|
||||
Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN],
|
||||
Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk],
|
||||
Alexandre Franke [fr], Piotr Drąg [pl]
|
||||
|
||||
3.6.0
|
||||
=====
|
||||
* keyboard: Make input source items accessible [Florian; #684462]
|
||||
* Don't show network dialogs in the lock screen [Giovanni; #684384]
|
||||
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
|
||||
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
|
||||
* Fix alignment of search section headers in RTL locales [Florian; #684379]
|
||||
* screenShield: Fix unlock animation [Florian; #684591]
|
||||
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
|
||||
* userMenu: Fix texture updates on icon changes [Florian; #679268]
|
||||
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
|
||||
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
|
||||
#683546]
|
||||
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
|
||||
Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
|
||||
Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
|
||||
Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
|
||||
Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
|
||||
Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
|
||||
Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
|
||||
Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
|
||||
Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
|
||||
YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
|
||||
Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
|
||||
Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
|
||||
Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
|
||||
|
||||
3.5.92
|
||||
======
|
||||
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
|
||||
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
|
||||
* Add zoom out animation on login [Jasper; #683170]
|
||||
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
|
||||
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
|
||||
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
|
||||
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
|
||||
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
|
||||
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
|
||||
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
|
||||
* Change stage background color to grey [Adel; #683514]
|
||||
* messageTray: Update style of summary counters [Debarshi; #682891]
|
||||
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
|
||||
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
|
||||
* Hide the a11y menu in the lock screen, but show it in the login screen
|
||||
[Giovanni; #682542]
|
||||
* Fix show-apps button dropping off the dash [Florian; #683340]
|
||||
* Fix committing strings to shell entries from input method [Florian; #658325]
|
||||
* Make IBus display strings consistent with control-center [Rui; #683124]
|
||||
* Fix missing short codes for some input sources [Rui; #683613]
|
||||
* Remove support for long-press from entry context menus [Jasper; #683509]
|
||||
* screenShield: Add box-shadow to the shield [Florian]
|
||||
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
|
||||
* Fix extension styling [Giovanni; #682128]
|
||||
* Fix on-screen keyboard not working with system-modal dialogs
|
||||
[Giovanni; #664309]
|
||||
* Fix insensitive styling for popup menu items [Giovanni; #683988]
|
||||
* Disable the message tray dwell when the user is interacting [Owen; #683811]
|
||||
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
|
||||
* Autostart fprintd when necessary [Ray; #683131]
|
||||
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
|
||||
* Make Return key dismiss screenshield [Ray; #683889]
|
||||
* Fix keyboard navigation in the message tray [Florian; #682243]
|
||||
* Remove the places & devices search provider [Giovanni; #683506]
|
||||
* Enable hot corner while the message tray is up [Florian; #682255]
|
||||
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
|
||||
* Fix fish flickering [Giovanni; #684154]
|
||||
* Fix extension ordering with !important [Jasper; #684163]
|
||||
* Allow the shell to run without the screenshield [Giovanni; #683060]
|
||||
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
|
||||
* Improve transition to the login dialog [Jasper; #682428]
|
||||
* Keep unlock dialog around until shield animation ends [Florian; #684342]
|
||||
* Expose shell keybindings in System Settings [Florian; #671010]
|
||||
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
|
||||
#672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
|
||||
#683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
|
||||
#683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
|
||||
#684162, #684214, #684343]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
|
||||
Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
|
||||
Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||
Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
|
||||
Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
|
||||
Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
|
||||
Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
|
||||
Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
|
||||
Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
|
||||
Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
|
||||
Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
|
||||
Fran Diéguez [gl], Praveen Illa [te]
|
||||
|
||||
3.5.91
|
||||
======
|
||||
* Improve modal dialog styling of network secret prompts [Jasper; #682412]
|
||||
* Fix visibility of non-active workspaces during overview transition
|
||||
[Florian; #682002]
|
||||
* Improve scrollbar theming [Cosimo; #682476]
|
||||
* Make sure the app menu remains hidden in locked state [Florian; #682475]
|
||||
* Add tooltip to show-applications icon [Jasper; #682445]
|
||||
* Do not add duplicate remote search providers [Florian; #682470]
|
||||
* Handle 'popup-menu' signal on summary items [Florian; #682486]
|
||||
* Fix dwelling during mouse-down [Owen; #682385]
|
||||
* Set label actor for endSessionDialog.ListItem [Alejandro; #677503]
|
||||
* Don't match on comments when searching applications [Florian; #682529]
|
||||
* Make workspace selector more similar to the mockup [Stefano; #662087]
|
||||
* Fix extension installation and reloading [Jasper; #682578]
|
||||
* Hide removable devices in the lock screen [Giovanni; #681143]
|
||||
* Reset cancellable after hitting Escape on login screen [Alban; #681537]
|
||||
* Fix suspend from the user menu [Giovanni; #682746]
|
||||
* Set label actor for summary items in message tray [Alejandro; #677229]
|
||||
* Set label for the "Show applications" dash button [Alejandro; #682366]
|
||||
* Load extensions as late as possible [Jasper; #682822]
|
||||
* Improve mount operation dialogs [Jon; #682645]
|
||||
* Remove "Connect to ..." item from places search [Florian; #682817]
|
||||
* Don't auto-expand notifications with actions [Giovanni; #682738]
|
||||
* Add a new lock screen menu to combine volume network and power
|
||||
[Giovanni; #682540]
|
||||
* Add support for pre-edit to StIMText [Daiki; #664041]
|
||||
* Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540]
|
||||
* Use monitor geometry for dwelling [Florian; #683044]
|
||||
* Add support for surrounding-text to StIMText [Daiki; #683015]
|
||||
* Improve the placement and style of the "No results" text [Jasper; #683135]
|
||||
* Remove broken network device activation policy [Giovanni; #683136]
|
||||
* Hide power status icon when no battery is present [Tim; #683080]
|
||||
* Ensure summary items are square and have spacing [Debarshi; #682248]
|
||||
* Fix close buttons overlapping screen edge [Debarshi; #682343]
|
||||
* Escape the tray when a legacy icon is clicked [Giovanni; #682244]
|
||||
* Update arrow in the screen shield to match latest mockups [Giovanni; #682285]
|
||||
* Allow lifting the screen shield with the mouse wheel [Giovanni; #683164]
|
||||
* Make sure to show the app menu after unlocking the screen [Jasper; #683154]
|
||||
* Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui;
|
||||
#582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546,
|
||||
#682683, #682710, #682998, #683073, #683137, #683156]
|
||||
|
||||
Contributors:
|
||||
Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
|
||||
Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner,
|
||||
Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno
|
||||
|
||||
Translations:
|
||||
Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb],
|
||||
Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru],
|
||||
Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el],
|
||||
Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi],
|
||||
A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it]
|
||||
|
||||
3.5.90
|
||||
======
|
||||
* Use symbolic icons for workspace switch OSD [Jon; #680738]
|
||||
* Lock screen improvements:
|
||||
- Hide user menu and a11y menu in the screen lock [Giovanni; #681143]
|
||||
- Bump the lock screen slightly when pressing a key [Giovanni; #681143]
|
||||
- Constrain vertical movement of the screen shield [Giovanni; #681143]
|
||||
- Return to lock screen on idle [Giovanni; #682041]
|
||||
- Unlock screen automatically after fast-user switching [Giovanni; #682096]
|
||||
- Fix "other user" label [Ray; #681750]
|
||||
* Constrain content of system modals to primary monitor [#681743]
|
||||
* Respect automatic lock setting on suspend/user-switch [Giovanni; #680231]
|
||||
* Improve styling of keyring prompt [Jasper; #681821]
|
||||
* Do not hard-code <super> as overlay-key [Florian; #665547]
|
||||
* Update style of attached modal dialogs [Florian; #681601]
|
||||
* a11y: allow navigation on non reactive items [Alejandro; #667439, #667439]
|
||||
* Implement mode-less overview design [Joost, Florian; #682109]
|
||||
* Implement message-tray redesign:
|
||||
- Restyle the message tray [Ana, Allan, Florian; #677213, #682342]
|
||||
- Move the desktop upwards when showing the tray [Debarshi; #681392]
|
||||
- Add a close button to notifications [Ana, Jasper; #682253]
|
||||
- Add a keybinding to toggle the tray [Debarshi; #681392]
|
||||
- Make the tray keyboard navigable [Debarshi; #681519]
|
||||
- Add dwelling at the bottom of the screen to open the tray [Owen; #682310]
|
||||
- Don't time out banners when the user is inactive [Marina, Jasper]
|
||||
- Misc fixes and cleanups [Jasper, Marina]
|
||||
* Fix showing "Next Week" on Sundays [Sebastian; #682198]
|
||||
* Delay restoring IM presence until the network comes up [Florian; #677982]
|
||||
* Display enterprise login hint [Ray; #681975]
|
||||
* Ignore unrecognized/irrelevant network devices/connections [Dan; #682364]
|
||||
* Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico;
|
||||
#643687, #682045, #682189]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann,
|
||||
Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro,
|
||||
Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor,
|
||||
Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa],
|
||||
Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl],
|
||||
Мирослав Николић [sr, sr@latin]
|
||||
|
||||
3.5.5
|
||||
=====
|
||||
* Update style to match mockups [Allan]
|
||||
- improve calendar layout and legibility
|
||||
- update notifications and menus
|
||||
- use a common style for entries
|
||||
- update scrollbars to match GTK+
|
||||
- improve clock/unlock button in lock screen
|
||||
- update polkit dialogs [Jasper]
|
||||
* Fix login dialog growing when selecting different users [Florian; #675076]
|
||||
* Implement screen lock in the shell [Giovanni]
|
||||
- restructure login code to be shared with session unlock [#619955]
|
||||
- add initial screen shield / unlock dialog implementation [#619955]
|
||||
- implement (optional) notification list on lock shield [#619955]
|
||||
- update login dialog style to match lock screen [#619955]
|
||||
- filter notifications to only show new ones on the screen lock [#681143]
|
||||
- make notifications scrollable if necessary [#681143]
|
||||
- use correct application names in notifications [#681143]
|
||||
- allow to return to the shield by pressing Escape [#681143]
|
||||
* Minor login dialog improvements [Florian]
|
||||
- update style to match the overall visuals [#660913]
|
||||
- indicate whether users are logged in [#658185]
|
||||
* Add support for background-repeat CSS property [Jasper; #680801]
|
||||
* Add :active pseudo class on scroll handles [Florian]
|
||||
* Remove markup from translated strings [Matthias; #681270]
|
||||
* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
|
||||
Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
|
||||
#681101, #681382]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||
Piotr Drąg , Florian Müllner, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
|
||||
Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
|
||||
Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
|
||||
Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
|
||||
Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
|
||||
|
||||
3.5.4
|
||||
=====
|
||||
* Fix wrong result handling of remote calls [Florian; #678852]
|
||||
* dateMenu: Fix regression that caused no date to be displayed [Colin]
|
||||
* WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992]
|
||||
* Show the workspace switcher for move-to-workspace keybinding
|
||||
[Giovanni, Jasper; #674104, #660839, #679005]
|
||||
* userMenu: Move "Power off" item to the bottom [Florian; #678887]
|
||||
* Remove contacts search provider [Florian, Rui; #677442]
|
||||
* network: don't ask for always-ask secrets when interaction isn't allowed
|
||||
[Dan; #679091]
|
||||
* PolkitAgent: Look for the right password prompt [Matthias; #675300]
|
||||
* Implement extension updates [Jasper; #679099]
|
||||
* userMenu: Don't disconnect account signals when disabled [Guillaume; #669112]
|
||||
* Fix startup notification when opening calendar [Florian; #677907]
|
||||
* networkAgent: use absolute path if configured [Clemens; #679212]
|
||||
* recorder: Port to GStreamer-1.0 API [Florian; #679445]
|
||||
* telepathyClient: don't add log messages on presence changes [Ana; #669508]
|
||||
* lookingGlass: Don't use a signal callback on 'paint' to draw the border
|
||||
[Jasper; #679464]
|
||||
* Add support for inhibiting automount [Hans; #678597]
|
||||
* Implemented banner support for the login screen [Matthias, Marius; #665346]
|
||||
* boxpointer: Flip side if we would end outside the monitor [Rui; #678164]
|
||||
* boxpointer: Change 'animate' parameter on show/hide to a bitmask
|
||||
[Rui; #678337]
|
||||
* Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499]
|
||||
* UserMenu: show "Install Updates & Restart" when appropriate
|
||||
[Giovanni; #677394, #680080]
|
||||
* messageTray: don't show the message tray when a new notification is shown
|
||||
[Ana; #677210]
|
||||
* panel: don't break when indicator has no menu [Jean-Philippe; #678694]
|
||||
* appMenu: Disable app menu during startup animations [Florian; #672322]
|
||||
* autorun: Add a notification when unmounting drives [Cosimo; #676125]
|
||||
* st-icon: Fix potential crash involving shadows [Jasper; #679776]
|
||||
* Remove manual garbage collection on tweeners end [Cosimo; #679832]
|
||||
* dash: hide tooltips when overview begins hiding [Stefano; #674241]
|
||||
* Update modal dialog animation for new centered position [Florian; #674499]
|
||||
* calendar: Fix grid lines in RTL locales [Florian; #679879]
|
||||
* Integrate IBus with keyboard indicator [Rui; #641531]
|
||||
* Move ibus status icon under keyboard [Matthias]
|
||||
* gdm: port from libgdmgreeter to libgdm [Ray; #676401]
|
||||
* Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico;
|
||||
#678978, #672790, #679847, #679944]
|
||||
|
||||
Contributors:
|
||||
Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi,
|
||||
Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini,
|
||||
Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska,
|
||||
Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi],
|
||||
Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg],
|
||||
Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he],
|
||||
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be],
|
||||
Praveen Illa [te]
|
||||
|
||||
3.5.3
|
||||
=====
|
||||
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
|
||||
* messageTray: Don't show non urgent notifications while in fullscreen
|
||||
[Adel; #677590]
|
||||
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
|
||||
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
|
||||
* appDisplay: Don't show apps in NoDisplay categories in the All view
|
||||
[Jasper; #658176]
|
||||
* st: Trigger theme updates on resolution changes [Florian; #677975]
|
||||
* Always enable a11y [Bastien; #678095]
|
||||
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
|
||||
* shell-app: Update app menu if necessary [Florian; #676238]
|
||||
* Enable the Screen Reader menu item [Matthias; #663256]
|
||||
* Disable unredirection when a modal operation is active [Giovanni]
|
||||
* Make folks optional [Colin]
|
||||
* Improve mount-operation support [Cosimo]
|
||||
- Fix exception when showing password entry [#678428]
|
||||
- Close the password entry on operation abort [#673787]
|
||||
- autorun: Don't allow autorun for things we mount on startup [#660595]
|
||||
- Turn passphrase prompt into a dialog [#674962]
|
||||
- Implement org.Gtk.MountOperationHandler [#678516]
|
||||
* Network menu improvements
|
||||
- Sort Wifi networks by strength [Giovanni; #658946]
|
||||
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
|
||||
* clock: Switch to using GnomeWallClock [Colin; #657074]
|
||||
* remoteSearch: Allow to reference .desktop file for Title/Icon
|
||||
[Florian; #678816]
|
||||
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
|
||||
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
|
||||
#658955, #677497, #678396, #678502]
|
||||
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
|
||||
|
||||
Contributors:
|
||||
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
|
||||
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
|
||||
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
|
||||
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
|
||||
|
||||
3.5.2
|
||||
=====
|
||||
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
|
||||
* popupMenu: make sure to break the grab when the slider is not visible
|
||||
[Stefano; #672713]
|
||||
* st-theme-node-drawing: Don't use GL types [Neil; #672711]
|
||||
* Mirror Evolution calendar settings into our own schema [Owen; #674424]
|
||||
* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
|
||||
* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
|
||||
* NetworkMenu: use network-offline while loading [Giovanni; #674426]
|
||||
* lookingGlass: Remove the Errors tab [Jasper; #675104]
|
||||
* searchDisplay: Reset keyboard focus after displaying async results
|
||||
[Rui; #675078]
|
||||
* gdm: don't fail if fprintd is unavailable [Ray; #675006]
|
||||
* messageTray: Fix scrolling up [Jasper; #661615]
|
||||
* main: Close the recorder instead of pausing it [Rui; #675128]
|
||||
* Accessibility [Alejandro]
|
||||
- Use the proper label_actor for date menu on top panel [#675307]
|
||||
- Set the proper role/label_actor for SearchResult.content [#672242]
|
||||
- do not expose a label text if 'hidden' style class is used [#675341]
|
||||
* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
|
||||
* theme: use a smaller border-radius for top bar [Jakub; #672430]
|
||||
* placeDisplay: use new bookmark file location [Matthias; #675443]
|
||||
* port all synchronous search providers to the async API [Jasper, Rui; #675328]
|
||||
* NetworkAgent: disallow multiple requests for the same connection/setting
|
||||
[Giovanni; #674961]
|
||||
* userMenu: Update to latest mockups [Florian; #675802]
|
||||
* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
|
||||
* messageTray: Make Source usable without subclassing [Jasper; #661236]
|
||||
* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
|
||||
* Fix formatting of bluetooth passkey [Florian; #651251]
|
||||
* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
|
||||
* Don't use global.log() [Jasper; #675790]
|
||||
* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
|
||||
* shell-app: Raise windows in reverse order to preserve the stacking
|
||||
[Rui; #676371]
|
||||
* Generalize gdm-mode [Florian; #676156]
|
||||
* Switch string formatting to the one inside gjs [Jasper; #675479]
|
||||
* extensionUtils: Support subdirectories in getCurrentExtension
|
||||
[Jasper; #677001]
|
||||
* panel: Refuse to add (legacy) status icons not part of the session mode
|
||||
[Florian; #677058]
|
||||
* Add an initial-setup mode [Matthias; #676697]
|
||||
* status/keyboard: Port to the new input sources settings [Rui; #641531]
|
||||
* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
|
||||
* userMenu: Indicate progress on status changes [Florian; #659067]
|
||||
* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
|
||||
* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
|
||||
* endSessionDialog: Factor out _updateDescription from _updateContent
|
||||
[Alejandro; #674210]
|
||||
* Fix empathy's appMenu freezing the shell [Alban; #676447]
|
||||
* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
|
||||
#672272]
|
||||
* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
|
||||
Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
|
||||
#675370, #676347, #676806, #677097]
|
||||
|
||||
Contributors:
|
||||
Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
|
||||
Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
|
||||
Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
|
||||
Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
|
||||
Colin Walters, Dan Winship, Ron Yorston
|
||||
|
||||
Translations:
|
||||
OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
|
||||
Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
|
||||
Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
|
||||
Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
|
||||
Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
|
||||
|
||||
3.4.1
|
||||
=====
|
||||
* Fix crash that occurred when an icon theme change caused unexpected
|
||||
reentrancy in the icon loading code [Jasper; #673512]
|
||||
* Don't show system and other disabled users in the GDM user list
|
||||
[Adel; #673784]
|
||||
* Make gnome-shell-calendar-server initialize GTK+ so it can display
|
||||
password prompts if needed [#673608; Owen, Rico]
|
||||
* Adapt to Mutter API change for keybinding addition [Florian; #673014]
|
||||
* Fix crash when an extension was installed as both a user extension
|
||||
and a system extension [#673613; Jasper]
|
||||
* Fix bug where chat entry could end up partially offscreen [Joost, 661944]
|
||||
* Fix problem where icons weren't updating when theme was changed
|
||||
[#672941; Florian]
|
||||
* Look for Evolution calendar settings in GSettings, not GConf [#673610; Owen]
|
||||
* Add <super>F10 for the application menu [#672909; Florian]
|
||||
* Fix %Id format characters to work in translations [#673106; Cosimo]
|
||||
(were already used in fa translation)
|
||||
* Fix error when NetworkManager restarts [#673043; Giovanni]
|
||||
* Improve efficiency of overview redraws by working around Clutter issue
|
||||
[Stefano; #670636]
|
||||
* Misc bug fixes [Florian, Giovanni, Jasper, Rui, Stefano;
|
||||
#672592, #672641, #672719, #673187, #673233, #673656]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, Adel Gadllah, Rui Matos,
|
||||
Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz,
|
||||
Joost Verdoorn
|
||||
|
||||
Translations:
|
||||
Khaled Hosny [ar], Ihar Hrachyshka [be], Alexander Shopov [bg], Gil Forcada,
|
||||
Jordi Serratosa [ca], Petr Kovar [cs], Bruce Cowan [en_GB],
|
||||
Carles Ferrando [ca@valencia], Wolfgang Stöggl [de], Daniel Mustieles [es],
|
||||
Arash Mousavi [fa], Bruno Brouard [fr], Fran Diéguez [gl],
|
||||
Sweta Kothari [gu], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
||||
Shankar Prasad [kn], Žygimantas Beručka [lt], Rudolfs Mazurs [lv],
|
||||
Sandeep Sheshrao Shedmake [mr], Kjartan Maraas [nb], Piotr Drąg [pl],
|
||||
Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl],
|
||||
Miroslav Nikolić [sr], Sasi Bhushan, Praveen Illa [te], Yinghua Wang [zh_CN]
|
||||
|
||||
3.4.0
|
||||
=====
|
||||
* Don't crash when taking screenshots [Jasper; #672775]
|
||||
* Fix dialog-resizing problem [Florian; #672543]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
|
||||
Alexander Shopov [bg], Marek Černocký [cs], Jiri Grönroos, Timo Jyrinki [fi],
|
||||
Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
|
||||
Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
|
||||
Mattias Põldaru [et], Changwoo Ryu [ko], Rudolfs Mazurs [lv],
|
||||
Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
|
||||
Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.3.92
|
||||
======
|
||||
* Add shell-dialogs for GNOME Keyring prompts [Stef; #652459, #652460, #671034]
|
||||
* When the user returns from idle, bring up the message tray if there were
|
||||
messages while they were away [Marina; #643014]
|
||||
* https://live.gnome.org/EveryDetailMatters
|
||||
- Make the workspace thumbnails clickable all the way to the edge of the
|
||||
screen [Stefano; #643319]
|
||||
- Don't slide out the workspace thumbnails if the mouse is over them when
|
||||
entering the overview [Joost, #651092]
|
||||
- Fix placeholder jumps while dragging a dash item [Joost; #651842]
|
||||
- Don't favorite apps if they are dropped back at the same position
|
||||
[Jean-Philippe; #656333]
|
||||
- To avoid confusion, don't allow removing running apps from favorites
|
||||
[Florian; #644853]
|
||||
- Fix creation of new workspaces by dragging application launchers
|
||||
[Stefano; #664202]
|
||||
- Make it easier to drag dash items without triggering the menu
|
||||
[Florian; #637103]
|
||||
* Accessibility [Alejandro]
|
||||
- Add StWidget API for easily adding accessible states and setting roles,
|
||||
names [#668366, #667432, #671378]
|
||||
- Set accessibility information on UI elements
|
||||
[#644255, #667432, #668361, #672047, #670308, #670312, #670719, #671404]
|
||||
* Improve key-navigation in the overview [Rui, Florian; #663901]
|
||||
* Key navigation bug fixes [Rui, Florian; #662493, #663437, #665215, #671998]
|
||||
* Honor a 'org.gnome.shell.overrides.dynamic-workspaces' setting that
|
||||
determines whether the workspace count is dynamic and unsaved in GSettings
|
||||
or static and saved. [Florian; #671568]
|
||||
* Avoid saving user presence to GSettings when not necessary
|
||||
[Florian; #665701, #668214]
|
||||
* Save screencasts in the users Videos/ directory [Adel; #670749]
|
||||
Use a "human readable" filename [Florian, Adel, Ray; #670753]
|
||||
* Allow dragging from the empty part of the top panel to unmaximize a window
|
||||
[Florian; #666359]
|
||||
* Fix hangs that could occur when switching away to a VT [Ray; #653833]
|
||||
* Fix problems with installing from extensions.gnome.org [Giovanni; #671134]
|
||||
* Fix locking the screen when suspending via menu [David, Gert; #670820]
|
||||
* Fix browser plugin with Konqueror and Opera [Jasper]
|
||||
* Fix shell restart not to bring up failure screen [Giovanni; #648384]
|
||||
* Reorganize and clean up CSS theming [Allan; #668209]
|
||||
* Improve appearance of modal dialogs [Allan, Florian; #670227, #668209]
|
||||
* Update the calendar code to use ECalClient [Giovanni; #671177]
|
||||
* Update jhbuild script to use the main moduleset [Owen, Will; #668440]
|
||||
* StTextureCache: code cleanup, evict unused icons, merge together
|
||||
simulataneous requests for the same icon [Jasper; #670771, #671656, #672273]
|
||||
* Clean up St for recent Clutter changes and fix bugs. StContainer and
|
||||
StGroup are removed [Jasper, Florian; #670034, #670640, #670904]
|
||||
* Code cleanup [Adel, Jasper, Rui; #613194, #671086, #671103]
|
||||
* Misc bug fixes
|
||||
[Adel, Colin G, Cosimo, Florian, Giovanni, Jasper, Marius, Rui, Stefano;
|
||||
#651130, #658946, #667552, #670076, #671001, #670979, #671410, #671411,
|
||||
#671556, #671656, #671657, #672011, #672024, #672240, #672265, #672270,
|
||||
#672321, #672326, #672413, #672471]
|
||||
|
||||
Contributors:
|
||||
Jean-Philippe Braun, Giovanni Campagna, Cosimo Cecchi, Allan Day,
|
||||
Stefano Facchini, David Foerster, Adel Gadllah, Marius Gedminas,
|
||||
Colin Guthrie, Gert Michael Kulyk, William Lachance, Rui Matos,
|
||||
Florian Müllner, Alejandro Piñeiro, Jan Alexander Steffens,
|
||||
Jasper St. Pierre, Ray Strode, Owen Taylor, Joost Verdoorn, Stef Walter,
|
||||
Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
|
||||
Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cs],
|
||||
Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
|
||||
Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
|
||||
Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
|
||||
Bruno Brouard [fr], Fran Diéguez [gl], Sweta Kothari [gu],
|
||||
Yaron Shahrabani [he], Gabor Kelemen [hu], Jiro Matsuzawa [ja],
|
||||
Baurzhan Muftakhidinov [kk], Seong-ho Cho [ko], Žygimantas Beručka [lt],
|
||||
Anita Reitere [lv], Anish A, Praveen Arimbrathodiyil, Mohammed Sadiq [ml],
|
||||
fKjartan Maraas [nb], Wouter Bolsterlee [nl], A S Alam [pa], Piotr Drąg [pl],
|
||||
Duarte Loreto [pt], Jonh Wendell [pt_BR], Yuri Myasoedov [ru],
|
||||
Matej Urbančič [sl], Miroslav Nikolić [sr], Tirumurti Vasudevan [ta],
|
||||
Sasi Bhushan, Krishnababu Krothapalli [te], Daniel Korostil [uk],
|
||||
Nguyễn Thái Ngọc Duy [vi], YunQiang Su, Yinghua Wang [zh_CN],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.3.90
|
||||
======
|
||||
|
||||
* Allow other applications to implement search providers via D-Bus
|
||||
[Florian; #663125, #670148]
|
||||
* Remove "Recent Items" search, as replaced by Documents search
|
||||
[Florian; #663125]
|
||||
* Allow NetworkManager VPN plugins to use a shell-themed dialog
|
||||
[Giovanni; #658484]
|
||||
* Port away from deprecated Clutter API [Jasper, Florian, Adel; #670034]
|
||||
- StTooltip is removed
|
||||
- StWidget is now a concrete class and can be instantiated
|
||||
- st_container_destroy_children(), st_box_layout_insert_actor(),
|
||||
and other functions removed in favor of new replacements in Clutter.
|
||||
* Use systemd for console/session handling when available [Lennart]
|
||||
* Visual improvements to contact search, padding, top panel, checkboxes
|
||||
[Allan, Florian, Jakub; #669489, #669811, #669993]
|
||||
* Add a include_cursor parameter to Screenshot and ScreenshotWindow
|
||||
D-Bus methods [Adel; #670086]
|
||||
* Add a "FlashArea" D-Bus method to do the screenshot flash without a
|
||||
screenshot [Adel; #669660]
|
||||
* Build fixes [Adel, Giovanni, Jasper; #658484, #669637]
|
||||
* Misc bug fixes [Adel, Florian, Giovanni, Guillaume, Jasper, Jeff,
|
||||
Marc-Antoine, Stef, Stefano, Will; #642135, #658484, #658908, #667694,
|
||||
#669098, #669921, #669662, #669694, #669776, #669887, #669921, #670005,
|
||||
#670006, #670319, #670418, #670489]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Allan Day, Guillaume Desmottes, Jeff Epler,
|
||||
Stefano Facchini, Adel Gadllah, Florian Müllner, Marc-Antoine Perennou,
|
||||
Jasper St. Pierre, Lennart Poettering, Jakub Steiner, Jasper St. Pierre,
|
||||
Will Thompson, Stef Walter
|
||||
|
||||
Translations:
|
||||
Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cs],
|
||||
Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
|
||||
Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
|
||||
A S Alam [pa], Matej Urbančič [sl], Miroslav Nikolić [sr],
|
||||
Praveen Illa [te], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.3.5
|
||||
=====
|
||||
|
||||
* Extension system: [Jasper; #668429]
|
||||
http://blog.mecheye.net/2012/02/more-extension-api-breaks/
|
||||
- Add a 'gnome-shell-extension-prefs' application for displaying extension
|
||||
preferences as provided by the extension in a prefs.js file.
|
||||
- Allow launching gnome-shell-extension-prefs from extensions.gnome.org
|
||||
throuhg the browser plugin.
|
||||
- Add ExtensionUtils.getCurrentExtension() for an extension to get a
|
||||
handle to an extension object, to get local imports or paths.
|
||||
- Add an onshellrestart callback to the browser plugin [Jasper; #668517]
|
||||
* Screenshots:
|
||||
- Move the screenshot "flash" to the shell [Cosimo; #668618]
|
||||
- Move saving screenshots to a thread [Adel; #652952]
|
||||
- Correctly screenshot rounded decorations [Jasper; #662486]
|
||||
* Screen recorder:
|
||||
- Change the default pipeline to favor speed over quality [Owen; #669066]
|
||||
- Drop frames to keep from running the user out of memory [Owen; #669066]
|
||||
* Work around a slow implementation of glReadPixels() in the Intel drivers,
|
||||
improving performance for screenshots and the screen recorder.
|
||||
[Owen; #669065]
|
||||
* Use Keywords: field in desktop files when search for applications
|
||||
[Florian; #609702]
|
||||
* Strip debian- when matching desktop file names [Jasper; #665647]
|
||||
* Fix up various problems from CSS background size-addition
|
||||
[Florian, Jasper; #668430, #633462]
|
||||
* UI tweaks and behavior fixes
|
||||
[Florian, Giovanni, Stefano; #643867, #666197, #664622]
|
||||
* Some improvements to exported accessibility information [Alejando; #667376]
|
||||
* Don't show contacts without IM information as offline [Florian; #662685]
|
||||
* Don't change status from hidden to extended_away when going idle
|
||||
[Florian; #642408]
|
||||
* Cleanups [Emmanuele, Jasper; #662152, #669239]
|
||||
* Misc bug fixes [Cosimo, Dan, Florian, Jasper, Rui, Stefano;
|
||||
#633462, #643867, #662213, #662386, #662747, #665000, #665017, #665322,
|
||||
#667371, #667860, #668020, #668517, #668541, #669236]
|
||||
|
||||
Contributors:
|
||||
Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
|
||||
Adel Gadllah, Rui Matos, Florian Müllner, Alejandro Piñeiro,
|
||||
Jasper St. Pierre, Owen Taylor, Dan Winship
|
||||
|
||||
Translations:
|
||||
Daniel Mustieles [es], Timo Jyrinki [fi], Seán de Búrca [ga],
|
||||
Fran Diéguez [gl], Kjartan Maraas [nb], Wouter Bolsterlee [nl],
|
||||
Danishka Navin [si], Yaron Shahrabani [he], Matej Urbančič [sl],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.3.4
|
||||
=====
|
||||
* https://live.gnome.org/EveryDetailMatters
|
||||
@ -73,7 +915,7 @@ Contributors:
|
||||
Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Petr Kovar [cz], Kris Thomsen [dk], Daniel Mustieles [es],
|
||||
Petr Kovar [cs], Kris Thomsen [dk], Daniel Mustieles [es],
|
||||
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
|
||||
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
|
||||
@ -236,7 +1078,7 @@ Contributors:
|
||||
Translations:
|
||||
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
|
||||
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
|
||||
Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
|
||||
Petr Kovar [cs], Mario Blättermann [de], Kris Thomsen [dk],
|
||||
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
|
||||
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
|
||||
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
||||
|
@ -13,6 +13,14 @@ PKG_NAME="gnome-shell"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Fetch submodules if needed
|
||||
if test ! -f src/gvc/Makefile.am;
|
||||
then
|
||||
echo "+ Setting up submodules"
|
||||
git submodule init
|
||||
fi
|
||||
git submodule update
|
||||
|
||||
which gnome-autogen.sh || {
|
||||
echo "You need to install gnome-common from GNOME Git (or from"
|
||||
echo "your OS vendor's package manager)."
|
||||
|
@ -41,7 +41,7 @@
|
||||
"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_API_VERSION 1
|
||||
#define PLUGIN_API_VERSION 5
|
||||
|
||||
typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
@ -104,7 +104,7 @@ check_origin_and_protocol (NPP instance)
|
||||
&location))
|
||||
goto out;
|
||||
|
||||
if (!NPVARIANT_IS_OBJECT (document))
|
||||
if (!NPVARIANT_IS_OBJECT (location))
|
||||
goto out;
|
||||
|
||||
hostname = get_string_property (instance,
|
||||
@ -161,6 +161,7 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
||||
plugin->newp = NPP_New;
|
||||
plugin->destroy = NPP_Destroy;
|
||||
plugin->getvalue = NPP_GetValue;
|
||||
plugin->setwindow = NPP_SetWindow;
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
@ -222,7 +223,7 @@ NPP_New(NPMIMEType mimetype,
|
||||
NULL, /* interface info */
|
||||
"org.gnome.Shell",
|
||||
"/org/gnome/Shell",
|
||||
"org.gnome.Shell",
|
||||
"org.gnome.Shell.Extensions",
|
||||
NULL, /* GCancellable */
|
||||
&error);
|
||||
if (!data->proxy)
|
||||
@ -265,8 +266,11 @@ typedef struct {
|
||||
NPObject parent;
|
||||
NPP instance;
|
||||
GDBusProxy *proxy;
|
||||
GSettings *settings;
|
||||
NPObject *listener;
|
||||
NPObject *restart_listener;
|
||||
gint signal_id;
|
||||
guint watch_name_id;
|
||||
} PluginObject;
|
||||
|
||||
static void
|
||||
@ -284,7 +288,7 @@ on_shell_signal (GDBusProxy *proxy,
|
||||
gint32 status;
|
||||
gchar *error;
|
||||
NPVariant args[3];
|
||||
NPVariant result;
|
||||
NPVariant result = { NPVariantType_Void };
|
||||
|
||||
g_variant_get (parameters, "(sis)", &uuid, &status, &error);
|
||||
STRINGZ_TO_NPVARIANT (uuid, args[0]);
|
||||
@ -300,6 +304,28 @@ on_shell_signal (GDBusProxy *proxy,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_shell_appeared (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
PluginObject *obj = (PluginObject*) user_data;
|
||||
|
||||
if (obj->restart_listener)
|
||||
{
|
||||
NPVariant result = { NPVariantType_Void };
|
||||
|
||||
funcs.invokeDefault (obj->instance, obj->restart_listener,
|
||||
NULL, 0, &result);
|
||||
|
||||
funcs.releasevariantvalue (&result);
|
||||
}
|
||||
}
|
||||
|
||||
#define SHELL_SCHEMA "org.gnome.shell"
|
||||
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
|
||||
|
||||
static NPObject *
|
||||
plugin_object_allocate (NPP instance,
|
||||
NPClass *klass)
|
||||
@ -309,9 +335,18 @@ plugin_object_allocate (NPP instance,
|
||||
|
||||
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;
|
||||
@ -328,41 +363,22 @@ plugin_object_deallocate (NPObject *npobj)
|
||||
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);
|
||||
}
|
||||
|
||||
static NPIdentifier api_version_id;
|
||||
static NPIdentifier shell_version_id;
|
||||
static NPIdentifier get_info_id;
|
||||
static NPIdentifier list_extensions_id;
|
||||
static NPIdentifier enable_extension_id;
|
||||
static NPIdentifier install_extension_id;
|
||||
static NPIdentifier uninstall_extension_id;
|
||||
static NPIdentifier onextension_changed_id;
|
||||
static NPIdentifier get_errors_id;
|
||||
|
||||
static bool
|
||||
plugin_object_has_method (NPObject *npobj,
|
||||
NPIdentifier name)
|
||||
{
|
||||
return (name == get_info_id ||
|
||||
name == list_extensions_id ||
|
||||
name == enable_extension_id ||
|
||||
name == install_extension_id ||
|
||||
name == uninstall_extension_id ||
|
||||
name == get_errors_id);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
uuid_is_valid (const gchar *uuid)
|
||||
uuid_is_valid (NPString string)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; uuid[i]; i ++)
|
||||
for (i = 0; i < string.UTF8Length; i++)
|
||||
{
|
||||
gchar c = uuid[i];
|
||||
gchar c = string.UTF8Characters[i];
|
||||
if (c < 32 || c >= 127)
|
||||
return FALSE;
|
||||
|
||||
@ -425,8 +441,73 @@ jsonify_variant (GVariant *variant,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_args (const gchar *format_str,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
gsize i;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (strlen (format_str) != argc)
|
||||
return FALSE;
|
||||
|
||||
va_start (args, argv);
|
||||
|
||||
for (i = 0; format_str[i]; i++)
|
||||
{
|
||||
gpointer arg_location;
|
||||
const NPVariant arg = argv[i];
|
||||
|
||||
arg_location = va_arg (args, gpointer);
|
||||
|
||||
switch (format_str[i])
|
||||
{
|
||||
case 'u':
|
||||
{
|
||||
NPString string;
|
||||
|
||||
if (!NPVARIANT_IS_STRING (arg))
|
||||
goto out;
|
||||
|
||||
string = NPVARIANT_TO_STRING (arg);
|
||||
|
||||
if (!uuid_is_valid (string))
|
||||
goto out;
|
||||
|
||||
*(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
if (!NPVARIANT_IS_BOOLEAN (arg))
|
||||
goto out;
|
||||
|
||||
*(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
if (!NPVARIANT_IS_OBJECT (arg))
|
||||
goto out;
|
||||
|
||||
*(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
va_end (args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_list_extensions (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *args,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
@ -452,87 +533,158 @@ plugin_list_extensions (PluginObject *obj,
|
||||
|
||||
static gboolean
|
||||
plugin_enable_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
gboolean enabled)
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
gboolean ret;
|
||||
gchar *uuid;
|
||||
gboolean enabled;
|
||||
gsize length;
|
||||
gchar **uuids;
|
||||
const gchar **new_uuids;
|
||||
|
||||
if (!parse_args ("ub", argc, argv, &uuid, &enabled))
|
||||
return FALSE;
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
(enabled ? "EnableExtension" : "DisableExtension"),
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
|
||||
length = g_strv_length (uuids);
|
||||
|
||||
g_free (uuid_str);
|
||||
if (enabled)
|
||||
{
|
||||
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
|
||||
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
|
||||
new_uuids[length] = uuid;
|
||||
new_uuids[length + 1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize i = 0, j = 0;
|
||||
new_uuids = g_new (const gchar *, length);
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
if (g_str_equal (uuids[i], uuid))
|
||||
continue;
|
||||
|
||||
return TRUE;
|
||||
new_uuids[j] = uuids[i];
|
||||
j++;
|
||||
}
|
||||
|
||||
new_uuids[j] = NULL;
|
||||
}
|
||||
|
||||
ret = g_settings_set_strv (obj->settings,
|
||||
ENABLED_EXTENSIONS_KEY,
|
||||
new_uuids);
|
||||
|
||||
g_strfreev (uuids);
|
||||
g_free (new_uuids);
|
||||
g_free (uuid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct _AsyncClosure AsyncClosure;
|
||||
|
||||
struct _AsyncClosure {
|
||||
PluginObject *obj;
|
||||
NPObject *callback;
|
||||
NPObject *errback;
|
||||
};
|
||||
|
||||
static void
|
||||
install_extension_cb (GObject *proxy,
|
||||
GAsyncResult *async_res,
|
||||
gpointer user_data)
|
||||
{
|
||||
AsyncClosure *async_closure = (AsyncClosure *) user_data;
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
NPVariant args[1];
|
||||
NPVariant result = { NPVariantType_Void };
|
||||
NPObject *callback;
|
||||
|
||||
res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
|
||||
|
||||
if (res == NULL)
|
||||
{
|
||||
if (g_dbus_error_is_remote_error (error))
|
||||
g_dbus_error_strip_remote_error (error);
|
||||
STRINGZ_TO_NPVARIANT (error->message, args[0]);
|
||||
callback = async_closure->errback;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *string_result;
|
||||
g_variant_get (res, "(&s)", &string_result);
|
||||
STRINGZ_TO_NPVARIANT (string_result, args[0]);
|
||||
callback = async_closure->callback;
|
||||
}
|
||||
|
||||
funcs.invokeDefault (async_closure->obj->instance,
|
||||
callback, args, 1, &result);
|
||||
|
||||
funcs.releasevariantvalue (&result);
|
||||
|
||||
funcs.releaseobject (async_closure->callback);
|
||||
funcs.releaseobject (async_closure->errback);
|
||||
g_slice_free (AsyncClosure, async_closure);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_install_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPString version_tag)
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
gchar *version_tag_str;
|
||||
gchar *uuid;
|
||||
NPObject *callback, *errback;
|
||||
AsyncClosure *async_closure;
|
||||
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
version_tag_str = g_strndup (version_tag.UTF8Characters,
|
||||
version_tag.UTF8Length);
|
||||
async_closure = g_slice_new (AsyncClosure);
|
||||
async_closure->obj = obj;
|
||||
async_closure->callback = funcs.retainobject (callback);
|
||||
async_closure->errback = funcs.retainobject (errback);
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
"InstallRemoteExtension",
|
||||
g_variant_new ("(ss)",
|
||||
uuid_str,
|
||||
version_tag_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
install_extension_cb,
|
||||
async_closure);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_free (version_tag_str);
|
||||
g_free (uuid);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_uninstall_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"UninstallExtension",
|
||||
g_variant_new ("(s)",
|
||||
uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_free (uuid);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
@ -546,29 +698,26 @@ plugin_uninstall_extension (PluginObject *obj,
|
||||
|
||||
static gboolean
|
||||
plugin_get_info (PluginObject *obj,
|
||||
NPString uuid,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"GetExtensionInfo",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_free (uuid);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
@ -582,30 +731,25 @@ plugin_get_info (PluginObject *obj,
|
||||
|
||||
static gboolean
|
||||
plugin_get_errors (PluginObject *obj,
|
||||
NPString uuid,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"GetExtensionErrors",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
g_warning ("Failed to retrieve errors: %s", error->message);
|
||||
@ -616,6 +760,29 @@ plugin_get_errors (PluginObject *obj,
|
||||
return jsonify_variant (res, result);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_launch_extension_prefs (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid;
|
||||
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
"LaunchExtensionPrefs",
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
plugin_get_api_version (PluginObject *obj,
|
||||
NPVariant *result)
|
||||
@ -666,10 +833,40 @@ plugin_get_shell_version (PluginObject *obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define METHODS \
|
||||
METHOD (list_extensions) \
|
||||
METHOD (get_info) \
|
||||
METHOD (enable_extension) \
|
||||
METHOD (install_extension) \
|
||||
METHOD (uninstall_extension) \
|
||||
METHOD (get_errors) \
|
||||
METHOD (launch_extension_prefs) \
|
||||
/* */
|
||||
|
||||
#define METHOD(x) \
|
||||
static NPIdentifier x##_id;
|
||||
METHODS
|
||||
#undef METHOD
|
||||
|
||||
static NPIdentifier api_version_id;
|
||||
static NPIdentifier shell_version_id;
|
||||
static NPIdentifier onextension_changed_id;
|
||||
static NPIdentifier onrestart_id;
|
||||
|
||||
static bool
|
||||
plugin_object_has_method (NPObject *npobj,
|
||||
NPIdentifier name)
|
||||
{
|
||||
#define METHOD(x) (name == (x##_id)) ||
|
||||
/* expands to (name == list_extensions_id) || FALSE; */
|
||||
return METHODS FALSE;
|
||||
#undef METHOD
|
||||
}
|
||||
|
||||
static bool
|
||||
plugin_object_invoke (NPObject *npobj,
|
||||
NPIdentifier name,
|
||||
const NPVariant *args,
|
||||
const NPVariant *argv,
|
||||
uint32_t argc,
|
||||
NPVariant *result)
|
||||
{
|
||||
@ -681,53 +878,13 @@ plugin_object_invoke (NPObject *npobj,
|
||||
|
||||
VOID_TO_NPVARIANT (*result);
|
||||
|
||||
if (!plugin_object_has_method (npobj, name))
|
||||
#define METHOD(x) \
|
||||
if (name == x##_id) \
|
||||
return plugin_##x (obj, argc, argv, result);
|
||||
METHODS
|
||||
#undef METHOD
|
||||
|
||||
return FALSE;
|
||||
|
||||
if (name == list_extensions_id)
|
||||
return plugin_list_extensions (obj, result);
|
||||
else if (name == get_info_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result);
|
||||
}
|
||||
else if (name == enable_extension_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE;
|
||||
|
||||
return plugin_enable_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
NPVARIANT_TO_BOOLEAN(args[1]));
|
||||
}
|
||||
else if (name == install_extension_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
|
||||
|
||||
return plugin_install_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
NPVARIANT_TO_STRING(args[1]));
|
||||
}
|
||||
else if (name == uninstall_extension_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_uninstall_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
result);
|
||||
}
|
||||
else if (name == get_errors_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_get_errors (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
result);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -735,6 +892,7 @@ plugin_object_has_property (NPObject *npobj,
|
||||
NPIdentifier name)
|
||||
{
|
||||
return (name == onextension_changed_id ||
|
||||
name == onrestart_id ||
|
||||
name == api_version_id ||
|
||||
name == shell_version_id);
|
||||
}
|
||||
@ -761,6 +919,33 @@ plugin_object_get_property (NPObject *npobj,
|
||||
else
|
||||
NULL_TO_NPVARIANT (*result);
|
||||
}
|
||||
else if (name == onrestart_id)
|
||||
{
|
||||
if (obj->restart_listener)
|
||||
OBJECT_TO_NPVARIANT (obj->restart_listener, *result);
|
||||
else
|
||||
NULL_TO_NPVARIANT (*result);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool
|
||||
plugin_object_set_callback (NPObject **listener,
|
||||
const NPVariant *value)
|
||||
{
|
||||
if (!NPVARIANT_IS_OBJECT (*value) && !NPVARIANT_IS_NULL (*value))
|
||||
return FALSE;
|
||||
|
||||
if (*listener)
|
||||
funcs.releaseobject (*listener);
|
||||
*listener = NULL;
|
||||
|
||||
if (NPVARIANT_IS_OBJECT (*value))
|
||||
{
|
||||
*listener = NPVARIANT_TO_OBJECT (*value);
|
||||
funcs.retainobject (*listener);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -772,25 +957,13 @@ plugin_object_set_property (NPObject *npobj,
|
||||
{
|
||||
PluginObject *obj;
|
||||
|
||||
if (!plugin_object_has_property (npobj, name))
|
||||
return FALSE;
|
||||
obj = (PluginObject *)npobj;
|
||||
|
||||
if (name == onextension_changed_id)
|
||||
{
|
||||
obj = (PluginObject*) npobj;
|
||||
if (obj->listener)
|
||||
funcs.releaseobject (obj->listener);
|
||||
return plugin_object_set_callback (&obj->listener, value);
|
||||
|
||||
obj->listener = NULL;
|
||||
if (NPVARIANT_IS_OBJECT (*value))
|
||||
{
|
||||
obj->listener = NPVARIANT_TO_OBJECT (*value);
|
||||
funcs.retainobject (obj->listener);
|
||||
return TRUE;
|
||||
}
|
||||
else if (NPVARIANT_IS_NULL (*value))
|
||||
return TRUE;
|
||||
}
|
||||
if (name == onrestart_id)
|
||||
return plugin_object_set_callback (&obj->restart_listener, value);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@ -824,7 +997,9 @@ init_methods_and_properties (void)
|
||||
install_extension_id = funcs.getstringidentifier ("installExtension");
|
||||
uninstall_extension_id = funcs.getstringidentifier ("uninstallExtension");
|
||||
get_errors_id = funcs.getstringidentifier ("getExtensionErrors");
|
||||
launch_extension_prefs_id = funcs.getstringidentifier ("launchExtensionPrefs");
|
||||
|
||||
onrestart_id = funcs.getstringidentifier ("onshellrestart");
|
||||
onextension_changed_id = funcs.getstringidentifier ("onchange");
|
||||
}
|
||||
|
||||
@ -853,3 +1028,12 @@ NPP_GetValue(NPP instance,
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
/* Opera tries to call NPP_SetWindow without checking the
|
||||
* NULL pointer beforehand. */
|
||||
NPError
|
||||
NPP_SetWindow(NPP instance,
|
||||
NPWindow *window)
|
||||
{
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
132
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.3.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -36,6 +36,8 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.22])
|
||||
|
||||
AC_PATH_PROG([XSLTPROC], [xsltproc])
|
||||
|
||||
GLIB_GSETTINGS
|
||||
|
||||
# Get a value to substitute into gnome-shell.in
|
||||
@ -44,45 +46,50 @@ AC_SUBST(PYTHON)
|
||||
|
||||
# We need at least this, since gst_plugin_register_static() was added
|
||||
# in 0.10.16, but nothing older than 0.10.21 has been tested.
|
||||
GSTREAMER_MIN_VERSION=0.10.16
|
||||
GSTREAMER_MIN_VERSION=0.11.92
|
||||
|
||||
recorder_modules=
|
||||
build_recorder=false
|
||||
AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
|
||||
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.7.5
|
||||
CLUTTER_MIN_VERSION=1.11.11
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.29.18
|
||||
MUTTER_MIN_VERSION=3.3.3
|
||||
FOLKS_MIN_VERSION=0.5.2
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.7.3
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.31.6
|
||||
LIBECAL_MIN_VERSION=2.32.0
|
||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.15.6
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.7
|
||||
PULSE_MIN_VERS=2.0
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
folks >= $FOLKS_MIN_VERSION
|
||||
atk-bridge-2.0
|
||||
libmutter >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 $recorder_modules
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
gl
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
@ -93,7 +100,9 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util gnome-keyring-1)
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
@ -101,13 +110,10 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
|
||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
||||
AC_SUBST(JHBUILD_TYPELIBDIR)
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
@ -117,10 +123,11 @@ AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 gnome-desktop-3.0 >= 2.90.0 x11)
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
@ -136,10 +143,37 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
|
||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||
|
||||
AC_ARG_WITH(systemd,
|
||||
AS_HELP_STRING([--with-systemd],
|
||||
[Add systemd support]),
|
||||
[with_systemd=$withval], [with_systemd=auto])
|
||||
|
||||
PKG_CHECK_MODULES(SYSTEMD,
|
||||
[libsystemd-login libsystemd-daemon],
|
||||
[have_systemd=yes], [have_systemd=no])
|
||||
|
||||
if test "x$with_systemd" = "xauto" ; then
|
||||
if test x$have_systemd = xno ; then
|
||||
use_systemd=no
|
||||
else
|
||||
use_systemd=yes
|
||||
fi
|
||||
else
|
||||
use_systemd=$with_systemd
|
||||
fi
|
||||
|
||||
if test "x$use_systemd" = "xyes"; then
|
||||
if test "x$have_systemd" = "xno"; then
|
||||
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
|
||||
fi
|
||||
|
||||
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
|
||||
fi
|
||||
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
AC_SUBST(MUTTER_GIR_DIR)
|
||||
@ -178,11 +212,23 @@ AC_SUBST(TYPELIBDIR)
|
||||
|
||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||
|
||||
AC_ARG_ENABLE(man,
|
||||
[AS_HELP_STRING([--enable-man],
|
||||
[generate man pages [default=yes]])],,
|
||||
enable_man=yes)
|
||||
if test "$enable_man" != no; then
|
||||
AC_PATH_PROG([XSLTPROC], [xsltproc])
|
||||
if test -z "$XSLTPROC"; then
|
||||
AC_MSG_ERROR([xsltproc is required for --enable-man])
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||
|
||||
# Stay command-line compatible with the gnome-common configure option. Here
|
||||
# minimum/yes/maximum are the same, however.
|
||||
AC_ARG_ENABLE(compile_warnings,
|
||||
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
|
||||
enable_compile_warnings=maximum)
|
||||
enable_compile_warnings=error)
|
||||
|
||||
changequote(,)dnl
|
||||
if test "$enable_compile_warnings" != no ; then
|
||||
@ -198,7 +244,7 @@ if test "$enable_compile_warnings" != no ; then
|
||||
if test "$enable_compile_warnings" = error ; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Werror" ;;
|
||||
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
@ -206,34 +252,9 @@ fi
|
||||
changequote([,])dnl
|
||||
|
||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||
AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
||||
|
||||
AC_MSG_CHECKING([location of system Certificate Authority list])
|
||||
AC_ARG_WITH(ca-certificates,
|
||||
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
|
||||
[path to system Certificate Authority list])])
|
||||
|
||||
if test "$with_ca_certificates" = "no"; then
|
||||
AC_MSG_RESULT([disabled])
|
||||
else
|
||||
if test -z "$with_ca_certificates"; then
|
||||
for f in /etc/pki/tls/certs/ca-bundle.crt \
|
||||
/etc/ssl/certs/ca-certificates.crt; do
|
||||
if test -f "$f"; then
|
||||
with_ca_certificates="$f"
|
||||
fi
|
||||
done
|
||||
if test -z "$with_ca_certificates"; then
|
||||
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT($with_ca_certificates)
|
||||
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
|
||||
fi
|
||||
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
|
||||
|
||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
||||
|
||||
@ -247,8 +268,9 @@ AC_CONFIG_FILES([
|
||||
docs/reference/st/Makefile
|
||||
docs/reference/st/st-docs.sgml
|
||||
js/Makefile
|
||||
js/misc/config.js
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/Makefile
|
||||
src/gvc/Makefile
|
||||
browser-plugin/Makefile
|
||||
tests/Makefile
|
||||
po/Makefile.in
|
||||
|
12
data/50-gnome-shell-screenshot.xml.in
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="Screenshots"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-recording"
|
||||
_description="Record a screencast"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
21
data/50-gnome-shell-system.xml.in
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="System"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-message-tray"
|
||||
_description="Show the message tray"/>
|
||||
|
||||
<KeyListEntry name="focus-active-notification"
|
||||
_description="Focus the active notification"/>
|
||||
|
||||
<KeyListEntry name="toggle-application-view"
|
||||
_description="Show all applications"/>
|
||||
|
||||
<KeyListEntry name="open-application-menu"
|
||||
_description="Open the application menu"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
@ -1,5 +1,5 @@
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
@ -8,20 +8,22 @@ desktop_DATA = gnome-shell.desktop
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
# Placeholder until we add intltool
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
|
||||
searchprovidersdir = $(pkgdatadir)/search_providers
|
||||
dist_searchproviders_DATA = \
|
||||
search_providers/google.xml \
|
||||
search_providers/wikipedia.xml
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
themedir = $(pkgdatadir)/theme
|
||||
dist_theme_DATA = \
|
||||
theme/calendar-arrow-left.svg \
|
||||
theme/calendar-arrow-right.svg \
|
||||
theme/calendar-today.svg \
|
||||
theme/checkbox-focused.svg \
|
||||
theme/checkbox-off-focused.svg \
|
||||
theme/checkbox-off.svg \
|
||||
theme/checkbox.svg \
|
||||
theme/close-window.svg \
|
||||
theme/close.svg \
|
||||
theme/corner-ripple-ltr.png \
|
||||
@ -29,27 +31,40 @@ dist_theme_DATA = \
|
||||
theme/dash-placeholder.svg \
|
||||
theme/filter-selected-ltr.svg \
|
||||
theme/filter-selected-rtl.svg \
|
||||
theme/gdm.css \
|
||||
theme/gnome-shell.css \
|
||||
theme/panel-border.svg \
|
||||
theme/logged-in-indicator.svg \
|
||||
theme/message-tray-background.png \
|
||||
theme/more-results.svg \
|
||||
theme/noise-texture.png \
|
||||
theme/panel-button-border.svg \
|
||||
theme/panel-button-highlight-narrow.svg \
|
||||
theme/panel-button-highlight-wide.svg \
|
||||
theme/process-working.svg \
|
||||
theme/running-indicator.svg \
|
||||
theme/scroll-hhandle.svg \
|
||||
theme/scroll-vhandle.svg \
|
||||
theme/source-button-border.svg \
|
||||
theme/summary-counter.svg \
|
||||
theme/toggle-off-us.svg \
|
||||
theme/toggle-off-intl.svg \
|
||||
theme/toggle-on-us.svg \
|
||||
theme/toggle-on-intl.svg \
|
||||
theme/ws-switch-arrow-up.svg \
|
||||
theme/ws-switch-arrow-down.svg
|
||||
theme/ws-switch-arrow-up.png \
|
||||
theme/ws-switch-arrow-down.png
|
||||
|
||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
keys_in_files = \
|
||||
50-gnome-shell-screenshot.xml.in \
|
||||
50-gnome-shell-system.xml.in \
|
||||
$(NULL)
|
||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
|
||||
%.gschema.xml.in: %.gschema.xml.in.in Makefile
|
||||
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
|
||||
$< > $@ || rm $@
|
||||
|
||||
@GSETTINGS_RULES@
|
||||
|
||||
# We need to compile schemas at make time
|
||||
@ -62,21 +77,21 @@ all-local: gschemas.compiled
|
||||
convertdir = $(datadir)/GConf/gsettings
|
||||
convert_DATA = gnome-shell-overrides.convert
|
||||
|
||||
shadersdir = $(pkgdatadir)/shaders
|
||||
shaders_DATA = \
|
||||
shaders/dim-window.glsl
|
||||
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-extension-prefs.desktop.in.in \
|
||||
$(introspection_DATA) \
|
||||
$(menu_DATA) \
|
||||
$(shaders_DATA) \
|
||||
$(convert_DATA) \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
$(keys_in_files) \
|
||||
org.gnome.shell.gschema.xml.in.in
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(keys_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
gschemas.compiled
|
||||
|
||||
gschemas.compiled \
|
||||
org.gnome.shell.gschema.valid \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
|
12
data/gnome-shell-extension-prefs.desktop.in.in
Normal file
@ -0,0 +1,12 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_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
|
||||
X-GNOME-Bugzilla-Component=extensions
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
@ -13,4 +13,4 @@ NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
X-GNOME-Provides=panel;windowmanager;
|
||||
X-GNOME-Autostart-Notify=true
|
||||
X-GNOME-AutoRestart=true
|
||||
X-GNOME-AutoRestart=false
|
||||
|
73
data/org.gnome.ShellSearchProvider.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
|
||||
<!--
|
||||
org.gnome.Shell.SearchProvider:
|
||||
@short_description: Search provider interface
|
||||
|
||||
The interface used for integrating into GNOME Shell's search
|
||||
interface. This interface is deprecated, and org.gnome.Shell.SearchProvider2 should be used instead.
|
||||
-->
|
||||
<interface name="org.gnome.Shell.SearchProvider">
|
||||
|
||||
<!--
|
||||
GetInitialResultSet:
|
||||
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||
|
||||
Called when the user first begins a search.
|
||||
-->
|
||||
<method name="GetInitialResultSet">
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="as" name="results" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
GetSubsearchResultSet:
|
||||
@previous_results: Array of results previously returned by GetInitialResultSet().
|
||||
@terms: Array of updated search terms, which the provider should treat as logical AND.
|
||||
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||
|
||||
Called when a search is performed which is a "subsearch" of
|
||||
the previous search, e.g. the method may return less results, but
|
||||
not more or different results.
|
||||
|
||||
This allows search providers to only search through the previous
|
||||
result set, rather than possibly performing a full re-query.
|
||||
-->
|
||||
<method name="GetSubsearchResultSet">
|
||||
<arg type="as" name="previous_results" direction="in" />
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="as" name="results" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
GetResultMetas:
|
||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images).
|
||||
|
||||
Return an array of meta data used to display each given result
|
||||
-->
|
||||
<method name="GetResultMetas">
|
||||
<arg type="as" name="identifiers" direction="in" />
|
||||
<arg type="aa{sv}" name="metas" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ActivateResult:
|
||||
@identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||
|
||||
Called when the users chooses a given result. The result should
|
||||
be displayed in the application associated with the corresponding
|
||||
provider.
|
||||
|
||||
This method is deprecated, and providers should implement ActivateResult2()
|
||||
instead.
|
||||
-->
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" name="identifier" direction="in" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
87
data/org.gnome.ShellSearchProvider2.xml
Normal file
@ -0,0 +1,87 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
|
||||
<!--
|
||||
org.gnome.Shell.SearchProvider2:
|
||||
@short_description: Search provider interface
|
||||
|
||||
The interface used for integrating into GNOME Shell's search
|
||||
interface (version 2).
|
||||
-->
|
||||
<interface name="org.gnome.Shell.SearchProvider2">
|
||||
|
||||
<!--
|
||||
GetInitialResultSet:
|
||||
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||
|
||||
Called when the user first begins a search.
|
||||
-->
|
||||
<method name="GetInitialResultSet">
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="as" name="results" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
GetSubsearchResultSet:
|
||||
@previous_results: Array of results previously returned by GetInitialResultSet().
|
||||
@terms: Array of updated search terms, which the provider should treat as logical AND.
|
||||
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||
|
||||
Called when a search is performed which is a "subsearch" of
|
||||
the previous search, e.g. the method may return less results, but
|
||||
not more or different results.
|
||||
|
||||
This allows search providers to only search through the previous
|
||||
result set, rather than possibly performing a full re-query.
|
||||
-->
|
||||
<method name="GetSubsearchResultSet">
|
||||
<arg type="as" name="previous_results" direction="in" />
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="as" name="results" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
GetResultMetas:
|
||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||
|
||||
Return an array of meta data used to display each given result
|
||||
-->
|
||||
<method name="GetResultMetas">
|
||||
<arg type="as" name="identifiers" direction="in" />
|
||||
<arg type="aa{sv}" name="metas" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ActivateResult:
|
||||
@identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||
@timestamp: A timestamp of the user interaction that triggered this call
|
||||
|
||||
Called when the users chooses a given result. The result should
|
||||
be displayed in the application associated with the corresponding
|
||||
provider. The provided search terms can be used to allow launching a full search in
|
||||
the application.
|
||||
-->
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" name="identifier" direction="in" />
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="u" name="timestamp" direction="in" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
LaunchSearch:
|
||||
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||
@timestamp: A timestamp of the user interaction that triggered this call
|
||||
|
||||
Asks the search provider to launch a full search in the application for the provided terms.
|
||||
-->
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="u" name="timestamp" direction="in" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
@ -39,10 +39,6 @@
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="disabled-open-search-providers" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>disabled OpenSearch providers</_summary>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
@ -53,15 +49,40 @@
|
||||
</key>
|
||||
<key name="saved-im-presence" type="i">
|
||||
<default>1</default>
|
||||
<_summary></_summary>
|
||||
<_summary>Internally used to store the last IM presence explicitly set by the user. The
|
||||
value here is from the TpConnectionPresenceType enumeration.</_summary>
|
||||
</key>
|
||||
<key name="saved-session-presence" type="i">
|
||||
<default>0</default>
|
||||
<_summary></_summary>
|
||||
<_summary>Internally used to store the last session presence status for the user. The
|
||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
<key name="always-show-log-out" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
|
||||
<_description>
|
||||
This key overrides the automatic hiding of the 'Log out'
|
||||
menuitem in single-user, single-session situations.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-full-name" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Show full name in the user menu</_summary>
|
||||
<_description>Whether the users full name is shown in the user menu or not.</_description>
|
||||
</key>
|
||||
<key name="remember-mount-password" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
||||
<_description>
|
||||
The shell will request a password when an encrypted device or a
|
||||
remote filesystem is mounted. If the password can be saved for
|
||||
future use a 'Remember Password' checkbox will be present.
|
||||
This key sets the default state of the checkbox.
|
||||
</_description>
|
||||
</key>
|
||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||
</schema>
|
||||
|
||||
@ -76,6 +97,46 @@
|
||||
</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>
|
||||
Keybinding to open the application menu.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-application-view" type="as">
|
||||
<default>["<Super>a"]</default>
|
||||
<_summary>Keybinding to open the "Show Applications" view</_summary>
|
||||
<_description>
|
||||
Keybinding to open the "Show Applications" view of the Activities
|
||||
Overview.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-message-tray" type="as">
|
||||
<default>["<Super>m"]</default>
|
||||
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
||||
<_description>
|
||||
Keybinding to toggle the visibility of the message tray.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="focus-active-notification" type="as">
|
||||
<default>["<Super>n"]</default>
|
||||
<_summary>Keybinding to focus the active notification</_summary>
|
||||
<_description>
|
||||
Keybinding to focus the active notification.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-recording" type="as">
|
||||
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
|
||||
<_summary>Keybinding to toggle the screen recorder</_summary>
|
||||
<_description>
|
||||
Keybinding to start/stop the builtin screen recorder.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="keyboard-type" type="s">
|
||||
@ -87,28 +148,10 @@
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-seconds" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show time with seconds</_summary>
|
||||
<_description>
|
||||
If true, display seconds in time.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-date" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show date in clock</_summary>
|
||||
<_description>
|
||||
If true, display date in the clock, in addition to time.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="framerate" type="i">
|
||||
<default>15</default>
|
||||
<default>30</default>
|
||||
<_summary>Framerate used for recording screencasts.</_summary>
|
||||
<_description>
|
||||
The framerate of the resulting screencast recordered
|
||||
@ -127,7 +170,7 @@
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
|
||||
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
</_description>
|
||||
@ -143,36 +186,80 @@
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/">
|
||||
<enum id="org.gnome.shell.window-switcher.AppIconMode">
|
||||
<value value="1" nick="thumbnail-only"/>
|
||||
<value value="2" nick="app-icon-only"/>
|
||||
<value value="3" nick="both"/>
|
||||
</enum>
|
||||
<schema id="org.gnome.shell.window-switcher"
|
||||
path="/org/gnome/shell/window-switcher/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode">
|
||||
<default>'both'</default>
|
||||
<_summary>The application icon mode.</_summary>
|
||||
<_description>
|
||||
Configures how the windows are shown in the switcher. Valid possibilities
|
||||
are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only'
|
||||
(shows only the application icon) or 'both'.
|
||||
</_description>
|
||||
</key>
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>false</default>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
If true, only windows from the current workspace are shown in the switcher.
|
||||
Otherwise, all windows are included.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="attach-modal-dialogs" type="b">
|
||||
<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="button-layout" type="s">
|
||||
<default>":close"</default>
|
||||
<summary>Arrangement of buttons on the titlebar</summary>
|
||||
<description>
|
||||
<_summary>Arrangement of buttons on the titlebar</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.desktop.wm.preferences when
|
||||
running GNOME Shell.
|
||||
</description>
|
||||
</_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>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="workspaces-only-on-primary" type="b">
|
||||
<default>true</default>
|
||||
<summary>Workspaces only on primary monitor</summary>
|
||||
<_summary>Workspaces only on primary monitor</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="focus-change-on-pointer-rest" type="b">
|
||||
<default>true</default>
|
||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
@ -1,7 +0,0 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Google</ShortName>
|
||||
<Description>Google Search</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
|
||||
</OpenSearchDescription>
|
@ -1,44 +0,0 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Wikipedia</ShortName>
|
||||
<Description>Wikipedia, the free encyclopedia</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
|
||||
<!-- The criterion for being below is being listed with more than 100,000
|
||||
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
|
||||
<Language>ar</Language>
|
||||
<Language>bg</Language>
|
||||
<Language>ca</Language>
|
||||
<Language>cs</Language>
|
||||
<Language>da</Language>
|
||||
<Language>de</Language>
|
||||
<Language>en</Language>
|
||||
<Language>eo</Language>
|
||||
<Language>es</Language>
|
||||
<Language>fa</Language>
|
||||
<Language>fi</Language>
|
||||
<Language>fr</Language>
|
||||
<Language>he</Language>
|
||||
<Language>hu</Language>
|
||||
<Language>id</Language>
|
||||
<Language>it</Language>
|
||||
<Language>ja</Language>
|
||||
<Language>ko</Language>
|
||||
<Language>lt</Language>
|
||||
<Language>nl</Language>
|
||||
<Language>no</Language>
|
||||
<Language>pl</Language>
|
||||
<Language>pt</Language>
|
||||
<Language>ro</Language>
|
||||
<Language>ru</Language>
|
||||
<Language>sk</Language>
|
||||
<Language>sl</Language>
|
||||
<Language>sr</Language>
|
||||
<Language>sv</Language>
|
||||
<Language>tr</Language>
|
||||
<Language>uk</Language>
|
||||
<Language>vi</Language>
|
||||
<Language>vo</Language>
|
||||
<Language>war</Language>
|
||||
<Language>zh</Language>
|
||||
</OpenSearchDescription>
|
@ -1,27 +0,0 @@
|
||||
#version 110
|
||||
uniform sampler2D tex;
|
||||
uniform float fraction;
|
||||
uniform float height;
|
||||
const float c = -0.2;
|
||||
const float border_max_height = 60.0;
|
||||
|
||||
mat4 contrast = mat4 (1.0 + c, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0 + c, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0 + c, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0);
|
||||
vec4 off = vec4(0.633, 0.633, 0.633, 0);
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture2D(tex, cogl_tex_coord_in[0].xy);
|
||||
float y = height * cogl_tex_coord_in[0].y;
|
||||
|
||||
// To reduce contrast, blend with a mid gray
|
||||
cogl_color_out = color * contrast - off * c * color.a;
|
||||
|
||||
// We only fully dim at a distance of BORDER_MAX_HEIGHT from the top and
|
||||
// when the fraction is 1.0. For other locations and fractions we linearly
|
||||
// interpolate back to the original undimmed color, so the top of the window
|
||||
// is at full color.
|
||||
cogl_color_out = color + (cogl_color_out - color) * max(min(y / border_max_height, 1.0), 0.0);
|
||||
cogl_color_out = color + (cogl_color_out - color) * fraction;
|
||||
}
|
@ -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="28"
|
||||
height="25"
|
||||
width="29"
|
||||
height="29"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="calendar-today.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
@ -118,6 +118,17 @@
|
||||
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"
|
||||
@ -127,20 +138,29 @@
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="8.3750933"
|
||||
inkscape:cy="8.0837211"
|
||||
inkscape:cx="20.652108"
|
||||
inkscape:cy="11.839084"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="741"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
borderlayer="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3109"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
@ -157,31 +177,28 @@
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-469.08263,-536.99307)">
|
||||
<g
|
||||
id="g3003">
|
||||
transform="translate(-469.08263,-532.99307)">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
|
||||
sodipodi:end="6.2831853"
|
||||
sodipodi:start="3.1415927"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:cx="51"
|
||||
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"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:type="arc" />
|
||||
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
|
||||
y="558.85046"
|
||||
x="468.96878"
|
||||
height="3.1425927"
|
||||
width="28.149134"
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect2996"
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
</g>
|
||||
width="31"
|
||||
height="3"
|
||||
x="468.08264"
|
||||
y="558.99304" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.1 KiB |
289
data/theme/checkbox-focused.svg
Normal file
@ -0,0 +1,289 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
id="svg3199"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="checkbox.svg">
|
||||
<defs
|
||||
id="defs3201">
|
||||
<linearGradient
|
||||
id="linearGradient15404"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop15406"
|
||||
offset="0"
|
||||
style="stop-color:#515151;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop15408"
|
||||
offset="1"
|
||||
style="stop-color:#292929;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<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="perspective3207" />
|
||||
<inkscape:perspective
|
||||
id="perspective3187"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5872-5-1"
|
||||
id="linearGradient5891-0-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="205.84143"
|
||||
y1="246.7094"
|
||||
x2="206.74803"
|
||||
y2="231.24142" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5872-5-1">
|
||||
<stop
|
||||
style="stop-color:#0b2e52;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5874-4-4" />
|
||||
<stop
|
||||
style="stop-color:#1862af;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5876-0-5" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5837-4-6"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect14768"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5884-4-7"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
y2="-388.72955"
|
||||
x2="-93.031357"
|
||||
y1="-396.34738"
|
||||
x1="-93.031357"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient14219"
|
||||
xlink:href="#linearGradient15404"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10013-4-63-6">
|
||||
<stop
|
||||
style="stop-color:#333333;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10015-2-76-1" />
|
||||
<stop
|
||||
style="stop-color:#292929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10017-46-15-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10597-5">
|
||||
<stop
|
||||
style="stop-color:#16191a;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10599-2" />
|
||||
<stop
|
||||
style="stop-color:#2b3133;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10601-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="-322.16354"
|
||||
x2="921.22498"
|
||||
y1="-330.05121"
|
||||
x1="921.32812"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15374"
|
||||
xlink:href="#linearGradient10013-4-63-6"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(-1199.9852,216.38048)"
|
||||
y2="-227.07961"
|
||||
x2="1203.9177"
|
||||
y1="-217.56708"
|
||||
x1="1203.9177"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15376"
|
||||
xlink:href="#linearGradient10597-5"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="-388.72955"
|
||||
x2="-93.031357"
|
||||
y1="-396.34738"
|
||||
x1="-93.031357"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient14219-6"
|
||||
xlink:href="#linearGradient15404-9"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient15404-9"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop15406-6"
|
||||
offset="0"
|
||||
style="stop-color:#515151;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop15408-7"
|
||||
offset="1"
|
||||
style="stop-color:#292929;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#2d2d2d"
|
||||
borderopacity="1"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="71.516955"
|
||||
inkscape:cy="5.8710559"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1412"
|
||||
inkscape:window-height="1067"
|
||||
inkscape:window-x="2635"
|
||||
inkscape:window-y="226"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid14843"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3204">
|
||||
<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(-342.5,-521.36218)">
|
||||
<g
|
||||
transform="matrix(0.80230061,0,0,0.80230061,-87.624044,-453.10297)"
|
||||
id="g14586-0"
|
||||
style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
|
||||
id="g15291-9-6"
|
||||
style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new">
|
||||
<g
|
||||
transform="translate(877.50354,-102.83507)"
|
||||
id="g16853-4-9"
|
||||
style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;enable-background:new">
|
||||
<rect
|
||||
transform="scale(1,-1)"
|
||||
style="color:#000000;fill:url(#linearGradient14219-6);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1.24833274;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new"
|
||||
id="rect6506-6"
|
||||
width="11.281681"
|
||||
height="11.26221"
|
||||
x="-409.59354"
|
||||
y="-284.40115"
|
||||
rx="1.0052766"
|
||||
ry="1.0052764" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="translate(343.99999,987.99997)"
|
||||
id="g5886-5"
|
||||
style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.84337,0,0,0.84337,-110.16632,-503.56182)"
|
||||
id="g14586">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
|
||||
id="g15291-9"
|
||||
style="display:inline;enable-background:new">
|
||||
<g
|
||||
transform="translate(877.50354,-102.83507)"
|
||||
id="g16853-4"
|
||||
style="enable-background:new" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="translate(343.99999,987.99997)"
|
||||
id="g5886"
|
||||
style="display:inline;enable-background:new">
|
||||
<path
|
||||
style="fill:none;stroke:url(#linearGradient5891-0-4);stroke-width:7.11431503;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
|
||||
id="path5835"
|
||||
inkscape:path-effect="#path-effect5837-4-6"
|
||||
inkscape:original-d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:original-d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
|
||||
inkscape:path-effect="#path-effect5837-4-6"
|
||||
id="path5880"
|
||||
d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
|
||||
style="fill:none;stroke:#4787c8;stroke-width:3.55715752;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#7ea7d3;stroke-width:1.18571913px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 197.45937,240.47455 c -0.17828,-0.29362 -0.20087,-0.67548 -0.0603,-0.98892 0.14055,-0.31344 0.43739,-0.54812 0.77144,-0.62817 0.33405,-0.08 0.69314,-0.01 0.99635,0.15175 0.30321,0.16144 0.55146,0.40727 0.79165,0.65284 l 3.66429,3.74643 12.87946,-12.98973 c 0.20796,-0.20974 0.42306,-0.41969 0.68548,-0.55522 0.26242,-0.13553 0.57293,-0.19052 0.85827,-0.11426 0.14267,0.0381 0.27708,0.10787 0.38874,0.20452 0.11167,0.0966 0.20021,0.22004 0.25479,0.35726 0.0546,0.13722 0.075,0.28793 0.0585,0.43468 -0.0165,0.14674 -0.07,0.28919 -0.15422,0.41052"
|
||||
id="path5882"
|
||||
inkscape:path-effect="#path-effect5884-4-7"
|
||||
inkscape:original-d="m 197.45937,240.47455 c 0.65604,-0.56057 2.02485,-1.34847 2.49911,-0.8125 l 3.66429,3.74643 12.87946,-12.98973 c 0.6875,-0.6875 2.09152,0.7375 2.09152,0.7375"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csccc" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
198
data/theme/checkbox-off-focused.svg
Normal file
@ -0,0 +1,198 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
id="svg3199"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="checkbox-off.svg">
|
||||
<defs
|
||||
id="defs3201">
|
||||
<linearGradient
|
||||
id="linearGradient15404"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop15406"
|
||||
offset="0"
|
||||
style="stop-color:#515151;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop15408"
|
||||
offset="1"
|
||||
style="stop-color:#292929;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<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="perspective3207" />
|
||||
<inkscape:perspective
|
||||
id="perspective3187"
|
||||
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-effect5837-4-6"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect14768"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5884-4-7"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
y2="-388.72955"
|
||||
x2="-93.031357"
|
||||
y1="-396.34738"
|
||||
x1="-93.031357"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient14219"
|
||||
xlink:href="#linearGradient15404"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10013-4-63-6">
|
||||
<stop
|
||||
style="stop-color:#333333;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10015-2-76-1" />
|
||||
<stop
|
||||
style="stop-color:#292929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10017-46-15-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10597-5">
|
||||
<stop
|
||||
style="stop-color:#16191a;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10599-2" />
|
||||
<stop
|
||||
style="stop-color:#2b3133;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10601-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="-322.16354"
|
||||
x2="921.22498"
|
||||
y1="-330.05121"
|
||||
x1="921.32812"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15374"
|
||||
xlink:href="#linearGradient10013-4-63-6"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(-1199.9852,216.38048)"
|
||||
y2="-227.07961"
|
||||
x2="1203.9177"
|
||||
y1="-217.56708"
|
||||
x1="1203.9177"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15376"
|
||||
xlink:href="#linearGradient10597-5"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#2d2d2d"
|
||||
borderopacity="1"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="6.1225392"
|
||||
inkscape:cy="3.6003241"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1412"
|
||||
inkscape:window-height="1067"
|
||||
inkscape:window-x="2116"
|
||||
inkscape:window-y="261"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid14843"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3204">
|
||||
<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(-342.5,-521.36218)">
|
||||
<g
|
||||
transform="matrix(0.80230061,0,0,0.80230061,-87.624044,-453.10297)"
|
||||
id="g14586"
|
||||
style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
|
||||
id="g15291-9"
|
||||
style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new">
|
||||
<g
|
||||
transform="translate(877.50354,-102.83507)"
|
||||
id="g16853-4"
|
||||
style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;enable-background:new">
|
||||
<rect
|
||||
transform="scale(1,-1)"
|
||||
style="color:#000000;fill:url(#linearGradient14219);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1.24833274;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new"
|
||||
id="rect6506-6"
|
||||
width="11.281681"
|
||||
height="11.26221"
|
||||
x="-409.59354"
|
||||
y="-284.40115"
|
||||
rx="1.0052766"
|
||||
ry="1.0052764" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="translate(343.99999,987.99997)"
|
||||
id="g5886"
|
||||
style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.5 KiB |
218
data/theme/checkbox-off.svg
Normal file
@ -0,0 +1,218 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
id="svg3199"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="checkbox.svg">
|
||||
<defs
|
||||
id="defs3201">
|
||||
<linearGradient
|
||||
id="linearGradient15404"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop15406"
|
||||
offset="0"
|
||||
style="stop-color:#515151;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop15408"
|
||||
offset="1"
|
||||
style="stop-color:#292929;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<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="perspective3207" />
|
||||
<inkscape:perspective
|
||||
id="perspective3187"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5872-5-1"
|
||||
id="linearGradient5891-0-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="205.84143"
|
||||
y1="246.7094"
|
||||
x2="206.74803"
|
||||
y2="231.24142" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5872-5-1">
|
||||
<stop
|
||||
style="stop-color:#0b2e52;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5874-4-4" />
|
||||
<stop
|
||||
style="stop-color:#1862af;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5876-0-5" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5837-4-6"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect14768"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5884-4-7"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
y2="-388.72955"
|
||||
x2="-93.031357"
|
||||
y1="-396.34738"
|
||||
x1="-93.031357"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient14219"
|
||||
xlink:href="#linearGradient15404"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10013-4-63-6">
|
||||
<stop
|
||||
style="stop-color:#333333;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10015-2-76-1" />
|
||||
<stop
|
||||
style="stop-color:#292929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10017-46-15-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10597-5">
|
||||
<stop
|
||||
style="stop-color:#16191a;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10599-2" />
|
||||
<stop
|
||||
style="stop-color:#2b3133;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10601-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="-322.16354"
|
||||
x2="921.22498"
|
||||
y1="-330.05121"
|
||||
x1="921.32812"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15374"
|
||||
xlink:href="#linearGradient10013-4-63-6"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(-1199.9852,216.38048)"
|
||||
y2="-227.07961"
|
||||
x2="1203.9177"
|
||||
y1="-217.56708"
|
||||
x1="1203.9177"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15376"
|
||||
xlink:href="#linearGradient10597-5"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#2d2d2d"
|
||||
borderopacity="1"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="71.247925"
|
||||
inkscape:cy="33.339093"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1412"
|
||||
inkscape:window-height="1067"
|
||||
inkscape:window-x="2116"
|
||||
inkscape:window-y="261"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid14843"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3204">
|
||||
<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(-342.5,-521.36218)">
|
||||
<g
|
||||
transform="matrix(0.84337,0,0,0.84337,-110.16632,-503.56182)"
|
||||
id="g14586">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
|
||||
id="g15291-9"
|
||||
style="display:inline;enable-background:new">
|
||||
<g
|
||||
transform="translate(877.50354,-102.83507)"
|
||||
id="g16853-4"
|
||||
style="enable-background:new">
|
||||
<rect
|
||||
transform="scale(1,-1)"
|
||||
style="color:#000000;fill:url(#linearGradient14219);fill-opacity:1;fill-rule:nonzero;stroke:#868686;stroke-width:0.59377144999999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new"
|
||||
id="rect6506-6"
|
||||
width="11.281681"
|
||||
height="11.26221"
|
||||
x="-409.59354"
|
||||
y="-284.40115"
|
||||
rx="0.95632279"
|
||||
ry="0.95632273" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="translate(343.99999,987.99997)"
|
||||
id="g5886"
|
||||
style="display:inline;enable-background:new" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.8 KiB |
243
data/theme/checkbox.svg
Normal file
@ -0,0 +1,243 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
id="svg3199"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="checkbox-focused.svg">
|
||||
<defs
|
||||
id="defs3201">
|
||||
<linearGradient
|
||||
id="linearGradient15404"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop15406"
|
||||
offset="0"
|
||||
style="stop-color:#515151;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop15408"
|
||||
offset="1"
|
||||
style="stop-color:#292929;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<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="perspective3207" />
|
||||
<inkscape:perspective
|
||||
id="perspective3187"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5872-5-1"
|
||||
id="linearGradient5891-0-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="205.84143"
|
||||
y1="246.7094"
|
||||
x2="206.74803"
|
||||
y2="231.24142" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5872-5-1">
|
||||
<stop
|
||||
style="stop-color:#0b2e52;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop5874-4-4" />
|
||||
<stop
|
||||
style="stop-color:#1862af;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop5876-0-5" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5837-4-6"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect14768"
|
||||
is_visible="true" />
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect5884-4-7"
|
||||
is_visible="true" />
|
||||
<linearGradient
|
||||
y2="-388.72955"
|
||||
x2="-93.031357"
|
||||
y1="-396.34738"
|
||||
x1="-93.031357"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient14219"
|
||||
xlink:href="#linearGradient15404"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10013-4-63-6">
|
||||
<stop
|
||||
style="stop-color:#333333;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10015-2-76-1" />
|
||||
<stop
|
||||
style="stop-color:#292929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10017-46-15-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient10597-5">
|
||||
<stop
|
||||
style="stop-color:#16191a;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10599-2" />
|
||||
<stop
|
||||
style="stop-color:#2b3133;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop10601-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="-322.16354"
|
||||
x2="921.22498"
|
||||
y1="-330.05121"
|
||||
x1="921.32812"
|
||||
gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15374"
|
||||
xlink:href="#linearGradient10013-4-63-6"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(-1199.9852,216.38048)"
|
||||
y2="-227.07961"
|
||||
x2="1203.9177"
|
||||
y1="-217.56708"
|
||||
x1="1203.9177"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient15376"
|
||||
xlink:href="#linearGradient10597-5"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#2d2d2d"
|
||||
borderopacity="1"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="64.516955"
|
||||
inkscape:cy="13.871056"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1412"
|
||||
inkscape:window-height="1067"
|
||||
inkscape:window-x="2635"
|
||||
inkscape:window-y="226"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-bbox="true"
|
||||
showborder="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid14843"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3204">
|
||||
<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(-342.5,-521.36218)">
|
||||
<g
|
||||
transform="matrix(0.84337,0,0,0.84337,-110.16632,-503.56182)"
|
||||
id="g14586">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
|
||||
id="g15291-9"
|
||||
style="display:inline;enable-background:new">
|
||||
<g
|
||||
transform="translate(877.50354,-102.83507)"
|
||||
id="g16853-4"
|
||||
style="enable-background:new">
|
||||
<rect
|
||||
transform="scale(1,-1)"
|
||||
style="color:#000000;fill:url(#linearGradient14219);fill-opacity:1;fill-rule:nonzero;stroke:#868686;stroke-width:0.59377144999999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new"
|
||||
id="rect6506-6"
|
||||
width="11.281681"
|
||||
height="11.26221"
|
||||
x="-409.59354"
|
||||
y="-284.40115"
|
||||
rx="0.95632279"
|
||||
ry="0.95632273" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
|
||||
transform="translate(343.99999,987.99997)"
|
||||
id="g5886"
|
||||
style="display:inline;enable-background:new">
|
||||
<path
|
||||
style="fill:none;stroke:url(#linearGradient5891-0-4);stroke-width:7.11431503;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
|
||||
id="path5835"
|
||||
inkscape:path-effect="#path-effect5837-4-6"
|
||||
inkscape:original-d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:original-d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
|
||||
inkscape:path-effect="#path-effect5837-4-6"
|
||||
id="path5880"
|
||||
d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
|
||||
style="fill:none;stroke:#4787c8;stroke-width:3.55715752;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#7ea7d3;stroke-width:1.18571913px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 197.45937,240.47455 c -0.17828,-0.29362 -0.20087,-0.67548 -0.0603,-0.98892 0.14055,-0.31344 0.43739,-0.54812 0.77144,-0.62817 0.33405,-0.08 0.69314,-0.01 0.99635,0.15175 0.30321,0.16144 0.55146,0.40727 0.79165,0.65284 l 3.66429,3.74643 12.87946,-12.98973 c 0.20796,-0.20974 0.42306,-0.41969 0.68548,-0.55522 0.26242,-0.13553 0.57293,-0.19052 0.85827,-0.11426 0.14267,0.0381 0.27708,0.10787 0.38874,0.20452 0.11167,0.0966 0.20021,0.22004 0.25479,0.35726 0.0546,0.13722 0.075,0.28793 0.0585,0.43468 -0.0165,0.14674 -0.07,0.28919 -0.15422,0.41052"
|
||||
id="path5882"
|
||||
inkscape:path-effect="#path-effect5884-4-7"
|
||||
inkscape:original-d="m 197.45937,240.47455 c 0.65604,-0.56057 2.02485,-1.34847 2.49911,-0.8125 l 3.66429,3.74643 12.87946,-12.98973 c 0.6875,-0.6875 2.09152,0.7375 2.09152,0.7375"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csccc" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.8 KiB |
@ -1,180 +0,0 @@
|
||||
/* Copyright 2011, Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU Lesser General Public License,
|
||||
* version 2.1, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* Login Dialog */
|
||||
|
||||
.login-dialog-title {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.login-dialog {
|
||||
border-radius: 16px;
|
||||
min-height: 150px;
|
||||
max-height: 700px;
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-fingerprint-message {
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-view {
|
||||
-st-vfade-offset: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:ltr {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:rtl {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
||||
font-size: 20pt;
|
||||
padding-left: 1em;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
|
||||
color: white;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-vertical-layout {
|
||||
spacing: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
|
||||
background-color: rgba(0,0,0,0.0);
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 8px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button {
|
||||
padding-top: 2em;
|
||||
}
|
||||
.login-dialog-not-listed-label {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-layout {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
.login-dialog-prompt-label {
|
||||
color: white;
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #5656cc;
|
||||
color: black;
|
||||
background-color: white;
|
||||
caret-color: black;
|
||||
caret-size: 1px;
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry .capslock-warning {
|
||||
icon-size: 16px;
|
||||
warning-color: #999;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry:insensitive {
|
||||
color: rgba(0,0,0,0.7);
|
||||
border: 2px solid #565656;
|
||||
}
|
||||
|
||||
.login-dialog-session-list {
|
||||
color: #ffffff;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:focus {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:active {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-scroll-view {
|
||||
background-gradient-start: rgba(80,80,80,0.3);
|
||||
background-gradient-end: rgba(80,80,80,0.7);
|
||||
background-gradient-direction: vertical;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(80,80,80,1.0);
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item:focus {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-triangle {
|
||||
padding-right: .5em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-box {
|
||||
spacing: .25em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-dot {
|
||||
width: .75em;
|
||||
height: .75em;
|
||||
}
|
130
data/theme/logged-in-indicator.svg
Normal file
@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="300"
|
||||
height="80"
|
||||
id="svg7355"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="logged-in-indicator.svg">
|
||||
<metadata
|
||||
id="metadata4175">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#2c1cff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
id="namedview4173"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.8760889"
|
||||
inkscape:cx="106.00403"
|
||||
inkscape:cy="80.68078"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g30864" />
|
||||
<defs
|
||||
id="defs7357">
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient36429"
|
||||
id="radialGradient7461"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.5919312,0,0,0.57582113,-20.687059,48.400487)"
|
||||
cx="47.428951"
|
||||
cy="167.16817"
|
||||
fx="47.428951"
|
||||
fy="167.16817"
|
||||
r="37" />
|
||||
<linearGradient
|
||||
id="linearGradient36429">
|
||||
<stop
|
||||
id="stop36431"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop36433"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient36471"
|
||||
id="radialGradient7463"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
|
||||
cx="49.067139"
|
||||
cy="242.50381"
|
||||
fx="49.067139"
|
||||
fy="242.50381"
|
||||
r="37.00671" />
|
||||
<linearGradient
|
||||
id="linearGradient36471">
|
||||
<stop
|
||||
id="stop36473"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop36475"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="37.00671"
|
||||
fy="242.50381"
|
||||
fx="49.067139"
|
||||
cy="242.50381"
|
||||
cx="49.067139"
|
||||
gradientTransform="matrix(3.4218418,0,0,0.03365337,-61.309005,138.5071)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient7488"
|
||||
xlink:href="#linearGradient36471" />
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g30864"
|
||||
transform="translate(255.223,70.118091)">
|
||||
<rect
|
||||
ry="3.4593496"
|
||||
rx="8.8641119"
|
||||
y="76.159348"
|
||||
x="12.596948"
|
||||
height="71.116341"
|
||||
width="182.22595"
|
||||
id="rect14000"
|
||||
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
id="rect34520"
|
||||
d="m 194.80022,146.83551 -182.559919,0"
|
||||
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:0.61184424;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
connector-curvature="0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
BIN
data/theme/message-tray-background.png
Normal file
After Width: | Height: | Size: 25 KiB |
109
data/theme/more-results.svg
Normal file
@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg12430"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="more-results.svg">
|
||||
<defs
|
||||
id="defs12432" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#7a7a7a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="8.3155237"
|
||||
inkscape:cy="0.89548874"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g14642-3-0"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1376"
|
||||
inkscape:window-x="1200"
|
||||
inkscape:window-y="187"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid13002" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata12435">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1036.3622)">
|
||||
<g
|
||||
style="display:inline"
|
||||
transform="translate(-141.99984,638.37113)"
|
||||
inkscape:label="zoom-in"
|
||||
id="g14642-3-0">
|
||||
<path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
|
||||
xlink:href="#rect11749-5-0-1-8"
|
||||
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
|
||||
id="path13004"
|
||||
inkscape:href="#rect11749-5-0-1-8"
|
||||
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
|
||||
transform="translate(0,1)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#path13004"
|
||||
id="use11960"
|
||||
transform="translate(1,-1)"
|
||||
width="16"
|
||||
height="16" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use11960"
|
||||
id="use11962"
|
||||
transform="translate(-2,0)"
|
||||
width="16"
|
||||
height="16" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
|
||||
id="rect11749-5-0-1-8" />
|
||||
<rect
|
||||
style="fill:none;stroke:none"
|
||||
id="rect3620-5-4"
|
||||
width="15.981825"
|
||||
height="16"
|
||||
x="142"
|
||||
y="398"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
BIN
data/theme/noise-texture.png
Normal file
After Width: | Height: | Size: 78 KiB |
@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="3"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.43599999000000000;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3779"
|
||||
width="3"
|
||||
height="10"
|
||||
x="0"
|
||||
y="0" />
|
||||
<rect
|
||||
style="fill:#536272;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="3"
|
||||
height="1"
|
||||
x="0"
|
||||
y="9" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 787 B |
@ -9,7 +9,7 @@
|
||||
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="21"
|
||||
width="17"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
@ -66,9 +66,9 @@
|
||||
<rect
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="3"
|
||||
width="7"
|
||||
height="2"
|
||||
x="9"
|
||||
x="5"
|
||||
y="8" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10"
|
||||
height="4"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="scroll-hhandle.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="rect3592"
|
||||
width="2"
|
||||
height="4"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect3592"
|
||||
id="use2825"
|
||||
transform="translate(8,0)"
|
||||
width="10"
|
||||
height="4" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use2825"
|
||||
id="use2827"
|
||||
transform="translate(-4,0)"
|
||||
width="10"
|
||||
height="4" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="4"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="scroll-hhandle.svg">
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="rect3592"
|
||||
width="2"
|
||||
height="4"
|
||||
x="0"
|
||||
y="-4"
|
||||
rx="0"
|
||||
ry="0"
|
||||
transform="matrix(0,1,-1,0,0,0)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect3592"
|
||||
id="use3705"
|
||||
transform="translate(0,4)"
|
||||
width="4"
|
||||
height="10" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use3705"
|
||||
id="use3707"
|
||||
transform="translate(0,4)"
|
||||
width="4"
|
||||
height="10" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
120
data/theme/summary-counter.svg
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
id="Foreground"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 23.272727 23.272727"
|
||||
enable-background="new 0 0 16 16"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="summary-counter.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata2399"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2397"><linearGradient
|
||||
id="linearGradient3173"><stop
|
||||
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3175" /><stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3177" /></linearGradient><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 8 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="16 : 8 : 1"
|
||||
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||
id="perspective2401" /><filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter16494-4"
|
||||
x="-0.20989846"
|
||||
width="1.4197969"
|
||||
y="-0.20903821"
|
||||
height="1.4180764"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.3282637"
|
||||
id="feGaussianBlur16496-8" /></filter><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient16498-6"
|
||||
id="radialGradient16504-1"
|
||||
cx="7.6582627"
|
||||
cy="5.8191104"
|
||||
fx="7.6582627"
|
||||
fy="5.8191104"
|
||||
r="8.6928644"
|
||||
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient16498-6"><stop
|
||||
style="stop-color:#9FD0FF;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop16500-8" /><stop
|
||||
style="stop-color:#3465A4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
|
||||
inkscape:window-height="709"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="15.386407"
|
||||
inkscape:cy="13.739577"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1179"
|
||||
inkscape:current-layer="g16402-8"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-maximized="1"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11246"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
|
||||
style="display:inline"
|
||||
id="g16402-8"
|
||||
transform="translate(4.7533483,2.8238929)"><g
|
||||
id="g3175-4"
|
||||
transform="translate(-0.89995416,0.94028614)"><path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
|
||||
xlink:href="#path2394-32"
|
||||
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
|
||||
id="path16480-5"
|
||||
inkscape:href="#path2394-32"
|
||||
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
|
||||
transform="translate(0,1.028519)" /><path
|
||||
clip-rule="evenodd"
|
||||
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
|
||||
id="path2394-32"
|
||||
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:nodetypes="csssc"
|
||||
inkscape:connector-curvature="0" /><g
|
||||
id="g3172-6" /></g></g></svg>
|
After Width: | Height: | Size: 5.4 KiB |
@ -9,7 +9,7 @@
|
||||
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="64"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg3273"
|
||||
version="1.1"
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
@ -9,7 +9,7 @@
|
||||
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="64"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg3012"
|
||||
version="1.1"
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
BIN
data/theme/ws-switch-arrow-down.png
Normal file
After Width: | Height: | Size: 850 B |
@ -1,376 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-down.svg">
|
||||
<metadata
|
||||
id="metadata3353">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="718"
|
||||
inkscape:window-height="480"
|
||||
id="namedview3351"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6979167"
|
||||
inkscape:cx="48"
|
||||
inkscape:cy="48"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg25070" />
|
||||
<defs
|
||||
id="defs25072">
|
||||
<linearGradient
|
||||
x1="-86.552246"
|
||||
y1="185.439"
|
||||
x2="-83.37072"
|
||||
y2="197.31261"
|
||||
id="linearGradient24957"
|
||||
xlink:href="#linearGradient4034-0-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(6,0)" />
|
||||
<linearGradient
|
||||
id="linearGradient4034-0-4">
|
||||
<stop
|
||||
id="stop4036-5-7"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4038-9-6"
|
||||
style="stop-color:#babdb6;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter24765">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix24767" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix24769" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="-74.520325"
|
||||
y1="169.06032"
|
||||
x2="-74.520325"
|
||||
y2="205.94189"
|
||||
id="linearGradient24955"
|
||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-5,0)" />
|
||||
<linearGradient
|
||||
id="linearGradient4632-1-3-9-3-2">
|
||||
<stop
|
||||
id="stop4634-1-8-3-9-0"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4636-1-9-9-8-8"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0.0274937" />
|
||||
<stop
|
||||
id="stop4638-8-3-9-6-6"
|
||||
style="stop-color:#f2f2f2;stop-opacity:1"
|
||||
offset="0.274937" />
|
||||
<stop
|
||||
id="stop4640-8-5-7-8-9"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.38707438" />
|
||||
<stop
|
||||
id="stop4642-5-41-9-6-9"
|
||||
style="stop-color:#d9dad8;stop-opacity:1"
|
||||
offset="0.66528589" />
|
||||
<stop
|
||||
id="stop4644-5-2-7-9-2"
|
||||
style="stop-color:#dfe0dd;stop-opacity:1"
|
||||
offset="0.76745707" />
|
||||
<stop
|
||||
id="stop4646-3-2-3-7-3"
|
||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
r="2.3554697"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
id="radialGradient24959"
|
||||
xlink:href="#linearGradient4869-4-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-1">
|
||||
<stop
|
||||
id="stop4871-6-2"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4879-7-4"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.31807542" />
|
||||
<stop
|
||||
id="stop4877-6-1"
|
||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
||||
offset="0.74691135" />
|
||||
<stop
|
||||
id="stop4873-1-0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25011">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25013" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25015" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
r="2.3554697"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
id="radialGradient24961"
|
||||
xlink:href="#linearGradient4869-4-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-0">
|
||||
<stop
|
||||
id="stop4871-6-8"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4879-7-5"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.31807542" />
|
||||
<stop
|
||||
id="stop4877-6-5"
|
||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
||||
offset="0.74691135" />
|
||||
<stop
|
||||
id="stop4873-1-4"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25023">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25025" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25027" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898"
|
||||
id="linearGradient24963"
|
||||
xlink:href="#linearGradient4941"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4941">
|
||||
<stop
|
||||
id="stop4943"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4945"
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25033">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25035" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25037" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898"
|
||||
id="linearGradient24965"
|
||||
xlink:href="#linearGradient4941-7"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4941-7">
|
||||
<stop
|
||||
id="stop4943-2"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4945-5"
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25043">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25045" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25047" />
|
||||
</filter>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25049">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25051" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25053" />
|
||||
</filter>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25055">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25057" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25059" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
|
||||
id="g4030-1-8"
|
||||
style="stroke:#000000;stroke-opacity:1;display:inline">
|
||||
<path
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
|
||||
id="path3165-7-3-1"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
|
||||
id="path3165-7-3-1-9"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 13 KiB |
BIN
data/theme/ws-switch-arrow-up.png
Normal file
After Width: | Height: | Size: 841 B |
@ -1,447 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-up.svg">
|
||||
<defs
|
||||
id="defs25072">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective25078" />
|
||||
<inkscape:perspective
|
||||
id="perspective24985"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4034-0-4"
|
||||
id="linearGradient24957"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(6)"
|
||||
x1="-86.552246"
|
||||
y1="185.439"
|
||||
x2="-83.37072"
|
||||
y2="197.31261" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4034-0-4">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4036-5-7" />
|
||||
<stop
|
||||
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4038-9-6" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter24765"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24767"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24769"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
||||
id="linearGradient24955"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-5)"
|
||||
x1="-74.520325"
|
||||
y1="169.06032"
|
||||
x2="-74.520325"
|
||||
y2="205.94189" />
|
||||
<linearGradient
|
||||
id="linearGradient4632-1-3-9-3-2">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4634-1-8-3-9-0" />
|
||||
<stop
|
||||
id="stop4636-1-9-9-8-8"
|
||||
offset="0.0274937"
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4638-8-3-9-6-6"
|
||||
offset="0.274937"
|
||||
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4640-8-5-7-8-9"
|
||||
offset="0.38707438"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4642-5-41-9-6-9"
|
||||
offset="0.66528589"
|
||||
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4644-5-2-7-9-2"
|
||||
offset="0.76745707"
|
||||
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4646-3-2-3-7-3" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-1"
|
||||
id="radialGradient24959"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-1">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-2" />
|
||||
<stop
|
||||
id="stop4879-7-4"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-1"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-0" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25011"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25013"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25015"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-0"
|
||||
id="radialGradient24961"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-0">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-8" />
|
||||
<stop
|
||||
id="stop4879-7-5"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-5"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-4" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25023"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25025"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25027"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941"
|
||||
id="linearGradient24963"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25033"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25035"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25037"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941-7"
|
||||
id="linearGradient24965"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941-7">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943-2" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945-5" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25043"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25045"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25047"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25049"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25051"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25053"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25055"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25057"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25059"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="-12.356322"
|
||||
inkscape:cy="57.536221"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1200"
|
||||
inkscape:window-height="840"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata25075">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0, 48)">
|
||||
<g
|
||||
id="g3181"
|
||||
transform="matrix(0,1,-1,0,48.0003,-48)">
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1;display:inline"
|
||||
transform="matrix(2,0,0,2,193.25,-374.967)"
|
||||
id="g4030-1-8">
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
sodipodi:nodetypes="ccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccscccsc"
|
||||
id="path3165-7-3-1"
|
||||
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path3165-7-3-1-9"
|
||||
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 16 KiB |
@ -68,13 +68,31 @@ IGNORE_HFILES= \
|
||||
gactionobserver.h \
|
||||
shell-recorder-src.h
|
||||
|
||||
if !BUILD_RECORDER
|
||||
IGNORE_HFILES += shell-recorder.h
|
||||
endif
|
||||
|
||||
# Images to copy into HTML directory.
|
||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
||||
HTML_IMAGES=
|
||||
|
||||
doc-gen-org.gnome.Shell.SearchProvider.xml: $(top_srcdir)/data/org.gnome.ShellSearchProvider.xml
|
||||
gdbus-codegen \
|
||||
--interface-prefix org.gnome.ShellSearchProvider. \
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.ShellSearchProvider.xml
|
||||
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||
gdbus-codegen \
|
||||
--interface-prefix org.gnome.ShellSearchProvider2. \
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||
content_files=
|
||||
content_files= \
|
||||
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml
|
||||
|
||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||
# These files must be listed here *and* in content_files
|
||||
|
@ -29,8 +29,6 @@
|
||||
<chapter>
|
||||
<title>Search</title>
|
||||
<xi:include href="xml/shell-app-system.xml"/>
|
||||
<xi:include href="xml/shell-contact-system.xml"/>
|
||||
<xi:include href="xml/shell-doc-system.xml"/>
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Tray Icons</title>
|
||||
@ -42,10 +40,11 @@
|
||||
<chapter>
|
||||
<title>Recorder</title>
|
||||
<xi:include href="xml/shell-recorder.xml"/>
|
||||
<xi:include href="xml/shell-recorder-src.xml"/>
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Integration helpers and utilities</title>
|
||||
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
|
||||
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
|
||||
<xi:include href="xml/shell-global.xml"/>
|
||||
<xi:include href="xml/shell-wm.xml"/>
|
||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
||||
|
@ -20,7 +20,6 @@
|
||||
<title>Abstract classes and Interfaces</title>
|
||||
<xi:include href="xml/st-widget.xml"/>
|
||||
<xi:include href="xml/st-widget-accessible.xml"/>
|
||||
<xi:include href="xml/st-container.xml"/>
|
||||
<xi:include href="xml/st-scrollable.xml"/>
|
||||
</chapter>
|
||||
<chapter id="widgets">
|
||||
@ -30,14 +29,11 @@
|
||||
<xi:include href="xml/st-entry.xml"/>
|
||||
<xi:include href="xml/st-icon.xml"/>
|
||||
<xi:include href="xml/st-label.xml"/>
|
||||
<xi:include href="xml/st-tooltip.xml"/>
|
||||
</chapter>
|
||||
<chapter id="containers">
|
||||
<title>Containers</title>
|
||||
<xi:include href="xml/st-bin.xml"/>
|
||||
<xi:include href="xml/st-box-layout.xml"/>
|
||||
<xi:include href="xml/st-group.xml"/>
|
||||
<xi:include href="xml/st-overflow-box.xml"/>
|
||||
<xi:include href="xml/st-scroll-view.xml"/>
|
||||
<xi:include href="xml/st-table.xml"/>
|
||||
</chapter>
|
||||
|
@ -52,13 +52,6 @@ its dependencies to build from tarballs.</description>
|
||||
<gnome:userid>walters</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Dan Winship</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:danw@gnome.org" />
|
||||
<gnome:userid>danw</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Marina Zhurakhinskaya</foaf:name>
|
||||
@ -66,4 +59,11 @@ its dependencies to build from tarballs.</description>
|
||||
<gnome:userid>marinaz</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Florian Müllner</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
|
||||
<gnome:userid>fmuellner</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
</Project>
|
||||
|
@ -1,75 +1,94 @@
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = misc/config.js.in
|
||||
CLEANFILES = misc/config.js
|
||||
|
||||
misc/config.js: misc/config.js.in Makefile
|
||||
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
||||
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
|
||||
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
|
||||
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
|
||||
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
|
||||
-e "s|[@]datadir@|$(datadir)|g" \
|
||||
-e "s|[@]libexecdir@|$(libexecdir)|g" \
|
||||
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \
|
||||
$< > $@
|
||||
|
||||
jsdir = $(pkgdatadir)/js
|
||||
|
||||
nobase_dist_js_DATA = \
|
||||
gdm/batch.js \
|
||||
gdm/consoleKit.js \
|
||||
gdm/fingerprint.js \
|
||||
gdm/loginDialog.js \
|
||||
gdm/powerMenu.js \
|
||||
gdm/realmd.js \
|
||||
gdm/util.js \
|
||||
extensionPrefs/main.js \
|
||||
misc/config.js \
|
||||
misc/docInfo.js \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/format.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/history.js \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
misc/modemManager.js \
|
||||
misc/params.js \
|
||||
misc/screenSaver.js \
|
||||
misc/util.js \
|
||||
perf/core.js \
|
||||
ui/altTab.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/automountManager.js \
|
||||
ui/autorunManager.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/contactDisplay.js \
|
||||
ui/checkBox.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
ui/dnd.js \
|
||||
ui/docDisplay.js \
|
||||
ui/endSessionDialog.js \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/environment.js \
|
||||
ui/flashspot.js \
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/grabHelper.js \
|
||||
ui/iconGrid.js \
|
||||
ui/keyboard.js \
|
||||
ui/layout.js \
|
||||
ui/lightbox.js \
|
||||
ui/link.js \
|
||||
ui/lookingGlass.js \
|
||||
ui/magnifier.js \
|
||||
ui/magnifierDBus.js \
|
||||
ui/main.js \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/networkAgent.js \
|
||||
ui/separator.js \
|
||||
ui/sessionMode.js \
|
||||
ui/shellEntry.js \
|
||||
ui/shellMountOperation.js \
|
||||
ui/notificationDaemon.js \
|
||||
ui/overview.js \
|
||||
ui/panel.js \
|
||||
ui/panelMenu.js \
|
||||
ui/placeDisplay.js \
|
||||
ui/polkitAuthenticationAgent.js \
|
||||
ui/pointerWatcher.js \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/runDialog.js \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
ui/searchDisplay.js \
|
||||
ui/shellDBus.js \
|
||||
ui/statusIconDispatcher.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/lockScreenMenu.js \
|
||||
ui/status/network.js \
|
||||
ui/status/power.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/telepathyClient.js \
|
||||
ui/switcherPopup.js \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userMenu.js \
|
||||
ui/viewSelector.js \
|
||||
ui/wanda.js \
|
||||
@ -79,4 +98,13 @@ nobase_dist_js_DATA = \
|
||||
ui/workspaceThumbnail.js \
|
||||
ui/workspacesView.js \
|
||||
ui/workspaceSwitcherPopup.js \
|
||||
ui/xdndHandler.js
|
||||
ui/xdndHandler.js \
|
||||
ui/components/__init__.js \
|
||||
ui/components/autorunManager.js \
|
||||
ui/components/automountManager.js \
|
||||
ui/components/networkAgent.js \
|
||||
ui/components/polkitAgent.js \
|
||||
ui/components/recorder.js \
|
||||
ui/components/telepathyClient.js \
|
||||
ui/components/keyring.js \
|
||||
$(NULL)
|
||||
|
270
js/extensionPrefs/main.js
Normal file
@ -0,0 +1,270 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Gettext = imports.gettext;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Format = imports.format;
|
||||
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
<arg type="s" name="error"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||||
|
||||
function stripPrefix(string, prefix) {
|
||||
if (string.slice(0, prefix.length) == prefix)
|
||||
return string.slice(prefix.length);
|
||||
return string;
|
||||
}
|
||||
|
||||
const Application = new Lang.Class({
|
||||
Name: 'Application',
|
||||
_init: function() {
|
||||
GLib.set_prgname('gnome-shell-extension-prefs');
|
||||
this.application = new Gtk.Application({
|
||||
application_id: 'org.gnome.shell.ExtensionPrefs',
|
||||
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
|
||||
});
|
||||
|
||||
this.application.connect('activate', Lang.bind(this, this._onActivate));
|
||||
this.application.connect('command-line', Lang.bind(this, this._onCommandLine));
|
||||
this.application.connect('startup', Lang.bind(this, this._onStartup));
|
||||
|
||||
this._extensionPrefsModules = {};
|
||||
|
||||
this._extensionIters = {};
|
||||
},
|
||||
|
||||
_buildModel: function() {
|
||||
this._model = new Gtk.ListStore();
|
||||
this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
|
||||
},
|
||||
|
||||
_extensionAvailable: function(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
if (ExtensionUtils.isOutOfDate(extension))
|
||||
return false;
|
||||
|
||||
if (!extension.dir.get_child('prefs.js').query_exists(null))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_setExtensionInsensitive: function(layout, cell, model, iter, data) {
|
||||
let uuid = model.get_value(iter, 0);
|
||||
cell.set_sensitive(this._extensionAvailable(uuid));
|
||||
},
|
||||
|
||||
_getExtensionPrefsModule: function(extension) {
|
||||
let uuid = extension.metadata.uuid;
|
||||
|
||||
if (this._extensionPrefsModules.hasOwnProperty(uuid))
|
||||
return this._extensionPrefsModules[uuid];
|
||||
|
||||
ExtensionUtils.installImporter(extension);
|
||||
|
||||
let prefsModule = extension.imports.prefs;
|
||||
prefsModule.init(extension.metadata);
|
||||
|
||||
this._extensionPrefsModules[uuid] = prefsModule;
|
||||
return prefsModule;
|
||||
},
|
||||
|
||||
_selectExtension: function(uuid) {
|
||||
if (!this._extensionAvailable(uuid))
|
||||
return;
|
||||
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
let widget;
|
||||
|
||||
try {
|
||||
let prefsModule = this._getExtensionPrefsModule(extension);
|
||||
widget = prefsModule.buildPrefsWidget();
|
||||
} catch (e) {
|
||||
widget = this._buildErrorUI(extension, e);
|
||||
}
|
||||
|
||||
// Destroy the current prefs widget, if it exists
|
||||
if (this._extensionPrefsBin.get_child())
|
||||
this._extensionPrefsBin.get_child().destroy();
|
||||
|
||||
this._extensionPrefsBin.add(widget);
|
||||
this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
|
||||
},
|
||||
|
||||
_extensionSelected: function() {
|
||||
let [success, iter] = this._extensionSelector.get_active_iter();
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
let uuid = this._model.get_value(iter, 0);
|
||||
this._selectExtension(uuid);
|
||||
},
|
||||
|
||||
_buildErrorUI: function(extension, exc) {
|
||||
let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
let label = new Gtk.Label({
|
||||
label: _("There was an error loading the preferences dialog for %s:").format(extension.metadata.name)
|
||||
});
|
||||
box.add(label);
|
||||
|
||||
let errortext = '';
|
||||
errortext += exc;
|
||||
errortext += '\n\n';
|
||||
errortext += 'Stack trace:\n';
|
||||
|
||||
// Indent stack trace.
|
||||
errortext += exc.stack.split('\n').map(function(line) {
|
||||
return ' ' + line;
|
||||
}).join('\n');
|
||||
|
||||
let scroll = new Gtk.ScrolledWindow({ vexpand: true });
|
||||
let buffer = new Gtk.TextBuffer({ text: errortext });
|
||||
let textview = new Gtk.TextView({ buffer: buffer });
|
||||
textview.override_font(Pango.font_description_from_string('monospace'));
|
||||
scroll.add(textview);
|
||||
box.add(scroll);
|
||||
|
||||
box.show_all();
|
||||
return box;
|
||||
},
|
||||
|
||||
_buildUI: function(app) {
|
||||
this._window = new Gtk.ApplicationWindow({ application: app,
|
||||
window_position: Gtk.WindowPosition.CENTER,
|
||||
title: _("GNOME Shell Extension Preferences") });
|
||||
|
||||
this._window.set_size_request(600, 400);
|
||||
|
||||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
this._window.add(vbox);
|
||||
|
||||
let toolbar = new Gtk.Toolbar();
|
||||
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
|
||||
vbox.add(toolbar);
|
||||
let toolitem;
|
||||
|
||||
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
|
||||
use_markup: true });
|
||||
toolitem = new Gtk.ToolItem({ child: label });
|
||||
toolbar.add(toolitem);
|
||||
|
||||
this._extensionSelector = new Gtk.ComboBox({ model: this._model,
|
||||
margin_left: 8,
|
||||
hexpand: true });
|
||||
this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
|
||||
|
||||
let renderer = new Gtk.CellRendererText();
|
||||
this._extensionSelector.pack_start(renderer, true);
|
||||
this._extensionSelector.add_attribute(renderer, 'text', 1);
|
||||
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
|
||||
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
|
||||
|
||||
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
|
||||
toolitem.set_expand(true);
|
||||
toolbar.add(toolitem);
|
||||
|
||||
this._extensionPrefsBin = new Gtk.Frame();
|
||||
vbox.add(this._extensionPrefsBin);
|
||||
|
||||
let label = new Gtk.Label({
|
||||
label: _("Select an extension to configure using the combobox above."),
|
||||
vexpand: true
|
||||
});
|
||||
|
||||
this._extensionPrefsBin.add(label);
|
||||
|
||||
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
|
||||
this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
|
||||
if (ExtensionUtils.extensions[uuid] !== undefined)
|
||||
this._scanExtensions();
|
||||
}));
|
||||
|
||||
this._window.show_all();
|
||||
},
|
||||
|
||||
_scanExtensions: function() {
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||
finder.scanExtensions();
|
||||
},
|
||||
|
||||
_extensionFound: function(signals, extension) {
|
||||
let iter = this._model.append();
|
||||
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||
this._extensionIters[extension.uuid] = iter;
|
||||
},
|
||||
|
||||
|
||||
_onActivate: function() {
|
||||
this._window.present();
|
||||
},
|
||||
|
||||
_onStartup: function(app) {
|
||||
this._buildModel();
|
||||
this._buildUI(app);
|
||||
this._scanExtensions();
|
||||
},
|
||||
|
||||
_onCommandLine: function(app, commandLine) {
|
||||
app.activate();
|
||||
let args = commandLine.get_arguments();
|
||||
if (args.length) {
|
||||
let uuid = args[0];
|
||||
|
||||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||||
uuid = stripPrefix(uuid, "extension:///");
|
||||
|
||||
if (!this._extensionAvailable(uuid))
|
||||
return 1;
|
||||
|
||||
this._selectExtension(uuid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
function initEnvironment() {
|
||||
// Monkey-patch in a "global" object that fakes some Shell utilities
|
||||
// that ExtensionUtils depends on.
|
||||
window.global = {
|
||||
log: function() {
|
||||
print([].join.call(arguments, ', '));
|
||||
},
|
||||
|
||||
logError: function(s) {
|
||||
log('ERROR: ' + s);
|
||||
},
|
||||
|
||||
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
|
||||
};
|
||||
|
||||
String.prototype.format = Format.format;
|
||||
}
|
||||
|
||||
function main(argv) {
|
||||
initEnvironment();
|
||||
|
||||
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
||||
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
||||
|
||||
let app = new Application();
|
||||
app.application.run(argv);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
|
||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
|
||||
<method name='CanRestart'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='CanStop'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='Restart' />
|
||||
<method name='Stop' />
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||
|
||||
function ConsoleKitManager() {
|
||||
return new ConsoleKitProxy(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
};
|
@ -11,10 +11,16 @@ const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
|
||||
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
|
||||
|
||||
function FprintManager() {
|
||||
return new FprintManagerProxy(Gio.DBus.system,
|
||||
'net.reactivated.Fprint',
|
||||
'/net/reactivated/Fprint/Manager');
|
||||
};
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
||||
g_interface_name: FprintManagerInfo.name,
|
||||
g_interface_info: FprintManagerInfo,
|
||||
g_name: 'net.reactivated.Fprint',
|
||||
g_object_path: '/net/reactivated/Fprint/Manager',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
@ -18,10 +18,12 @@
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const ConsoleKit = imports.gdm.consoleKit;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -30,16 +32,17 @@ const PowerMenuButton = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('system-shutdown', null);
|
||||
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
/* Translators: accessible name of the power menu in the login screen */
|
||||
this.parent('system-shutdown-symbolic', _("Power"));
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed::disable-restart-buttons',
|
||||
Lang.bind(this, this._updateVisibility));
|
||||
|
||||
this._createSubMenu();
|
||||
|
||||
this._upClient.connect('notify::can-suspend',
|
||||
Lang.bind(this, this._updateHaveSuspend));
|
||||
this._updateHaveSuspend();
|
||||
|
||||
// ConsoleKit doesn't send notifications when shutdown/reboot
|
||||
// are disabled, so we update the menu item each time the menu opens
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
@ -47,64 +50,41 @@ const PowerMenuButton = new Lang.Class({
|
||||
if (open) {
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
}
|
||||
}));
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
|
||||
this.actor.hide();
|
||||
else
|
||||
this.actor.show();
|
||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._consoleKitManager.CanStopRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
|
||||
this._haveShutdown = result;
|
||||
else
|
||||
this._haveShutdown = false;
|
||||
|
||||
if (this._haveShutdown) {
|
||||
this._powerOffItem.actor.show();
|
||||
} else {
|
||||
this._powerOffItem.actor.hide();
|
||||
}
|
||||
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveRestart: function() {
|
||||
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._loginManager.canReboot(Lang.bind(this, function(result) {
|
||||
this._haveRestart = result;
|
||||
else
|
||||
this._haveRestart = false;
|
||||
|
||||
if (this._haveRestart) {
|
||||
this._restartItem.actor.show();
|
||||
} else {
|
||||
this._restartItem.actor.hide();
|
||||
}
|
||||
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
|
||||
if (this._haveSuspend)
|
||||
this._suspendItem.actor.show();
|
||||
else
|
||||
this._suspendItem.actor.hide();
|
||||
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
@ -127,17 +107,23 @@ const PowerMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_onActivateSuspend: function() {
|
||||
if (this._haveSuspend)
|
||||
this._upClient.suspend_sync(null);
|
||||
if (!this._haveSuspend)
|
||||
return;
|
||||
|
||||
this._loginManager.suspend();
|
||||
},
|
||||
|
||||
_onActivateRestart: function() {
|
||||
if (this._haveRestart)
|
||||
this._consoleKitManager.RestartRemote();
|
||||
if (!this._haveRestart)
|
||||
return;
|
||||
|
||||
this._loginManager.reboot();
|
||||
},
|
||||
|
||||
_onActivatePowerOff: function() {
|
||||
if (this._haveShutdown)
|
||||
this._consoleKitManager.StopRemote();
|
||||
if (!this._haveShutdown)
|
||||
return;
|
||||
|
||||
this._loginManager.powerOff();
|
||||
}
|
||||
});
|
||||
|
139
js/gdm/realmd.js
Normal file
@ -0,0 +1,139 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
|
||||
<property name="Name" type="s" access="read"/>
|
||||
<property name="Version" type="s" access="read"/>
|
||||
<property name="Realms" type="ao" access="read"/>
|
||||
<method name="Discover">
|
||||
<arg name="string" type="s" direction="in"/>
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
<arg name="relevance" type="i" direction="out"/>
|
||||
<arg name="realm" type="ao" direction="out"/>
|
||||
</method>
|
||||
</interface>;
|
||||
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
|
||||
|
||||
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
|
||||
<method name="Cancel">
|
||||
<arg name="operation" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="Release" />
|
||||
<method name="SetLocale">
|
||||
<arg name="locale" type="s" direction="in"/>
|
||||
</method>
|
||||
<signal name="Diagnostics">
|
||||
<arg name="data" type="s"/>
|
||||
<arg name="operation" type="s"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
|
||||
|
||||
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
|
||||
<property name="Name" type="s" access="read"/>
|
||||
<property name="Configured" type="s" access="read"/>
|
||||
<property name="Details" type="a(ss)" access="read"/>
|
||||
<property name="LoginFormats" type="as" access="read"/>
|
||||
<property name="LoginPolicy" type="s" access="read"/>
|
||||
<property name="PermittedLogins" type="as" access="read"/>
|
||||
<property name="SupportedInterfaces" type="as" access="read"/>
|
||||
<method name="ChangeLoginPolicy">
|
||||
<arg name="login_policy" type="s" direction="in"/>
|
||||
<arg name="permitted_add" type="as" direction="in"/>
|
||||
<arg name="permitted_remove" type="as" direction="in"/>
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
</method>
|
||||
<method name="Deconfigure">
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
</method>
|
||||
</interface>;
|
||||
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
|
||||
|
||||
const Manager = new Lang.Class({
|
||||
Name: 'Manager',
|
||||
|
||||
_init: function(parentActor) {
|
||||
this._aggregateProvider = Provider(Gio.DBus.system,
|
||||
'org.freedesktop.realmd',
|
||||
'/org/freedesktop/realmd',
|
||||
Lang.bind(this, this._reloadRealms))
|
||||
this._realms = {};
|
||||
|
||||
this._aggregateProvider.connect('g-properties-changed',
|
||||
Lang.bind(this, function(proxy, properties) {
|
||||
if ('Realms' in properties.deep_unpack())
|
||||
this._reloadRealms();
|
||||
}));
|
||||
},
|
||||
|
||||
_reloadRealms: function() {
|
||||
let realmPaths = this._aggregateProvider.Realms;
|
||||
|
||||
if (!realmPaths)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < realmPaths.length; i++) {
|
||||
let realm = Realm(Gio.DBus.system,
|
||||
'org.freedesktop.realmd',
|
||||
realmPaths[i],
|
||||
Lang.bind(this, this._onRealmLoaded));
|
||||
}
|
||||
},
|
||||
|
||||
_reloadRealm: function(realm) {
|
||||
if (!realm.Configured) {
|
||||
if (this._realms[realm.get_object_path()])
|
||||
delete this._realms[realm.get_object_path()];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._realms[realm.get_object_path()] = realm;
|
||||
|
||||
this._updateLoginFormat();
|
||||
},
|
||||
|
||||
_onRealmLoaded: function(realm, error) {
|
||||
if (error)
|
||||
return;
|
||||
|
||||
this._reloadRealm(realm);
|
||||
|
||||
realm.connect('g-properties-changed',
|
||||
Lang.bind(this, function(proxy, properties) {
|
||||
if ('Configured' in properties.deep_unpack())
|
||||
this._reloadRealm();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateLoginFormat: function() {
|
||||
let newLoginFormat;
|
||||
|
||||
for (let realmPath in this._realms) {
|
||||
let realm = this._realms[realmPath];
|
||||
if (realm.LoginFormats && realm.LoginFormats.length > 0) {
|
||||
newLoginFormat = realm.LoginFormats[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._loginFormat != newLoginFormat) {
|
||||
this._loginFormat = newLoginFormat;
|
||||
this.emit('login-format-changed', newLoginFormat);
|
||||
}
|
||||
},
|
||||
|
||||
get loginFormat() {
|
||||
if (this._loginFormat !== undefined)
|
||||
return this._loginFormat;
|
||||
|
||||
this._updateLoginFormat();
|
||||
|
||||
return this._loginFormat;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Manager.prototype)
|
372
js/gdm/util.js
Normal file
@ -0,0 +1,372 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const Fprint = imports.gdm.fingerprint;
|
||||
const Realmd = imports.gdm.realmd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||
const FADE_ANIMATION_TIME = 0.16;
|
||||
|
||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||
|
||||
const LOGO_KEY = 'logo';
|
||||
const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
||||
|
||||
function fadeInActor(actor) {
|
||||
if (actor.opacity == 255 && actor.visible)
|
||||
return null;
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
actor.show();
|
||||
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
|
||||
|
||||
actor.opacity = 0;
|
||||
actor.set_height(0);
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 255,
|
||||
height: naturalHeight,
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this.set_height(-1);
|
||||
hold.release();
|
||||
},
|
||||
});
|
||||
|
||||
return hold;
|
||||
}
|
||||
|
||||
function fadeOutActor(actor) {
|
||||
if (!actor.visible || actor.opacity == 0) {
|
||||
actor.opacity = 0;
|
||||
actor.hide();
|
||||
return null;
|
||||
}
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 0,
|
||||
height: 0,
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this.hide();
|
||||
this.set_height(-1);
|
||||
hold.release();
|
||||
},
|
||||
});
|
||||
return hold;
|
||||
}
|
||||
|
||||
const ShellUserVerifier = new Lang.Class({
|
||||
Name: 'ShellUserVerifier',
|
||||
|
||||
_init: function(client, params) {
|
||||
params = Params.parse(params, { reauthenticationOnly: false });
|
||||
this._reauthOnly = params.reauthenticationOnly;
|
||||
|
||||
this._client = client;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._realmManager = new Realmd.Manager();
|
||||
|
||||
this._failCounter = 0;
|
||||
},
|
||||
|
||||
begin: function(userName, hold) {
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this._hold = hold;
|
||||
this._userName = userName;
|
||||
|
||||
this._checkForFingerprintReader();
|
||||
|
||||
if (userName) {
|
||||
// If possible, reauthenticate an already running session,
|
||||
// so any session specific credentials get updated appropriately
|
||||
this._client.open_reauthentication_channel(userName, this._cancellable,
|
||||
Lang.bind(this, this._reauthenticationChannelOpened));
|
||||
} else {
|
||||
this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this._cancellable)
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._userVerifier)
|
||||
this._userVerifier.call_cancel_sync(null);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
if (this._cancellable) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable = null;
|
||||
}
|
||||
|
||||
if (this._userVerifier) {
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
}
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
// Clear any previous message
|
||||
this.emit('show-message', null, null);
|
||||
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
},
|
||||
|
||||
_checkForFingerprintReader: function() {
|
||||
this._haveFingerprintReader = false;
|
||||
|
||||
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
||||
return;
|
||||
|
||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
||||
function(device, error) {
|
||||
if (!error && device)
|
||||
this._haveFingerprintReader = true;
|
||||
}));
|
||||
},
|
||||
|
||||
_reportInitError: function(where, error) {
|
||||
logError(error, where);
|
||||
this._hold.release();
|
||||
|
||||
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
|
||||
this._verificationFailed(false);
|
||||
},
|
||||
|
||||
_reauthenticationChannelOpened: function(client, result) {
|
||||
try {
|
||||
this._userVerifier = client.open_reauthentication_channel_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e if e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
|
||||
!this._reauthOnly) {
|
||||
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there is
|
||||
// no session to reauthenticate. Fall back to performing verification
|
||||
// from this login session
|
||||
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to open reauthentication channel', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
this._hold.release();
|
||||
},
|
||||
|
||||
_userVerifierGot: function(client, result) {
|
||||
try {
|
||||
this._userVerifier = client.get_user_verifier_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to obtain user verifier', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
this._hold.release();
|
||||
},
|
||||
|
||||
_connectSignals: function() {
|
||||
this._userVerifier.connect('info', Lang.bind(this, this._onInfo));
|
||||
this._userVerifier.connect('problem', Lang.bind(this, this._onProblem));
|
||||
this._userVerifier.connect('info-query', Lang.bind(this, this._onInfoQuery));
|
||||
this._userVerifier.connect('secret-info-query', Lang.bind(this, this._onSecretInfoQuery));
|
||||
this._userVerifier.connect('conversation-stopped', Lang.bind(this, this._onConversationStopped));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
},
|
||||
|
||||
_beginVerification: function() {
|
||||
this._hold.acquire();
|
||||
|
||||
if (this._userName) {
|
||||
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification for user', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
}));
|
||||
|
||||
if (this._haveFingerprintReader) {
|
||||
this._hold.acquire();
|
||||
|
||||
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start fingerprint verification for user', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onInfo: function(client, serviceName, info) {
|
||||
// We don't display fingerprint messages, because they
|
||||
// have words like UPEK in them. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||
this._haveFingerprintReader) {
|
||||
|
||||
// Translators: this message is shown below the password entry field
|
||||
// to indicate the user can swipe their finger instead
|
||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this.emit('show-message', info, 'login-dialog-message-info');
|
||||
}
|
||||
},
|
||||
|
||||
_onProblem: function(client, serviceName, problem) {
|
||||
// we don't want to show auth failed messages to
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
this.emit('show-message', problem, 'login-dialog-message-warning');
|
||||
},
|
||||
|
||||
_showRealmLoginHint: function() {
|
||||
if (this._realmManager.loginFormat) {
|
||||
let hint = this._realmManager.loginFormat;
|
||||
|
||||
hint = hint.replace(/%U/g, 'user');
|
||||
hint = hint.replace(/%D/g, 'DOMAIN');
|
||||
hint = hint.replace(/%[^UD]/g, '');
|
||||
|
||||
// Translators: this message is shown below the username entry field
|
||||
// to clue the user in on how to login to the local network realm
|
||||
this.emit('show-login-hint',
|
||||
_("(e.g., user or %s)").format(hint));
|
||||
}
|
||||
},
|
||||
|
||||
_onInfoQuery: function(client, serviceName, question) {
|
||||
// We only expect questions to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
|
||||
this._showRealmLoginHint();
|
||||
this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
|
||||
Lang.bind(this, this._showRealmLoginHint));
|
||||
|
||||
this.emit('ask-question', serviceName, question, '');
|
||||
},
|
||||
|
||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||
// We only expect secret requests to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
|
||||
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
this.clear();
|
||||
|
||||
// Clear previous attempts to authenticate
|
||||
this._failCounter = 0;
|
||||
|
||||
this.emit('reset');
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this.emit('verification-complete');
|
||||
},
|
||||
|
||||
_verificationFailed: function(retry) {
|
||||
// For Not Listed / enterprise logins, immediately reset
|
||||
// the dialog
|
||||
// Otherwise, we allow ALLOWED_FAILURES attempts. After that, we
|
||||
// go back to the welcome screen.
|
||||
|
||||
this._failCounter++;
|
||||
let canRetry = retry && this._userName &&
|
||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||
|
||||
if (canRetry) {
|
||||
this.clear();
|
||||
this.begin(this._userName, new Batch.Hold());
|
||||
} else {
|
||||
// Allow some time to see the message, then reset everything
|
||||
Mainloop.timeout_add(3000, Lang.bind(this, function() {
|
||||
this.cancel();
|
||||
|
||||
this._onReset();
|
||||
}));
|
||||
}
|
||||
|
||||
this.emit('verification-failed');
|
||||
},
|
||||
|
||||
_onConversationStopped: function(client, serviceName) {
|
||||
// if the password service fails, then cancel everything.
|
||||
// But if, e.g., fingerprint fails, still give
|
||||
// password authentication a chance to succeed
|
||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this._verificationFailed(true);
|
||||
}
|
||||
|
||||
this.emit('hide-login-hint');
|
||||
|
||||
if (this._realmLoginHintSignalId) {
|
||||
this._realmManager.disconnect(this._realmLoginHintSignalId);
|
||||
this._realmLoginHintSignalId = 0;
|
||||
}
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
@ -4,9 +4,12 @@
|
||||
const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||
/* The version of this package */
|
||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||
/* The version of GJS we're linking to */
|
||||
const GJS_VERSION = '@GJS_VERSION@';
|
||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
/* The system TLS CA list */
|
||||
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
|
||||
/* gettext package */
|
||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
||||
/* locale dir */
|
||||
const LOCALEDIR = '@datadir@/locale';
|
||||
/* other standard directories */
|
||||
const LIBEXECDIR = '@libexecdir@';
|
||||
const SYSCONFDIR = '@sysconfdir@';
|
||||
|
@ -1,136 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const THUMBNAIL_ICON_MARGIN = 2;
|
||||
|
||||
const DocInfo = new Lang.Class({
|
||||
Name: 'DocInfo',
|
||||
|
||||
_init : function(recentInfo) {
|
||||
this.recentInfo = recentInfo;
|
||||
// We actually used get_modified() instead of get_visited()
|
||||
// here, as GtkRecentInfo doesn't updated get_visited()
|
||||
// correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
|
||||
this.timestamp = recentInfo.get_modified();
|
||||
this.name = recentInfo.get_display_name();
|
||||
this._lowerName = this.name.toLowerCase();
|
||||
this.uri = recentInfo.get_uri();
|
||||
this.mimeType = recentInfo.get_mime_type();
|
||||
},
|
||||
|
||||
createIcon : function(size) {
|
||||
return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
|
||||
},
|
||||
|
||||
launch : function(workspaceIndex) {
|
||||
Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
|
||||
},
|
||||
|
||||
matchTerms: function(terms) {
|
||||
let mtype = Search.MatchType.NONE;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
let idx = this._lowerName.indexOf(term);
|
||||
if (idx == 0) {
|
||||
mtype = Search.MatchType.PREFIX;
|
||||
} else if (idx > 0) {
|
||||
if (mtype == Search.MatchType.NONE)
|
||||
mtype = Search.MatchType.SUBSTRING;
|
||||
} else {
|
||||
return Search.MatchType.NONE;
|
||||
}
|
||||
}
|
||||
return mtype;
|
||||
}
|
||||
});
|
||||
|
||||
var docManagerInstance = null;
|
||||
|
||||
function getDocManager() {
|
||||
if (docManagerInstance == null)
|
||||
docManagerInstance = new DocManager();
|
||||
return docManagerInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* DocManager wraps the DocSystem, primarily to expose DocInfo objects.
|
||||
*/
|
||||
const DocManager = new Lang.Class({
|
||||
Name: 'DocManager',
|
||||
|
||||
_init: function() {
|
||||
this._docSystem = Shell.DocSystem.get_default();
|
||||
this._infosByTimestamp = [];
|
||||
this._infosByUri = {};
|
||||
this._docSystem.connect('changed', Lang.bind(this, this._reload));
|
||||
this._reload();
|
||||
},
|
||||
|
||||
_reload: function() {
|
||||
let docs = this._docSystem.get_all();
|
||||
this._infosByTimestamp = [];
|
||||
this._infosByUri = {};
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
let recentInfo = docs[i];
|
||||
|
||||
let docInfo = new DocInfo(recentInfo);
|
||||
this._infosByTimestamp.push(docInfo);
|
||||
this._infosByUri[docInfo.uri] = docInfo;
|
||||
}
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
getTimestampOrderedInfos: function() {
|
||||
return this._infosByTimestamp;
|
||||
},
|
||||
|
||||
getInfosByUri: function() {
|
||||
return this._infosByUri;
|
||||
},
|
||||
|
||||
lookupByUri: function(uri) {
|
||||
return this._infosByUri[uri];
|
||||
},
|
||||
|
||||
queueExistenceCheck: function(count) {
|
||||
return this._docSystem.queue_existence_check(count);
|
||||
},
|
||||
|
||||
_searchDocs: function(items, terms) {
|
||||
let multiplePrefixMatches = [];
|
||||
let prefixMatches = [];
|
||||
let multipleSubtringMatches = [];
|
||||
let substringMatches = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let mtype = item.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.MULTIPLE_PREFIX)
|
||||
multiplePrefixMatches.push(item.uri);
|
||||
else if (mtype == Search.MatchType.PREFIX)
|
||||
prefixMatches.push(item.uri);
|
||||
else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
|
||||
multipleSubtringMatches.push(item.uri);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringMatches.push(item.uri);
|
||||
}
|
||||
return multiplePrefixMatches.concat(prefixMatches.concat(multipleSubtringMatches.concat(substringMatches)));
|
||||
},
|
||||
|
||||
initialSearch: function(terms) {
|
||||
return this._searchDocs(this._infosByTimestamp, terms);
|
||||
},
|
||||
|
||||
subsearch: function(previousResults, terms) {
|
||||
return this._searchDocs(previousResults.map(Lang.bind(this,
|
||||
function(url) {
|
||||
return this._infosByUri[url];
|
||||
})), terms);
|
||||
}
|
||||
});
|
||||
|
||||
Signals.addSignalMethods(DocManager.prototype);
|
185
js/misc/extensionUtils.js
Normal file
@ -0,0 +1,185 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
// Common utils for the extension system and the extension
|
||||
// preferences tool
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const ShellJS = imports.gi.ShellJS;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
|
||||
const ExtensionType = {
|
||||
SYSTEM: 1,
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensions = {};
|
||||
|
||||
function getCurrentExtension() {
|
||||
let stack = (new Error()).stack;
|
||||
|
||||
// Assuming we're importing this directly from an extension (and we shouldn't
|
||||
// ever not be), its UUID should be directly in the path here.
|
||||
let extensionStackLine = stack.split('\n')[1];
|
||||
if (!extensionStackLine)
|
||||
throw new Error('Could not find current extension');
|
||||
|
||||
// The stack line is like:
|
||||
// init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
|
||||
//
|
||||
// In the case that we're importing from
|
||||
// module scope, the first field is blank:
|
||||
// @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
|
||||
let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
|
||||
if (!match)
|
||||
throw new Error('Could not find current extension');
|
||||
|
||||
let path = match[1];
|
||||
let file = Gio.File.new_for_path(path);
|
||||
|
||||
// Walk up the directory tree, looking for an extesion with
|
||||
// the same UUID as a directory name.
|
||||
while (file != null) {
|
||||
let extension = extensions[file.get_basename()];
|
||||
if (extension !== undefined)
|
||||
return extension;
|
||||
file = file.get_parent();
|
||||
}
|
||||
|
||||
throw new Error('Could not find current extension');
|
||||
}
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
* @current: the version we have
|
||||
*
|
||||
* Check if a component is compatible for an extension.
|
||||
* @required is an array, and at least one version must match.
|
||||
* @current must be in the format <major>.<minor>.<point>.<micro>
|
||||
* <micro> is always ignored
|
||||
* <point> is ignored if <minor> is even (so you can target the
|
||||
* whole stable release)
|
||||
* <minor> and <major> must match
|
||||
* Each target version must be at least <major> and <minor>
|
||||
*/
|
||||
function versionCheck(required, current) {
|
||||
let currentArray = current.split('.');
|
||||
let major = currentArray[0];
|
||||
let minor = currentArray[1];
|
||||
let point = currentArray[2];
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
let requiredArray = required[i].split('.');
|
||||
if (requiredArray[0] == major &&
|
||||
requiredArray[1] == minor &&
|
||||
(requiredArray[2] == point ||
|
||||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isOutOfDate(extension) {
|
||||
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function createExtensionObject(uuid, dir, type) {
|
||||
let info;
|
||||
|
||||
let metadataFile = dir.get_child('metadata.json');
|
||||
if (!metadataFile.query_exists(null)) {
|
||||
throw new Error('Missing metadata.json');
|
||||
}
|
||||
|
||||
let metadataContents, success, tag;
|
||||
try {
|
||||
[success, metadataContents, tag] = metadataFile.load_contents(null);
|
||||
} catch (e) {
|
||||
throw new Error('Failed to load metadata.json: ' + e);
|
||||
}
|
||||
let meta;
|
||||
try {
|
||||
meta = JSON.parse(metadataContents);
|
||||
} catch (e) {
|
||||
throw new Error('Failed to parse metadata.json: ' + e);
|
||||
}
|
||||
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
throw new Error('missing "' + prop + '" property in metadata.json');
|
||||
}
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
}
|
||||
|
||||
let extension = {};
|
||||
|
||||
extension.metadata = meta;
|
||||
extension.uuid = meta.uuid;
|
||||
extension.type = type;
|
||||
extension.dir = dir;
|
||||
extension.path = dir.get_path();
|
||||
extension.error = '';
|
||||
extension.hasPrefs = dir.get_child('prefs.js').query_exists(null);
|
||||
|
||||
extensions[uuid] = extension;
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
var _extension = null;
|
||||
|
||||
function installImporter(extension) {
|
||||
_extension = extension;
|
||||
ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path);
|
||||
_extension = null;
|
||||
}
|
||||
|
||||
const ExtensionFinder = new Lang.Class({
|
||||
Name: 'ExtensionFinder',
|
||||
|
||||
_loadExtension: function(extensionDir, info, perUserDir) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
return;
|
||||
let uuid = info.get_name();
|
||||
let existing = extensions[uuid];
|
||||
if (existing) {
|
||||
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
|
||||
return;
|
||||
}
|
||||
|
||||
let extension;
|
||||
let type = extensionDir.has_prefix(perUserDir) ? ExtensionType.PER_USER
|
||||
: ExtensionType.SYSTEM;
|
||||
try {
|
||||
extension = createExtensionObject(uuid, extensionDir, type);
|
||||
} catch(e) {
|
||||
logError(e, 'Could not load extension %s'.format(uuid));
|
||||
return;
|
||||
}
|
||||
this.emit('extension-found', extension);
|
||||
},
|
||||
|
||||
scanExtensions: function() {
|
||||
let perUserDir = Gio.File.new_for_path(global.userdatadir);
|
||||
FileUtils.collectFromDatadirsAsync('extensions',
|
||||
{ processFile: Lang.bind(this, this._loadExtension),
|
||||
includeUserDir: true,
|
||||
data: perUserDir });
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ExtensionFinder.prototype);
|
@ -2,10 +2,12 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
function listDirAsync(file, callback) {
|
||||
let allFiles = [];
|
||||
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
file.enumerate_children_async('standard::name,standard::type',
|
||||
Gio.FileQueryInfoFlags.NONE,
|
||||
GLib.PRIORITY_LOW, null, function (obj, res) {
|
||||
let enumerator = obj.enumerate_children_finish(res);
|
||||
@ -23,12 +25,69 @@ function listDirAsync(file, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function _collectFromDirectoryAsync(dir, loadState) {
|
||||
function done() {
|
||||
loadState.numLoading--;
|
||||
if (loadState.loadedCallback &&
|
||||
loadState.numLoading == 0)
|
||||
loadState.loadedCallback(loadState.data);
|
||||
}
|
||||
|
||||
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
|
||||
GLib.PRIORITY_DEFAULT, null, function(object, res) {
|
||||
try {
|
||||
object.query_info_finish(res);
|
||||
} catch (e) {
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
||||
log(e.message);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
listDirAsync(dir, Lang.bind(this, function(infos) {
|
||||
for (let i = 0; i < infos.length; i++)
|
||||
loadState.processFile(dir.get_child(infos[i].get_name()),
|
||||
infos[i], loadState.data);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function collectFromDatadirsAsync(subdir, params) {
|
||||
params = Params.parse(params, { includeUserDir: false,
|
||||
processFile: null,
|
||||
loadedCallback: null,
|
||||
data: null });
|
||||
let loadState = { data: params.data,
|
||||
numLoading: 0,
|
||||
loadedCallback: params.loadedCallback,
|
||||
processFile: params.processFile };
|
||||
|
||||
if (params.processFile == null) {
|
||||
if (params.loadedCallback)
|
||||
params.loadedCallback(params.data);
|
||||
return;
|
||||
}
|
||||
|
||||
let dataDirs = GLib.get_system_data_dirs();
|
||||
if (params.includeUserDir)
|
||||
dataDirs.unshift(GLib.get_user_data_dir());
|
||||
loadState.numLoading = dataDirs.length;
|
||||
|
||||
for (let i = 0; i < dataDirs.length; i++) {
|
||||
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
|
||||
let dir = Gio.File.new_for_path(path);
|
||||
|
||||
_collectFromDirectoryAsync(dir, loadState);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteGFile(file) {
|
||||
// Work around 'delete' being a keyword in JS.
|
||||
return file['delete'](null);
|
||||
}
|
||||
|
||||
function recursivelyDeleteDir(dir) {
|
||||
function recursivelyDeleteDir(dir, deleteParent) {
|
||||
let children = dir.enumerate_children('standard::name,standard::type',
|
||||
Gio.FileQueryInfoFlags.NONE, null);
|
||||
|
||||
@ -39,8 +98,29 @@ function recursivelyDeleteDir(dir) {
|
||||
if (type == Gio.FileType.REGULAR)
|
||||
deleteGFile(child);
|
||||
else if (type == Gio.FileType.DIRECTORY)
|
||||
recursivelyDeleteDir(child);
|
||||
recursivelyDeleteDir(child, true);
|
||||
}
|
||||
|
||||
if (deleteParent)
|
||||
deleteGFile(dir);
|
||||
}
|
||||
|
||||
function recursivelyMoveDir(srcDir, destDir) {
|
||||
let children = srcDir.enumerate_children('standard::name,standard::type',
|
||||
Gio.FileQueryInfoFlags.NONE, null);
|
||||
|
||||
if (!destDir.query_exists(null))
|
||||
destDir.make_directory_with_parents(null);
|
||||
|
||||
let info, child;
|
||||
while ((info = children.next_file(null)) != null) {
|
||||
let type = info.get_file_type();
|
||||
let srcChild = srcDir.get_child(info.get_name());
|
||||
let destChild = destDir.get_child(info.get_name());
|
||||
log([srcChild.get_path(), destChild.get_path()]);
|
||||
if (type == Gio.FileType.REGULAR)
|
||||
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
|
||||
else if (type == Gio.FileType.DIRECTORY)
|
||||
recursivelyMoveDir(srcChild, destChild);
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
/*
|
||||
* This function is intended to extend the String object and provide
|
||||
* an String.format API for string formatting.
|
||||
* It has to be set up using String.prototype.format = Format.format;
|
||||
* Usage:
|
||||
* "somestring %s %d".format('hello', 5);
|
||||
* It supports %s, %d, %x and %f, for %f it also support precisions like
|
||||
* "%.2f".format(1.526). All specifiers can be prefixed with a minimum
|
||||
* field width, e.g. "%5s".format("foo"). Unless the width is prefixed
|
||||
* with '0', the formatted string will be padded with spaces.
|
||||
*/
|
||||
|
||||
function format() {
|
||||
let str = this;
|
||||
let i = 0;
|
||||
let args = arguments;
|
||||
|
||||
return str.replace(/%([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, widthGroup, precisionGroup, genericGroup) {
|
||||
|
||||
if (precisionGroup != '' && genericGroup != 'f')
|
||||
throw new Error("Precision can only be specified for 'f'");
|
||||
|
||||
let fillChar = (widthGroup[0] == '0') ? '0' : ' ';
|
||||
let width = parseInt(widthGroup, 10) || 0;
|
||||
|
||||
function fillWidth(s, c, w) {
|
||||
let fill = '';
|
||||
for (let i = 0; i < w; i++)
|
||||
fill += c;
|
||||
return fill.substr(s.length) + s;
|
||||
}
|
||||
|
||||
let s = '';
|
||||
switch (genericGroup) {
|
||||
case '%':
|
||||
return '%';
|
||||
break;
|
||||
case 's':
|
||||
s = args[i++].toString();
|
||||
break;
|
||||
case 'd':
|
||||
s = parseInt(args[i++]).toString();
|
||||
break;
|
||||
case 'x':
|
||||
s = parseInt(args[i++]).toString(16);
|
||||
break;
|
||||
case 'f':
|
||||
if (precisionGroup == '')
|
||||
s = parseFloat(args[i++]).toString();
|
||||
else
|
||||
s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported conversion character %' + genericGroup);
|
||||
}
|
||||
return fillWidth(s, fillChar, width);
|
||||
});
|
||||
}
|
@ -31,12 +31,12 @@ function Presence(initCallback, cancellable) {
|
||||
// change at runtime (changes always come in the form
|
||||
// of new inhibitors)
|
||||
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
|
||||
<property name="app_id" type="s" access="read" />
|
||||
<property name="client_id" type="s" access="read" />
|
||||
<property name="reason" type="s" access="read" />
|
||||
<property name="flags" type="u" access="read" />
|
||||
<property name="toplevel_xid" type="u" access="read" />
|
||||
<property name="cookie" type="u" access="read" />
|
||||
<method name="GetAppId">
|
||||
<arg type="s" direction="out" />
|
||||
</method>
|
||||
<method name="GetReason">
|
||||
<arg type="s" direction="out" />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
|
||||
@ -50,9 +50,20 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
<method name="Shutdown" />
|
||||
<method name="Reboot" />
|
||||
<method name="CanShutdown">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="IsInhibited">
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<signal name="InhibitorAdded">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
<signal name="InhibitorRemoved">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
||||
|
@ -41,24 +41,26 @@ const HistoryManager = new Lang.Class({
|
||||
this._historyIndex = this._history.length;
|
||||
},
|
||||
|
||||
prevItem: function(text) {
|
||||
_setPrevItem: function(text) {
|
||||
if (this._historyIndex <= 0)
|
||||
return text;
|
||||
return false;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex--;
|
||||
return this._indexChanged();
|
||||
this._indexChanged();
|
||||
return true;
|
||||
},
|
||||
|
||||
nextItem: function(text) {
|
||||
_setNextItem: function(text) {
|
||||
if (this._historyIndex >= this._history.length)
|
||||
return text;
|
||||
return false;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex++;
|
||||
return this._indexChanged();
|
||||
this._indexChanged();
|
||||
return true;
|
||||
},
|
||||
|
||||
lastItem: function() {
|
||||
@ -83,11 +85,9 @@ const HistoryManager = new Lang.Class({
|
||||
_onEntryKeyPress: function(entry, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Up) {
|
||||
this.prevItem(entry.get_text());
|
||||
return true;
|
||||
return this._setPrevItem(entry.get_text());
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
this.nextItem(entry.get_text());
|
||||
return true;
|
||||
return this._setNextItem(entry.get_text());
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@ -98,8 +98,6 @@ const HistoryManager = new Lang.Class({
|
||||
|
||||
if (this._entry)
|
||||
this._entry.set_text(current);
|
||||
|
||||
return current;
|
||||
},
|
||||
|
||||
_save: function() {
|
||||
|
229
js/misc/loginManager.js
Normal file
@ -0,0 +1,229 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||
<method name='PowerOff'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='Reboot'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='Suspend'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='CanPowerOff'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanReboot'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanSuspend'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
<signal name='Lock' />
|
||||
<signal name='Unlock' />
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
|
||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
|
||||
<method name='CanRestart'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='CanStop'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='Restart' />
|
||||
<method name='Stop' />
|
||||
<method name='GetCurrentSession'>
|
||||
<arg type='o' direction='out' />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
|
||||
<method name='IsActive'>
|
||||
<arg type='b' direction='out' />
|
||||
</method>
|
||||
<signal name='ActiveChanged'>
|
||||
<arg type='b' direction='out' />
|
||||
</signal>
|
||||
<signal name='Lock' />
|
||||
<signal name='Unlock' />
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||
|
||||
function haveSystemd() {
|
||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
||||
}
|
||||
|
||||
let _loginManager = null;
|
||||
|
||||
/**
|
||||
* LoginManager:
|
||||
* An abstraction over systemd/logind and ConsoleKit.
|
||||
*
|
||||
*/
|
||||
function getLoginManager() {
|
||||
if (_loginManager == null) {
|
||||
if (haveSystemd())
|
||||
_loginManager = new LoginManagerSystemd();
|
||||
else
|
||||
_loginManager = new LoginManagerConsoleKit();
|
||||
}
|
||||
|
||||
return _loginManager;
|
||||
}
|
||||
|
||||
const LoginManagerSystemd = new Lang.Class({
|
||||
Name: 'LoginManagerSystemd',
|
||||
|
||||
_init: function() {
|
||||
this._proxy = new SystemdLoginManager(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1');
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
// session objects have different interfaces - but in both cases there are
|
||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||
getCurrentSessionProxy: function() {
|
||||
if (!this._currentSession) {
|
||||
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1/session/' +
|
||||
GLib.getenv('XDG_SESSION_ID'));
|
||||
}
|
||||
|
||||
return this._currentSession;
|
||||
},
|
||||
|
||||
get sessionActive() {
|
||||
return Shell.session_is_active_for_systemd();
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
this._proxy.CanPowerOffRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
});
|
||||
},
|
||||
|
||||
canReboot: function(asyncCallback) {
|
||||
this._proxy.CanRebootRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
});
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
this._proxy.CanSuspendRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
});
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.PowerOffRemote(true);
|
||||
},
|
||||
|
||||
reboot: function() {
|
||||
this._proxy.RebootRemote(true);
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this._proxy.SuspendRemote(true);
|
||||
}
|
||||
});
|
||||
|
||||
const LoginManagerConsoleKit = new Lang.Class({
|
||||
Name: 'LoginManagerConsoleKit',
|
||||
|
||||
_init: function() {
|
||||
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
// session objects have different interfaces - but in both cases there are
|
||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||
getCurrentSessionProxy: function() {
|
||||
if (!this._currentSession) {
|
||||
let [currentSessionId] = this._proxy.GetCurrentSessionSync();
|
||||
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
currentSessionId);
|
||||
}
|
||||
|
||||
return this._currentSession;
|
||||
},
|
||||
|
||||
get sessionActive() {
|
||||
if (this._sessionActive !== undefined)
|
||||
return this._sessionActive;
|
||||
|
||||
let session = this.getCurrentSessionProxy();
|
||||
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
|
||||
this._sessionActive = isActive;
|
||||
}));
|
||||
[this._sessionActive] = session.IsActiveSync();
|
||||
|
||||
return this._sessionActive;
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
this._proxy.CanStopRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0]);
|
||||
});
|
||||
},
|
||||
|
||||
canReboot: function(asyncCallback) {
|
||||
this._proxy.CanRestartRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0]);
|
||||
});
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
asyncCallback(this._upClient.get_can_suspend());
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.StopRemote();
|
||||
},
|
||||
|
||||
reboot: function() {
|
||||
this._proxy.RestartRemote();
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
}
|
||||
});
|
@ -10,9 +10,7 @@ const Signals = imports.signals;
|
||||
|
||||
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
|
||||
<method name="GetRegistrationInfo">
|
||||
<arg type="u" direction="out" />
|
||||
<arg type="s" direction="out" />
|
||||
<arg type="s" direction="out" />
|
||||
<arg type="(uss)" direction="out" />
|
||||
</method>
|
||||
<method name="GetSignalQuality">
|
||||
<arg type="u" direction="out" />
|
||||
@ -35,9 +33,7 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
|
||||
<arg type="u" direction="out" />
|
||||
</method>
|
||||
<method name="GetServingSystem">
|
||||
<arg type="u" direction="out" />
|
||||
<arg type="s" direction="out" />
|
||||
<arg type="u" direction="out" />
|
||||
<arg type="(usu)" direction="out" />
|
||||
</method>
|
||||
<signal name="SignalQuality">
|
||||
<arg type="u" direction="out" />
|
||||
@ -50,81 +46,17 @@ let _providersTable;
|
||||
function _getProvidersTable() {
|
||||
if (_providersTable)
|
||||
return _providersTable;
|
||||
let [providers, countryCodes] = Shell.mobile_providers_parse();
|
||||
return _providersTable = providers;
|
||||
return _providersTable = Shell.mobile_providers_parse(null,null);
|
||||
}
|
||||
|
||||
const ModemGsm = new Lang.Class({
|
||||
Name: 'ModemGsm',
|
||||
|
||||
_init: function(path) {
|
||||
this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
|
||||
|
||||
this.signal_quality = 0;
|
||||
this.operator_name = null;
|
||||
|
||||
// Code is duplicated because the function have different signatures
|
||||
this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, [quality]) {
|
||||
this.signal_quality = quality;
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
log(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let [status, code, name] = result;
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
// it will return an error if the device is not connected
|
||||
this.signal_quality = 0;
|
||||
} else {
|
||||
let [quality] = result;
|
||||
this.signal_quality = quality;
|
||||
}
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
},
|
||||
|
||||
_findOperatorName: function(name, opCode) {
|
||||
if (name.length != 0 && (name.length > 6 || name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return name;
|
||||
}
|
||||
if (isNaN(parseInt(name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return name;
|
||||
}
|
||||
|
||||
let needle;
|
||||
if (name.length == 0 && opCode)
|
||||
needle = opCode;
|
||||
else if (name.length == 6 || name.length == 5)
|
||||
needle = name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
return this._findProviderForMCCMNC(needle);
|
||||
},
|
||||
|
||||
_findProviderForMCCMNC: function(needle) {
|
||||
let table = _getProvidersTable();
|
||||
function findProviderForMCCMNC(table, needle) {
|
||||
let needlemcc = needle.substring(0, 3);
|
||||
let needlemnc = needle.substring(3, needle.length);
|
||||
|
||||
let name2, name3;
|
||||
for (let iter in table) {
|
||||
let providers = table[iter];
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
@ -154,6 +86,97 @@ const ModemGsm = new Lang.Class({
|
||||
|
||||
return name3 || name2 || null;
|
||||
}
|
||||
|
||||
function findProviderForSid(table, sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
// Search through each country
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let cdma_sid = provider.get_cdma_sid();
|
||||
|
||||
// Search through CDMA SID list
|
||||
for (let j = 0; j < cdma_sid.length; j++) {
|
||||
if (cdma_sid[j] == sid)
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const ModemGsm = new Lang.Class({
|
||||
Name: 'ModemGsm',
|
||||
|
||||
_init: function(path) {
|
||||
this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
|
||||
|
||||
this.signal_quality = 0;
|
||||
this.operator_name = null;
|
||||
|
||||
// Code is duplicated because the function have different signatures
|
||||
this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, [quality]) {
|
||||
this.signal_quality = quality;
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
||||
if (err) {
|
||||
log(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let [status, code, name] = result;
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
// it will return an error if the device is not connected
|
||||
this.signal_quality = 0;
|
||||
} else {
|
||||
let [quality] = result;
|
||||
this.signal_quality = quality;
|
||||
}
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
},
|
||||
|
||||
_findOperatorName: function(name, opCode) {
|
||||
if (name) {
|
||||
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return name;
|
||||
}
|
||||
if (isNaN(parseInt(name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
let needle;
|
||||
if ((name == null || name.length == 0) && opCode)
|
||||
needle = opCode;
|
||||
else if (name.length == 6 || name.length == 5)
|
||||
needle = name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
let table = _getProvidersTable();
|
||||
return findProviderForMCCMNC(table, needle);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemGsm.prototype);
|
||||
|
||||
@ -165,7 +188,7 @@ const ModemCdma = new Lang.Class({
|
||||
|
||||
this.signal_quality = 0;
|
||||
this.operator_name = null;
|
||||
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, sender, params) {
|
||||
this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, params) {
|
||||
this.signal_quality = params[0];
|
||||
this.emit('notify::signal-quality');
|
||||
|
||||
@ -187,45 +210,20 @@ const ModemCdma = new Lang.Class({
|
||||
},
|
||||
|
||||
_refreshServingSystem: function() {
|
||||
this._proxy.GetServingSystemRemote(Lang.bind(this, function(result, err) {
|
||||
this._proxy.GetServingSystemRemote(Lang.bind(this, function([result], err) {
|
||||
if (err) {
|
||||
// it will return an error if the device is not connected
|
||||
this.operator_name = null;
|
||||
} else {
|
||||
let [bandClass, band, id] = result;
|
||||
if (name.length > 0)
|
||||
this.operator_name = this._findProviderForSid(id);
|
||||
else
|
||||
if (name.length > 0) {
|
||||
let table = _getProvidersTable();
|
||||
this.operator_name = findProviderForSid(table, id);
|
||||
} else
|
||||
this.operator_name = null;
|
||||
}
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
},
|
||||
|
||||
_findProviderForSid: function(sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
let table = _getProvidersTable();
|
||||
|
||||
// Search through each country
|
||||
for (let iter in table) {
|
||||
let providers = table[iter];
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let cdma_sid = provider.get_cdma_sid();
|
||||
|
||||
// Search through CDMA SID list
|
||||
for (let j = 0; j < cdma_sid.length; j++) {
|
||||
if (cdma_sid[j] == sid)
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemCdma.prototype);
|
||||
|
@ -1,48 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
|
||||
<method name="GetActive">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="Lock" />
|
||||
<method name="SetActive">
|
||||
<arg type="b" direction="in" />
|
||||
</method>
|
||||
<signal name="ActiveChanged">
|
||||
<arg type="b" direction="out" />
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface);
|
||||
|
||||
function ScreenSaverProxy() {
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||
g_interface_name: ScreenSaverInfo.name,
|
||||
g_interface_info: ScreenSaverInfo,
|
||||
g_name: 'org.gnome.ScreenSaver',
|
||||
g_object_path: '/org/gnome/ScreenSaver',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
self.init(null);
|
||||
self.screenSaverActive = false;
|
||||
|
||||
self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) {
|
||||
self.screenSaverActive = isActive;
|
||||
});
|
||||
self.connect('notify::g-name-owner', function() {
|
||||
if (self.g_name_owner) {
|
||||
self.GetActiveRemote(function(result, excp) {
|
||||
if (result) {
|
||||
let [isActive] = result;
|
||||
self.screenSaverActive = isActive;
|
||||
}
|
||||
});
|
||||
} else
|
||||
self.screenSaverActive = false;
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
112
js/misc/util.js
@ -1,9 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
@ -83,25 +80,34 @@ function spawnCommandLine(command_line) {
|
||||
// this will throw an error.
|
||||
function trySpawn(argv)
|
||||
{
|
||||
var success, pid;
|
||||
try {
|
||||
GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH,
|
||||
null, null);
|
||||
[success, pid] = GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
} catch (err) {
|
||||
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
|
||||
err.message = _("Command not found");
|
||||
} else {
|
||||
/* Rewrite the error in case of ENOENT */
|
||||
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
|
||||
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
|
||||
message: _("Command not found") });
|
||||
} else if (err instanceof GLib.Error) {
|
||||
// The exception from gjs contains an error string like:
|
||||
// Error invoking GLib.spawn_command_line_async: Failed to
|
||||
// execute child process "foo" (No such file or directory)
|
||||
// We are only interested in the part in the parentheses. (And
|
||||
// we can't pattern match the text, since it gets localized.)
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
}
|
||||
|
||||
let message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
throw new (err.constructor)({ code: err.code,
|
||||
message: message });
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
// Dummy child watch; we don't want to double-fork internally
|
||||
// because then we lose the parent-child relationship, which
|
||||
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
|
||||
}
|
||||
|
||||
// trySpawnCommandLine:
|
||||
// @command_line: a command line
|
||||
@ -144,7 +150,7 @@ function killall(processName) {
|
||||
// whatever...
|
||||
|
||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||
// It might be useful to return success/failure, but we'd need
|
||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||
// the current callers care, we don't bother.
|
||||
@ -153,86 +159,6 @@ function killall(processName) {
|
||||
}
|
||||
}
|
||||
|
||||
// This was ported from network-manager-applet
|
||||
// Copyright 2007 - 2011 Red Hat, Inc.
|
||||
// Author: Dan Williams <dcbw@redhat.com>
|
||||
|
||||
const _IGNORED_WORDS = [
|
||||
'Semiconductor',
|
||||
'Components',
|
||||
'Corporation',
|
||||
'Communications',
|
||||
'Company',
|
||||
'Corp.',
|
||||
'Corp',
|
||||
'Co.',
|
||||
'Inc.',
|
||||
'Inc',
|
||||
'Incorporated',
|
||||
'Ltd.',
|
||||
'Limited.',
|
||||
'Intel',
|
||||
'chipset',
|
||||
'adapter',
|
||||
'[hex]',
|
||||
'NDIS',
|
||||
'Module'
|
||||
];
|
||||
|
||||
const _IGNORED_PHRASES = [
|
||||
'Multiprotocol MAC/baseband processor',
|
||||
'Wireless LAN Controller',
|
||||
'Wireless LAN Adapter',
|
||||
'Wireless Adapter',
|
||||
'Network Connection',
|
||||
'Wireless Cardbus Adapter',
|
||||
'Wireless CardBus Adapter',
|
||||
'54 Mbps Wireless PC Card',
|
||||
'Wireless PC Card',
|
||||
'Wireless PC',
|
||||
'PC Card with XJACK(r) Antenna',
|
||||
'Wireless cardbus',
|
||||
'Wireless LAN PC Card',
|
||||
'Technology Group Ltd.',
|
||||
'Communication S.p.A.',
|
||||
'Business Mobile Networks BV',
|
||||
'Mobile Broadband Minicard Composite Device',
|
||||
'Mobile Communications AB',
|
||||
'(PC-Suite Mode)'
|
||||
];
|
||||
|
||||
function fixupPCIDescription(desc) {
|
||||
desc = desc.replace(/[_,]/, ' ');
|
||||
|
||||
/* Attempt to shorten ID by ignoring certain phrases */
|
||||
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
||||
let item = _IGNORED_PHRASES[i];
|
||||
let pos = desc.indexOf(item);
|
||||
if (pos != -1) {
|
||||
let before = desc.substring(0, pos);
|
||||
let after = desc.substring(pos + item.length, desc.length);
|
||||
desc = before + after;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attmept to shorten ID by ignoring certain individual words */
|
||||
let words = desc.split(' ');
|
||||
let out = [ ];
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
let item = words[i];
|
||||
|
||||
// skip empty items (that come out from consecutive spaces)
|
||||
if (item.length == 0)
|
||||
continue;
|
||||
|
||||
if (_IGNORED_WORDS.indexOf(item) == -1) {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
}
|
||||
|
||||
// lowerBound:
|
||||
// @array: an array or array-like object, already sorted
|
||||
// according to @cmp
|
||||
|
@ -1,5 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const System = imports.system;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Scripting = imports.ui.scripting;
|
||||
|
||||
@ -99,7 +101,7 @@ function run() {
|
||||
Main.overview.hide();
|
||||
yield Scripting.waitLeisure();
|
||||
|
||||
global.gc();
|
||||
System.gc();
|
||||
yield Scripting.sleep(1000);
|
||||
Scripting.collectStatistics();
|
||||
Scripting.scriptEvent('afterShowHide');
|
||||
@ -113,10 +115,10 @@ function run() {
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
Scripting.scriptEvent('applicationsShowStart');
|
||||
Main.overview._viewSelector.switchTab('applications');
|
||||
Main.overview._dash.showAppsButton.checked = true;
|
||||
yield Scripting.waitLeisure();
|
||||
Scripting.scriptEvent('applicationsShowDone');
|
||||
Main.overview._viewSelector.switchTab('windows');
|
||||
Main.overview._dash.showAppsButton.checked = false;
|
||||
yield Scripting.waitLeisure();
|
||||
}
|
||||
}
|
||||
|
861
js/ui/altTab.js
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GMenu = imports.gi.GMenu;
|
||||
@ -10,6 +11,7 @@ const Signals = imports.signals;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const AppFavorites = imports.ui.appFavorites;
|
||||
const DND = imports.ui.dnd;
|
||||
@ -17,10 +19,10 @@ const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Search = imports.ui.search;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MAX_APPLICATION_WORK_MILLIS = 75;
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
@ -35,6 +37,7 @@ const AlphabeticalView = new Lang.Class({
|
||||
|
||||
this._pendingAppLaterId = 0;
|
||||
this._appIcons = {}; // desktop file id
|
||||
this._allApps = [];
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
@ -45,30 +48,34 @@ const AlphabeticalView = new Lang.Class({
|
||||
style_class: 'vfade' });
|
||||
this.actor.add_actor(box);
|
||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
|
||||
// Reset scroll on mapping
|
||||
adjustment.value = 0;
|
||||
}));
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this.actor.add_action(action);
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
_onPan: function(action) {
|
||||
let [dist, dx, dy] = action.get_motion_delta(0);
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
||||
return false;
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
this._grid.removeAll();
|
||||
this._appIcons = {};
|
||||
this._allApps = [];
|
||||
},
|
||||
|
||||
_addApp: function(app) {
|
||||
addApp: function(app) {
|
||||
var id = app.get_id();
|
||||
let appIcon = new AppWellIcon(app);
|
||||
if (this._appIcons[id] !== undefined)
|
||||
return;
|
||||
|
||||
this._grid.addItem(appIcon.actor);
|
||||
let appIcon = new AppWellIcon(app);
|
||||
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
});
|
||||
this._grid.addItem(appIcon.actor, pos);
|
||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||
|
||||
this._appIcons[id] = appIcon;
|
||||
@ -119,14 +126,6 @@ const AlphabeticalView = new Lang.Class({
|
||||
icon.actor.visible = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setAppList: function(apps) {
|
||||
this._removeAll();
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
var app = apps[i];
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -146,9 +145,10 @@ const ViewByCategories = new Lang.Class({
|
||||
// (used only before the actor is mapped the first time)
|
||||
this._currentCategory = -2;
|
||||
this._categories = [];
|
||||
this._apps = null;
|
||||
|
||||
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
|
||||
this._categoryBox = new St.BoxLayout({ vertical: true,
|
||||
reactive: true,
|
||||
accessible_role: Atk.Role.LIST });
|
||||
this._categoryScroll = new St.ScrollView({ x_fill: false,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
@ -201,54 +201,61 @@ const ViewByCategories = new Lang.Class({
|
||||
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||
var entry = iter.get_entry();
|
||||
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
||||
if (!entry.get_app_info().get_nodisplay())
|
||||
if (!entry.get_app_info().get_nodisplay()) {
|
||||
this._view.addApp(app);
|
||||
appList.push(app);
|
||||
}
|
||||
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
if (!dir.get_is_nodisplay())
|
||||
this._loadCategory(iter.get_directory(), appList);
|
||||
var itemDir = iter.get_directory();
|
||||
if (!itemDir.get_is_nodisplay())
|
||||
this._loadCategory(itemDir, appList);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addCategory: function(name, index, dir, allApps) {
|
||||
_addCategory: function(name, index, dir) {
|
||||
let apps;
|
||||
|
||||
if (dir != null) {
|
||||
apps = [];
|
||||
this._loadCategory(dir, apps);
|
||||
|
||||
if (apps.length == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START,
|
||||
can_focus: true });
|
||||
can_focus: true ,
|
||||
accessible_role: Atk.Role.LIST_ITEM });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._selectCategory(index);
|
||||
}));
|
||||
|
||||
var apps;
|
||||
if (dir == null) {
|
||||
apps = allApps;
|
||||
this._allCategoryButton = button;
|
||||
} else {
|
||||
apps = [];
|
||||
this._loadCategory(dir, apps);
|
||||
this._categories.push({ apps: apps,
|
||||
name: name,
|
||||
button: button });
|
||||
}
|
||||
|
||||
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
return true;
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
this._view.removeAll();
|
||||
this._categories = [];
|
||||
this._categoryBox.destroy_children();
|
||||
this._categoryBox.destroy_all_children();
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this._removeAll();
|
||||
|
||||
var allApps = Shell.AppSystem.get_default().get_all();
|
||||
allApps.sort(function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
});
|
||||
|
||||
/* Translators: Filter to display all applications */
|
||||
this._addCategory(_("All"), -1, null, allApps);
|
||||
this._addCategory(_("All"), -1, null);
|
||||
|
||||
var tree = this._appSystem.get_tree();
|
||||
var root = tree.get_root_directory();
|
||||
@ -261,12 +268,12 @@ const ViewByCategories = new Lang.Class({
|
||||
var dir = iter.get_directory();
|
||||
if (dir.get_is_nodisplay())
|
||||
continue;
|
||||
this._addCategory(dir.get_name(), i, dir);
|
||||
|
||||
if (this._addCategory(dir.get_name(), i, dir))
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
this._view.setAppList(allApps);
|
||||
this._selectCategory(-1);
|
||||
|
||||
if (this._focusDummy) {
|
||||
@ -304,43 +311,43 @@ const AllAppDisplay = new Lang.Class({
|
||||
|
||||
const AppSearchProvider = new Lang.Class({
|
||||
Name: 'AppSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("APPLICATIONS"));
|
||||
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
this.id = 'applications';
|
||||
},
|
||||
|
||||
getResultMeta: function(app) {
|
||||
return { 'id': app,
|
||||
getResultMetas: function(apps, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
metas.push({ 'id': app,
|
||||
'name': app.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return app.create_icon_texture(size);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.initial_search(terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(previousResults, terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
|
||||
},
|
||||
|
||||
activateResult: function(app, params) {
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
activateResult: function(app) {
|
||||
let event = Clutter.get_current_event();
|
||||
let modifiers = event ? Shell.get_event_state(event) : 0;
|
||||
let modifiers = event ? event.get_state() : 0;
|
||||
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
|
||||
|
||||
if (openNewWindow)
|
||||
app.open_new_window(params.workspace);
|
||||
app.open_new_window(-1);
|
||||
else
|
||||
app.activate_full(params.workspace, params.timestamp);
|
||||
app.activate();
|
||||
},
|
||||
|
||||
dragActivateResult: function(id, params) {
|
||||
@ -360,47 +367,39 @@ const AppSearchProvider = new Lang.Class({
|
||||
|
||||
const SettingsSearchProvider = new Lang.Class({
|
||||
Name: 'SettingsSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("SETTINGS"));
|
||||
|
||||
this.appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
||||
},
|
||||
|
||||
getResultMeta: function(pref) {
|
||||
return { 'id': pref,
|
||||
getResultMetas: function(prefs, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < prefs.length; i++) {
|
||||
let pref = prefs[i];
|
||||
metas.push({ 'id': pref,
|
||||
'name': pref.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return pref.create_icon_texture(size);
|
||||
'createIcon': function() { return null; }
|
||||
});
|
||||
}
|
||||
};
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.search_settings(terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.search_settings(terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||
},
|
||||
|
||||
activateResult: function(pref, params) {
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
pref.activate_full(params.workspace, params.timestamp);
|
||||
activateResult: function(pref) {
|
||||
pref.activate();
|
||||
},
|
||||
|
||||
dragActivateResult: function(pref, params) {
|
||||
this.activateResult(pref, params);
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let app = resultMeta['id'];
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
launchSearch: function(terms) {
|
||||
// FIXME: this should be a remote search provider
|
||||
this.appInfo.launch([], global.create_app_launch_context());
|
||||
}
|
||||
});
|
||||
|
||||
@ -470,7 +469,6 @@ const AppWellIcon = new Lang.Class({
|
||||
Lang.bind(this,
|
||||
this._onStateChanged));
|
||||
this._onStateChanged();
|
||||
this.isMenuUp = false;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
@ -552,9 +550,11 @@ const AppWellIcon = new Lang.Class({
|
||||
this._menuManager.addMenu(this._menu);
|
||||
}
|
||||
|
||||
this.isMenuUp = true;
|
||||
this.emit('menu-state-changed', true);
|
||||
|
||||
this.actor.set_hover(true);
|
||||
this._menu.popup();
|
||||
this._menuManager.ignoreRelease();
|
||||
|
||||
return false;
|
||||
},
|
||||
@ -569,12 +569,12 @@ const AppWellIcon = new Lang.Class({
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
this.actor.sync_hover();
|
||||
this.isMenuUp = false;
|
||||
this.emit('menu-state-changed', false);
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
this.emit('launching');
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
let modifiers = event.get_state();
|
||||
|
||||
if (this._onActivateOverride) {
|
||||
this._onActivateOverride(event);
|
||||
@ -614,7 +614,7 @@ const AppIconMenu = new Lang.Class({
|
||||
|
||||
_init: function(source) {
|
||||
let side = St.Side.LEFT;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
side = St.Side.RIGHT;
|
||||
|
||||
this.parent(source.actor, 0.5, side);
|
||||
|
@ -84,9 +84,12 @@ const AppFavorites = new Lang.Class({
|
||||
|
||||
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||
|
||||
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
||||
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()),
|
||||
{ forFeedback: true,
|
||||
undoCallback: Lang.bind(this, function () {
|
||||
this._removeFavorite(appId);
|
||||
}));
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
addFavorite: function(appId) {
|
||||
@ -116,9 +119,11 @@ const AppFavorites = new Lang.Class({
|
||||
return;
|
||||
|
||||
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||
Lang.bind(this, function () {
|
||||
{ forFeedback: true,
|
||||
undoCallback: Lang.bind(this, function () {
|
||||
this._addFavorite(appId, pos);
|
||||
}));
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AppFavorites.prototype);
|
||||
|
@ -9,6 +9,13 @@ const Shell = imports.gi.Shell;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PopupAnimation = {
|
||||
NONE: 0,
|
||||
SLIDE: 1 << 0,
|
||||
FADE: 1 << 1,
|
||||
FULL: ~0,
|
||||
};
|
||||
|
||||
const POPUP_ANIMATION_TIME = 0.15;
|
||||
|
||||
/**
|
||||
@ -18,7 +25,10 @@ const POPUP_ANIMATION_TIME = 0.15;
|
||||
*
|
||||
* An actor which displays a triangle "arrow" pointing to a given
|
||||
* side. The .bin property is a container in which content can be
|
||||
* placed. The arrow position may be controlled via setArrowOrigin().
|
||||
* placed. The arrow position may be controlled via
|
||||
* setArrowOrigin(). The arrow side might be temporarily flipped
|
||||
* depending on the box size and source position to keep the box
|
||||
* totally inside the monitor if possible.
|
||||
*
|
||||
*/
|
||||
const BoxPointer = new Lang.Class({
|
||||
@ -26,6 +36,7 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
_init: function(arrowSide, binProperties) {
|
||||
this._arrowSide = arrowSide;
|
||||
this._userArrowSide = arrowSide;
|
||||
this._arrowOrigin = 0;
|
||||
this.actor = new St.Bin({ x_fill: true,
|
||||
y_fill: true });
|
||||
@ -45,16 +56,36 @@ const BoxPointer = new Lang.Class({
|
||||
this._xPosition = 0;
|
||||
this._yPosition = 0;
|
||||
this._sourceAlignment = 0.5;
|
||||
this._capturedEventId = 0;
|
||||
this._muteInput();
|
||||
},
|
||||
|
||||
_muteInput: function() {
|
||||
if (this._capturedEventId == 0)
|
||||
this._capturedEventId = this.actor.connect('captured-event',
|
||||
function() { return true; });
|
||||
},
|
||||
|
||||
_unmuteInput: function() {
|
||||
if (this._capturedEventId != 0) {
|
||||
this.actor.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
show: function(animate, onComplete) {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
||||
|
||||
if (animate & PopupAnimation.FADE)
|
||||
this.opacity = 0;
|
||||
else
|
||||
this.opacity = 255;
|
||||
|
||||
this.actor.show();
|
||||
|
||||
if (animate) {
|
||||
if (animate & PopupAnimation.SLIDE) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.yOffset = -rise;
|
||||
@ -75,8 +106,12 @@ const BoxPointer = new Lang.Class({
|
||||
xOffset: 0,
|
||||
yOffset: 0,
|
||||
transition: 'linear',
|
||||
onComplete: onComplete,
|
||||
time: POPUP_ANIMATION_TIME });
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._unmuteInput();
|
||||
if (onComplete)
|
||||
onComplete();
|
||||
}),
|
||||
time: animationTime });
|
||||
},
|
||||
|
||||
hide: function(animate, onComplete) {
|
||||
@ -84,8 +119,10 @@ const BoxPointer = new Lang.Class({
|
||||
let yOffset = 0;
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
let fade = (animate & PopupAnimation.FADE);
|
||||
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
||||
|
||||
if (animate) {
|
||||
if (animate & PopupAnimation.SLIDE) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
yOffset = rise;
|
||||
@ -102,13 +139,17 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
Tweener.addTween(this, { opacity: 0,
|
||||
this._muteInput();
|
||||
|
||||
Tweener.removeTweens(this);
|
||||
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
||||
xOffset: xOffset,
|
||||
yOffset: yOffset,
|
||||
transition: 'linear',
|
||||
time: POPUP_ANIMATION_TIME,
|
||||
time: animationTime,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
this.opacity = 0;
|
||||
this.xOffset = 0;
|
||||
this.yOffset = 0;
|
||||
if (onComplete)
|
||||
@ -178,8 +219,27 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped)
|
||||
if (this._sourceActor && this._sourceActor.mapped) {
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
|
||||
if (this._shouldFlip()) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this._arrowSide = St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this._arrowSide = St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this._arrowSide = St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this._arrowSide = St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_drawBorder: function(area) {
|
||||
@ -193,7 +253,6 @@ const BoxPointer = new Lang.Class({
|
||||
let halfBorder = borderWidth / 2;
|
||||
let halfBase = Math.floor(base/2);
|
||||
|
||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
||||
|
||||
let [width, height] = area.get_surface_size();
|
||||
@ -204,7 +263,6 @@ const BoxPointer = new Lang.Class({
|
||||
boxWidth -= rise;
|
||||
}
|
||||
let cr = area.get_context();
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
|
||||
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
||||
// with the arrow poking out of that
|
||||
@ -217,14 +275,51 @@ const BoxPointer = new Lang.Class({
|
||||
let [x1, y1] = [halfBorder, halfBorder];
|
||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
||||
|
||||
let skipTopLeft = false;
|
||||
let skipTopRight = false;
|
||||
let skipBottomLeft = false;
|
||||
let skipBottomRight = false;
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipTopRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.RIGHT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopRight = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.BOTTOM:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipBottomLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomLeft = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cr.moveTo(x1 + borderRadius, y1);
|
||||
if (this._arrowSide == St.Side.TOP) {
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
if (skipTopLeft) {
|
||||
cr.moveTo(x1, y2 - borderRadius);
|
||||
cr.lineTo(x1, y1 - rise);
|
||||
cr.lineTo(x1 + halfBase, y1);
|
||||
} else if (skipTopRight) {
|
||||
cr.lineTo(x2 - halfBase, y1);
|
||||
cr.lineTo(x2, y1 - rise);
|
||||
cr.lineTo(x2, y1 + borderRadius);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
@ -232,19 +327,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipTopRight) {
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
|
||||
// top-right corner
|
||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.RIGHT) {
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
if (skipTopRight) {
|
||||
cr.lineTo(x2 + rise, y1);
|
||||
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||
} else if (skipBottomRight) {
|
||||
cr.lineTo(x2, y2 - halfBase);
|
||||
cr.lineTo(x2 + rise, y2);
|
||||
cr.lineTo(x2 - borderRadius, y2);
|
||||
} else {
|
||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
@ -252,19 +348,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipBottomRight) {
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
|
||||
// bottom-right corner
|
||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||
0, Math.PI/2);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.BOTTOM) {
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
||||
if (skipBottomLeft) {
|
||||
cr.lineTo(x1 + halfBase, y2);
|
||||
cr.lineTo(x1, y2 + rise);
|
||||
cr.lineTo(x1, y2 - borderRadius);
|
||||
} else if (skipBottomRight) {
|
||||
cr.lineTo(x2, y2 + rise);
|
||||
cr.lineTo(x2 - halfBase, y2);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
@ -272,19 +369,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipBottomLeft) {
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
|
||||
// bottom-left corner
|
||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||
Math.PI/2, Math.PI);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.LEFT) {
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
if (skipTopLeft) {
|
||||
cr.lineTo(x1, y1 + halfBase);
|
||||
cr.lineTo(x1 - rise, y1);
|
||||
cr.lineTo(x1 + borderRadius, y1);
|
||||
} else if (skipBottomLeft) {
|
||||
cr.lineTo(x1 - rise, y2)
|
||||
cr.lineTo(x1 - rise, y2 - halfBase);
|
||||
} else {
|
||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
@ -292,20 +390,26 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipTopLeft) {
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
|
||||
// top-left corner
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
}
|
||||
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
cr.fillPreserve();
|
||||
|
||||
if (borderWidth > 0) {
|
||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
}
|
||||
},
|
||||
|
||||
setPosition: function(sourceActor, alignment) {
|
||||
this._arrowSide = this._userArrowSide;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
@ -322,11 +426,7 @@ const BoxPointer = new Lang.Class({
|
||||
if (!this._sourceActor)
|
||||
return;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||
},
|
||||
|
||||
_reposition: function(sourceActor, alignment) {
|
||||
@ -347,10 +447,9 @@ const BoxPointer = new Lang.Class({
|
||||
let arrowBase = themeNode.get_length('-arrow-base');
|
||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||
let halfMargin = margin / 2;
|
||||
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let gap = themeNode.get_length('-boxpointer-gap');
|
||||
let padding = themeNode.get_length('-arrow-rise');
|
||||
|
||||
let resX, resY;
|
||||
|
||||
@ -369,29 +468,66 @@ const BoxPointer = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
|
||||
// Now align and position the pointing axis, making sure
|
||||
// it fits on screen
|
||||
// Now align and position the pointing axis, making sure it fits on
|
||||
// screen. If the arrowOrigin is so close to the edge that the arrow
|
||||
// will not be isosceles, we try to compensate as follows:
|
||||
// - We skip the rounded corner and settle for a right angled arrow
|
||||
// as shown below. See _drawBorder for further details.
|
||||
// |\_____
|
||||
// |
|
||||
// |
|
||||
// - If the arrow was going to be acute angled, we move the position
|
||||
// of the box to maintain the arrow's accuracy.
|
||||
|
||||
let arrowOrigin;
|
||||
let halfBase = Math.floor(arrowBase/2);
|
||||
let halfBorder = borderWidth / 2;
|
||||
let halfMargin = margin / 2;
|
||||
let [x1, y1] = [halfBorder, halfBorder];
|
||||
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
case St.Side.BOTTOM:
|
||||
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
||||
|
||||
resX = Math.max(resX, monitor.x + 10);
|
||||
resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth));
|
||||
this.setArrowOrigin(sourceCenterX - resX);
|
||||
resX = Math.max(resX, monitor.x + padding);
|
||||
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
|
||||
|
||||
arrowOrigin = sourceCenterX - resX;
|
||||
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
|
||||
if (arrowOrigin > x1)
|
||||
resX += (arrowOrigin - x1);
|
||||
arrowOrigin = x1;
|
||||
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
|
||||
if (arrowOrigin < x2)
|
||||
resX -= (x2 - arrowOrigin);
|
||||
arrowOrigin = x2;
|
||||
}
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
case St.Side.RIGHT:
|
||||
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
||||
|
||||
resY = Math.max(resY, monitor.y + 10);
|
||||
resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight));
|
||||
resY = Math.max(resY, monitor.y + padding);
|
||||
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
|
||||
|
||||
this.setArrowOrigin(sourceCenterY - resY);
|
||||
arrowOrigin = sourceCenterY - resY;
|
||||
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
|
||||
if (arrowOrigin > y1)
|
||||
resY += (arrowOrigin - y1);
|
||||
arrowOrigin = y1;
|
||||
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
|
||||
if (arrowOrigin < y2)
|
||||
resX -= (y2 - arrowOrigin);
|
||||
arrowOrigin = y2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.setArrowOrigin(arrowOrigin);
|
||||
|
||||
let parent = this.actor.get_parent();
|
||||
let success, x, y;
|
||||
while (!success) {
|
||||
@ -425,6 +561,39 @@ const BoxPointer = new Lang.Class({
|
||||
-(this._yPosition + this._yOffset));
|
||||
},
|
||||
|
||||
_shouldFlip: function() {
|
||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||
return true;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
if (boxAllocation.y1 < monitor.y &&
|
||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||
return true;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||
return true;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (boxAllocation.x1 < monitor.x &&
|
||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
set xOffset(offset) {
|
||||
this._xOffset = offset;
|
||||
this._shiftActor();
|
||||
|
@ -11,7 +11,6 @@ const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
@ -95,15 +94,6 @@ function _getCalendarWeekForDate(date) {
|
||||
return weekNumber;
|
||||
}
|
||||
|
||||
function _getDigitWidth(actor){
|
||||
let context = actor.get_pango_context();
|
||||
let themeNode = actor.get_theme_node();
|
||||
let font = themeNode.get_font();
|
||||
let metrics = context.get_metrics(font, context.get_language());
|
||||
let width = metrics.get_approximate_digit_width();
|
||||
return width;
|
||||
}
|
||||
|
||||
function _getCalendarDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Calendar grid abbreviation for Sunday.
|
||||
@ -208,8 +198,7 @@ function CalendarServer() {
|
||||
g_interface_info: CalendarServerInfo,
|
||||
g_name: 'org.gnome.Shell.CalendarServer',
|
||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
|
||||
|
||||
self.init(null);
|
||||
return self;
|
||||
@ -271,8 +260,9 @@ const DBusEventSource = new Lang.Class({
|
||||
this._loadEvents(false);
|
||||
},
|
||||
|
||||
_onEventsReceived: function([appointments]) {
|
||||
_onEventsReceived: function(results, error) {
|
||||
let newEvents = [];
|
||||
let appointments = results ? results[0] : null;
|
||||
if (appointments != null) {
|
||||
for (let n = 0; n < appointments.length; n++) {
|
||||
let a = appointments[n];
|
||||
@ -340,25 +330,11 @@ const DBusEventSource = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(DBusEventSource.prototype);
|
||||
|
||||
// Calendar:
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
const Calendar = new Lang.Class({
|
||||
Name: 'Calendar',
|
||||
|
||||
_init: function(eventSource) {
|
||||
if (eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
|
||||
this._eventSource.connect('changed', Lang.bind(this,
|
||||
function() {
|
||||
this._update(false);
|
||||
}));
|
||||
}
|
||||
|
||||
_init: function() {
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._weekdate = NaN;
|
||||
this._digitWidth = NaN;
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
||||
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
@ -392,6 +368,24 @@ const Calendar = new Lang.Class({
|
||||
this._buildHeader ();
|
||||
},
|
||||
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._update(true);
|
||||
}
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date, forceReload) {
|
||||
if (!_sameDay(date, this._selectedDate)) {
|
||||
@ -406,15 +400,13 @@ const Calendar = new Lang.Class({
|
||||
|
||||
_buildHeader: function() {
|
||||
let offsetCols = this._useWeekdate ? 1 : 0;
|
||||
this.actor.destroy_children();
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
// Top line of the calendar '<| September 2009 |>'
|
||||
this._topBox = new St.BoxLayout();
|
||||
this.actor.add(this._topBox,
|
||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
|
||||
|
||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
||||
this._topBox.add(back);
|
||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||
@ -448,19 +440,7 @@ const Calendar = new Lang.Class({
|
||||
}
|
||||
|
||||
// All the children after this are days, and get removed when we update the calendar
|
||||
this._firstDayIndex = this.actor.get_children().length;
|
||||
},
|
||||
|
||||
_onStyleChange: function(actor, event) {
|
||||
// width of a digit in pango units
|
||||
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
|
||||
this._setWeekdateHeaderWidth();
|
||||
},
|
||||
|
||||
_setWeekdateHeaderWidth: function() {
|
||||
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
|
||||
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
|
||||
}
|
||||
this._firstDayIndex = this.actor.get_n_children();
|
||||
},
|
||||
|
||||
_onScroll : function(actor, event) {
|
||||
@ -551,6 +531,7 @@ const Calendar = new Lang.Class({
|
||||
let row = 2;
|
||||
while (true) {
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
if (!this._eventSource)
|
||||
button.reactive = false;
|
||||
@ -571,7 +552,10 @@ const Calendar = new Lang.Class({
|
||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||
if (row == 2)
|
||||
styleClass = 'calendar-day-top ' + styleClass;
|
||||
if (iter.getDay() == this._weekStart)
|
||||
|
||||
let leftMost = rtl ? iter.getDay() == (this._weekStart + 6) % 7
|
||||
: iter.getDay() == this._weekStart;
|
||||
if (leftMost)
|
||||
styleClass = 'calendar-day-left ' + styleClass;
|
||||
|
||||
if (_sameDay(now, iter))
|
||||
@ -618,16 +602,25 @@ Signals.addSignalMethods(Calendar.prototype);
|
||||
const EventsList = new Lang.Class({
|
||||
Name: 'EventsList',
|
||||
|
||||
_init: function(eventSource) {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
||||
this._date = new Date();
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
},
|
||||
|
||||
this._update();
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
}
|
||||
},
|
||||
|
||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||
@ -685,7 +678,7 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
_showOtherDay: function(day) {
|
||||
this.actor.destroy_children();
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
@ -702,7 +695,7 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
_showToday: function() {
|
||||
this.actor.destroy_children();
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
let now = new Date();
|
||||
let dayBegin = _getBeginningOfDay(now);
|
||||
@ -713,13 +706,15 @@ const EventsList = new Lang.Class({
|
||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
||||
|
||||
if (dayEnd.getDay() <= 4 + this._weekStart) {
|
||||
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
||||
|
||||
if (dayInWeek < 5) {
|
||||
/* If now is within the first 5 days we show "This week" and
|
||||
* 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 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
||||
} else {
|
||||
/* otherwise it's one of the two last days of the week ... show
|
||||
@ -727,7 +722,7 @@ const EventsList = new Lang.Class({
|
||||
* Saturday/Sunday
|
||||
*/
|
||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
||||
}
|
||||
},
|
||||
|
115
js/ui/checkBox.js
Normal file
@ -0,0 +1,115 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const CheckBoxContainer = new Lang.Class({
|
||||
Name: 'CheckBoxContainer',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.actor.connect('get-preferred-width',
|
||||
Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height',
|
||||
Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate',
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor.connect('style-changed', Lang.bind(this,
|
||||
function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
this._spacing = node.get_length('spacing');
|
||||
}));
|
||||
this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
|
||||
|
||||
this._box = new St.Bin();
|
||||
this.actor.add_actor(this._box);
|
||||
|
||||
this.label = new St.Label();
|
||||
this.label.clutter_text.set_line_wrap(true);
|
||||
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
||||
this.actor.add_actor(this.label);
|
||||
|
||||
this._spacing = 0;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
|
||||
|
||||
alloc.min_size = minWidth + this._spacing;
|
||||
alloc.natural_size = natWidth + this._spacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
/* FIXME: StBoxlayout currently does not handle
|
||||
height-for-width children correctly, so hard-code
|
||||
two lines for the label until that problem is fixed.
|
||||
|
||||
https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
|
||||
/*
|
||||
let [minBoxHeight, natBoxHeight] =
|
||||
this._box.get_preferred_height(forWidth);
|
||||
let [minLabelHeight, natLabelHeight] =
|
||||
this.label.get_preferred_height(forWidth);
|
||||
|
||||
alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
|
||||
alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
|
||||
*/
|
||||
let [minBoxHeight, natBoxHeight] =
|
||||
this._box.get_preferred_height(-1);
|
||||
let [minLabelHeight, natLabelHeight] =
|
||||
this.label.get_preferred_height(-1);
|
||||
|
||||
alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
|
||||
alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
let [minBoxWidth, natBoxWidth] =
|
||||
this._box.get_preferred_width(-1);
|
||||
let [minBoxHeight, natBoxHeight] =
|
||||
this._box.get_preferred_height(-1);
|
||||
childBox.x1 = box.x1;
|
||||
childBox.x2 = box.x1 + natBoxWidth;
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y1 + natBoxHeight;
|
||||
this._box.allocate(childBox, flags);
|
||||
|
||||
childBox.x1 = box.x1 + natBoxWidth + this._spacing;
|
||||
childBox.x2 = availWidth - childBox.x1;
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y2;
|
||||
this.label.allocate(childBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const CheckBox = new Lang.Class({
|
||||
Name: 'CheckBox',
|
||||
|
||||
_init: function(label) {
|
||||
this.actor = new St.Button({ style_class: 'check-box',
|
||||
button_mask: St.ButtonMask.ONE,
|
||||
toggle_mode: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._container = new CheckBoxContainer();
|
||||
this.actor.set_child(this._container.actor);
|
||||
|
||||
if (label)
|
||||
this.setLabel(label);
|
||||
},
|
||||
|
||||
setLabel: function(label) {
|
||||
this._container.label.set_text(label);
|
||||
},
|
||||
|
||||
getLabelActor: function() {
|
||||
return this._container.label;
|
||||
}
|
||||
});
|
65
js/ui/components/__init__.js
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ComponentManager = new Lang.Class({
|
||||
Name: 'ComponentManager',
|
||||
|
||||
_init: function() {
|
||||
this._allComponents = {};
|
||||
this._enabledComponents = [];
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let newEnabledComponents = Main.sessionMode.components;
|
||||
|
||||
newEnabledComponents.filter(Lang.bind(this, function(name) {
|
||||
return this._enabledComponents.indexOf(name) == -1;
|
||||
})).forEach(Lang.bind(this, function(name) {
|
||||
this._enableComponent(name);
|
||||
}));
|
||||
|
||||
this._enabledComponents.filter(Lang.bind(this, function(name) {
|
||||
return newEnabledComponents.indexOf(name) == -1;
|
||||
})).forEach(Lang.bind(this, function(name) {
|
||||
this._disableComponent(name);
|
||||
}));
|
||||
|
||||
this._enabledComponents = newEnabledComponents;
|
||||
},
|
||||
|
||||
_importComponent: function(name) {
|
||||
let module = imports.ui.components[name];
|
||||
return module.Component;
|
||||
},
|
||||
|
||||
_ensureComponent: function(name) {
|
||||
let component = this._allComponents[name];
|
||||
if (component)
|
||||
return component;
|
||||
|
||||
if (Main.sessionMode.isLocked)
|
||||
return null;
|
||||
|
||||
let constructor = this._importComponent(name);
|
||||
component = new constructor();
|
||||
this._allComponents[name] = component;
|
||||
return component;
|
||||
},
|
||||
|
||||
_enableComponent: function(name) {
|
||||
let component = this._ensureComponent(name);
|
||||
if (component)
|
||||
component.enable();
|
||||
},
|
||||
|
||||
_disableComponent: function(name) {
|
||||
let component = this._allComponents[name];
|
||||
if (component == null)
|
||||
return;
|
||||
component.disable();
|
||||
}
|
||||
});
|
@ -2,12 +2,17 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Params = imports.misc.params;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
|
||||
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
||||
|
||||
// GSettings keys
|
||||
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
||||
@ -15,117 +20,72 @@ const SETTING_ENABLE_AUTOMOUNT = 'automount';
|
||||
|
||||
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
|
||||
|
||||
const ConsoleKitSessionIface = <interface name="org.freedesktop.ConsoleKit.Session">
|
||||
<method name="IsActive">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<signal name="ActiveChanged">
|
||||
<arg type="b" direction="out" />
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitSessionProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
||||
|
||||
const ConsoleKitManagerIface = <interface name="org.freedesktop.ConsoleKit.Manager">
|
||||
<method name="GetCurrentSession">
|
||||
<arg type="o" direction="out" />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ConsoleKitManagerIface);
|
||||
|
||||
function ConsoleKitManager() {
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
||||
g_interface_name: ConsoleKitManagerInfo.name,
|
||||
g_interface_info: ConsoleKitManagerInfo,
|
||||
g_name: 'org.freedesktop.ConsoleKit',
|
||||
g_object_path: '/org/freedesktop/ConsoleKit/Manager',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
|
||||
self.connect('notify::g-name-owner', function() {
|
||||
if (self.g_name_owner) {
|
||||
self.GetCurrentSessionRemote(function([session]) {
|
||||
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
|
||||
|
||||
self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) {
|
||||
self.sessionActive = isActive;
|
||||
});
|
||||
self._ckSession.IsActiveRemote(function([isActive]) {
|
||||
self.sessionActive = isActive;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
self.sessionActive = true;
|
||||
}
|
||||
});
|
||||
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
||||
const AutomountManager = new Lang.Class({
|
||||
Name: 'AutomountManager',
|
||||
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
this._volumeQueue = [];
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._session.connectSignal('InhibitorAdded',
|
||||
Lang.bind(this, this._InhibitorsChanged));
|
||||
this._session.connectSignal('InhibitorRemoved',
|
||||
Lang.bind(this, this._InhibitorsChanged));
|
||||
this._inhibited = false;
|
||||
|
||||
this.ckListener = new ConsoleKitManager();
|
||||
|
||||
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._ssProxy.connectSignal('ActiveChanged',
|
||||
Lang.bind(this, this._screenSaverActiveChanged));
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
this._volumeMonitor.connect('volume-added',
|
||||
Lang.bind(this,
|
||||
this._onVolumeAdded));
|
||||
this._volumeMonitor.connect('volume-removed',
|
||||
Lang.bind(this,
|
||||
this._onVolumeRemoved));
|
||||
this._volumeMonitor.connect('drive-connected',
|
||||
Lang.bind(this,
|
||||
this._onDriveConnected));
|
||||
this._volumeMonitor.connect('drive-disconnected',
|
||||
Lang.bind(this,
|
||||
this._onDriveDisconnected));
|
||||
this._volumeMonitor.connect('drive-eject-button',
|
||||
Lang.bind(this,
|
||||
this._onDriveEjectButton));
|
||||
|
||||
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||
},
|
||||
|
||||
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
|
||||
if (!isActive) {
|
||||
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
|
||||
this._checkAndMountVolume(volume);
|
||||
}));
|
||||
}
|
||||
enable: function() {
|
||||
this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded));
|
||||
this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved));
|
||||
this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected));
|
||||
this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected));
|
||||
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
|
||||
|
||||
// clear the queue anyway
|
||||
this._volumeQueue = [];
|
||||
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._volumeMonitor.disconnect(this._volumeAddedId);
|
||||
this._volumeMonitor.disconnect(this._volumeRemovedId);
|
||||
this._volumeMonitor.disconnect(this._driveConnectedId);
|
||||
this._volumeMonitor.disconnect(this._driveDisconnectedId);
|
||||
this._volumeMonitor.disconnect(this._driveEjectButtonId);
|
||||
|
||||
if (this._mountAllId > 0) {
|
||||
Mainloop.source_remove(this._mountAllId);
|
||||
this._mountAllId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
|
||||
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT,
|
||||
Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._inhibited = result[0];
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_startupMountAll: function() {
|
||||
let volumes = this._volumeMonitor.get_volumes();
|
||||
volumes.forEach(Lang.bind(this, function(volume) {
|
||||
this._checkAndMountVolume(volume, { checkSession: false,
|
||||
useMountOp: false });
|
||||
useMountOp: false,
|
||||
allowAutorun: false });
|
||||
}));
|
||||
|
||||
this._mountAllId = 0;
|
||||
return false;
|
||||
},
|
||||
|
||||
_onDriveConnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this.ckListener.sessionActive)
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-added-media');
|
||||
@ -134,10 +94,7 @@ const AutomountManager = new Lang.Class({
|
||||
_onDriveDisconnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this.ckListener.sessionActive)
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-removed-media');
|
||||
@ -146,7 +103,7 @@ const AutomountManager = new Lang.Class({
|
||||
_onDriveEjectButton: function(monitor, drive) {
|
||||
// TODO: this code path is not tested, as the GVfs volume monitor
|
||||
// doesn't emit this signal just yet.
|
||||
if (!this.ckListener.sessionActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
// we force stop/eject in this case, so we don't have to pass a
|
||||
@ -180,21 +137,18 @@ const AutomountManager = new Lang.Class({
|
||||
|
||||
_checkAndMountVolume: function(volume, params) {
|
||||
params = Params.parse(params, { checkSession: true,
|
||||
useMountOp: true });
|
||||
useMountOp: true,
|
||||
allowAutorun: true });
|
||||
|
||||
if (params.checkSession) {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// don't attempt automount
|
||||
if (!this.ckListener.sessionActive)
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive) {
|
||||
if (this._volumeQueue.indexOf(volume) == -1)
|
||||
this._volumeQueue.push(volume);
|
||||
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inhibited)
|
||||
return;
|
||||
|
||||
// Volume is already mounted, don't bother.
|
||||
if (volume.get_mount())
|
||||
@ -215,15 +169,20 @@ const AutomountManager = new Lang.Class({
|
||||
|
||||
if (params.useMountOp) {
|
||||
let operation = new ShellMountOperation.ShellMountOperation(volume);
|
||||
this._mountVolume(volume, operation.mountOp);
|
||||
this._mountVolume(volume, operation, params.allowAutorun);
|
||||
} else {
|
||||
this._mountVolume(volume, null);
|
||||
this._mountVolume(volume, null, params.allowAutorun);
|
||||
}
|
||||
},
|
||||
|
||||
_mountVolume: function(volume, operation) {
|
||||
_mountVolume: function(volume, operation, allowAutorun) {
|
||||
if (allowAutorun)
|
||||
this._allowAutorun(volume);
|
||||
volume.mount(0, operation, null,
|
||||
|
||||
let mountOp = operation ? operation.mountOp : null;
|
||||
volume._operation = operation;
|
||||
|
||||
volume.mount(0, mountOp, null,
|
||||
Lang.bind(this, this._onVolumeMounted));
|
||||
},
|
||||
|
||||
@ -232,15 +191,19 @@ const AutomountManager = new Lang.Class({
|
||||
|
||||
try {
|
||||
volume.mount_finish(res);
|
||||
this._closeOperation(volume);
|
||||
} catch (e) {
|
||||
let string = e.toString();
|
||||
|
||||
// FIXME: needs proper error code handling instead of this
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
if (string.indexOf('No key available with this passphrase') != -1)
|
||||
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
|
||||
// backend in this case, see
|
||||
// https://bugs.freedesktop.org/show_bug.cgi?id=51271
|
||||
if (e.message.indexOf('No key available with this passphrase') != -1) {
|
||||
this._reaskPassword(volume);
|
||||
else
|
||||
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
|
||||
} else {
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
|
||||
|
||||
this._closeOperation(volume);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -252,8 +215,16 @@ const AutomountManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_reaskPassword: function(volume) {
|
||||
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
|
||||
this._mountVolume(volume, operation.mountOp);
|
||||
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null;
|
||||
let operation =
|
||||
new ShellMountOperation.ShellMountOperation(volume,
|
||||
{ existingDialog: existingDialog });
|
||||
this._mountVolume(volume, operation);
|
||||
},
|
||||
|
||||
_closeOperation: function(volume) {
|
||||
if (volume._operation)
|
||||
volume._operation.close();
|
||||
},
|
||||
|
||||
_allowAutorun: function(volume) {
|
||||
@ -267,3 +238,4 @@ const AutomountManager = new Lang.Class({
|
||||
});
|
||||
}
|
||||
});
|
||||
const Component = AutomountManager;
|
@ -4,6 +4,7 @@ const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
@ -23,12 +24,14 @@ const AutorunSetting = {
|
||||
};
|
||||
|
||||
// misc utils
|
||||
function ignoreAutorunForMount(mount) {
|
||||
function shouldAutorunMount(mount, forTransient) {
|
||||
let root = mount.get_root();
|
||||
let volume = mount.get_volume();
|
||||
|
||||
if ((root.is_native() && !isMountRootHidden(root)) ||
|
||||
(volume && volume.allowAutorun && volume.should_automount()))
|
||||
if (!volume || (!volume.allowAutorun && forTransient))
|
||||
return false;
|
||||
|
||||
if (!root.is_native() || isMountRootHidden(root))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -41,6 +44,17 @@ function isMountRootHidden(root) {
|
||||
return (path.indexOf('/.') != -1);
|
||||
}
|
||||
|
||||
function isMountNonLocal(mount) {
|
||||
// If the mount doesn't have an associated volume, that means it's
|
||||
// an uninteresting filesystem. Most devices that we care about will
|
||||
// have a mount, like media players and USB sticks.
|
||||
let volume = mount.get_volume();
|
||||
if (volume == null)
|
||||
return true;
|
||||
|
||||
return (volume.get_identifier("class") == "network");
|
||||
}
|
||||
|
||||
function startAppForMount(app, mount) {
|
||||
let files = [];
|
||||
let root = mount.get_root();
|
||||
@ -80,13 +94,21 @@ const ContentTypeDiscoverer = new Lang.Class({
|
||||
|
||||
_init: function(callback) {
|
||||
this._callback = callback;
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
},
|
||||
|
||||
guessContentTypes: function(mount) {
|
||||
let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
|
||||
let shouldScan = autorunEnabled && !isMountNonLocal(mount);
|
||||
|
||||
if (shouldScan) {
|
||||
// guess mount's content types using GIO
|
||||
mount.guess_content_type(false, null,
|
||||
Lang.bind(this,
|
||||
this._onContentTypeGuessed));
|
||||
} else {
|
||||
this._emitCallback(mount, []);
|
||||
}
|
||||
},
|
||||
|
||||
_onContentTypeGuessed: function(mount, res) {
|
||||
@ -140,54 +162,68 @@ const AutorunManager = new Lang.Class({
|
||||
Name: 'AutorunManager',
|
||||
|
||||
_init: function() {
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
this._volumeMonitor.connect('mount-added',
|
||||
Lang.bind(this,
|
||||
this._onMountAdded));
|
||||
this._volumeMonitor.connect('mount-removed',
|
||||
Lang.bind(this,
|
||||
this._onMountRemoved));
|
||||
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||
},
|
||||
|
||||
this._transDispatcher = new AutorunTransientDispatcher();
|
||||
this._createResidentSource();
|
||||
_ensureResidentSource: function() {
|
||||
if (this._residentSource)
|
||||
return;
|
||||
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
|
||||
mounts.forEach(Lang.bind(this, function (mount) {
|
||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||
function (mount, apps) {
|
||||
this._residentSource.addMount(mount, apps);
|
||||
}));
|
||||
|
||||
discoverer.guessContentTypes(mount);
|
||||
this._residentSource = new AutorunResidentSource(this);
|
||||
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
|
||||
this._residentSource.disconnect(destroyId);
|
||||
this._residentSource = null;
|
||||
}));
|
||||
},
|
||||
|
||||
_createResidentSource: function() {
|
||||
this._residentSource = new AutorunResidentSource();
|
||||
this._residentSource.connect('destroy',
|
||||
Lang.bind(this,
|
||||
this._createResidentSource));
|
||||
enable: function() {
|
||||
this._scanMounts();
|
||||
|
||||
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
|
||||
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
if (this._residentSource)
|
||||
this._residentSource.destroy();
|
||||
this._volumeMonitor.disconnect(this._mountAddedId);
|
||||
this._volumeMonitor.disconnect(this._mountRemovedId);
|
||||
},
|
||||
|
||||
_processMount: function(mount, hotplug) {
|
||||
let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
|
||||
this._ensureResidentSource();
|
||||
this._residentSource.addMount(mount, apps);
|
||||
|
||||
if (hotplug)
|
||||
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||
}));
|
||||
discoverer.guessContentTypes(mount);
|
||||
},
|
||||
|
||||
_scanMounts: function() {
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
mounts.forEach(Lang.bind(this, function(mount) {
|
||||
this._processMount(mount, false);
|
||||
}));
|
||||
},
|
||||
|
||||
_onMountAdded: function(monitor, mount) {
|
||||
// don't do anything if our session is not the currently
|
||||
// active one
|
||||
if (!Main.automountManager.ckListener.sessionActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||
function (mount, apps, contentTypes) {
|
||||
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||
this._residentSource.addMount(mount, apps);
|
||||
}));
|
||||
|
||||
discoverer.guessContentTypes(mount);
|
||||
this._processMount(mount, true);
|
||||
},
|
||||
|
||||
_onMountRemoved: function(monitor, mount) {
|
||||
this._transDispatcher.removeMount(mount);
|
||||
if (this._residentSource)
|
||||
this._residentSource.removeMount(mount);
|
||||
},
|
||||
|
||||
@ -224,9 +260,7 @@ const AutorunManager = new Lang.Class({
|
||||
try {
|
||||
mount.unmount_with_operation_finish(res);
|
||||
} catch (e) {
|
||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||
// but we can't access the error code from JS.
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to eject the mount ' + mount.get_name()
|
||||
+ ': ' + e.toString());
|
||||
}
|
||||
@ -236,9 +270,7 @@ const AutorunManager = new Lang.Class({
|
||||
try {
|
||||
source.eject_with_operation_finish(res);
|
||||
} catch (e) {
|
||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||
// but we can't access the error code from JS.
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to eject the drive ' + source.get_name()
|
||||
+ ': ' + e.toString());
|
||||
}
|
||||
@ -248,9 +280,7 @@ const AutorunManager = new Lang.Class({
|
||||
try {
|
||||
drive.stop_finish(res);
|
||||
} catch (e) {
|
||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||
// but we can't access the error code from JS.
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to stop the drive ' + drive.get_name()
|
||||
+ ': ' + e.toString());
|
||||
}
|
||||
@ -261,17 +291,22 @@ const AutorunResidentSource = new Lang.Class({
|
||||
Name: 'AutorunResidentSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Removable Devices"));
|
||||
_init: function(manager) {
|
||||
this.parent(_("Removable Devices"), 'media-removable');
|
||||
this.showInLockScreen = false;
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
this._notification = new AutorunResidentNotification(this);
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this._manager = manager;
|
||||
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
addMount: function(mount, apps) {
|
||||
if (ignoreAutorunForMount(mount))
|
||||
if (!shouldAutorunMount(mount, false))
|
||||
return;
|
||||
|
||||
let filtered = this._mounts.filter(function (element) {
|
||||
@ -310,12 +345,6 @@ const AutorunResidentSource = new Lang.Class({
|
||||
Main.messageTray.add(this);
|
||||
this.pushNotification(this._notification);
|
||||
}
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon ({ icon_name: 'media-removable',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
});
|
||||
|
||||
@ -323,7 +352,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
Name: 'AutorunResidentNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
_init: function(manager, source) {
|
||||
this.parent(source, source.title, null, { customContent: true });
|
||||
|
||||
// set the notification as resident
|
||||
@ -331,6 +360,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
|
||||
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
||||
vertical: true });
|
||||
this._manager = manager;
|
||||
|
||||
this.addActor(this._layout,
|
||||
{ x_expand: true,
|
||||
@ -339,7 +369,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
|
||||
updateForMounts: function(mounts) {
|
||||
// remove all the layout content
|
||||
this._layout.destroy_children();
|
||||
this._layout.destroy_all_children();
|
||||
|
||||
for (let idx = 0; idx < mounts.length; idx++) {
|
||||
let element = mounts[idx];
|
||||
@ -378,7 +408,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
expand: true });
|
||||
|
||||
let ejectIcon =
|
||||
new St.Icon({ icon_name: 'media-eject',
|
||||
new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||
style_class: 'hotplug-resident-eject-icon' });
|
||||
|
||||
let ejectButton =
|
||||
@ -393,7 +423,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
}));
|
||||
|
||||
ejectButton.connect('clicked', Lang.bind(this, function() {
|
||||
Main.autorunManager.ejectMount(mount);
|
||||
this._manager.ejectMount(mount);
|
||||
}));
|
||||
|
||||
return item;
|
||||
@ -403,7 +433,8 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
const AutorunTransientDispatcher = new Lang.Class({
|
||||
Name: 'AutorunTransientDispatcher',
|
||||
|
||||
_init: function() {
|
||||
_init: function(manager) {
|
||||
this._manager = manager;
|
||||
this._sources = [];
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
},
|
||||
@ -446,7 +477,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
||||
return;
|
||||
|
||||
// add a new source
|
||||
this._sources.push(new AutorunTransientSource(mount, apps));
|
||||
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
|
||||
},
|
||||
|
||||
addMount: function(mount, apps, contentTypes) {
|
||||
@ -455,7 +486,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
||||
return;
|
||||
|
||||
// if the mount doesn't want to be autorun, return
|
||||
if (ignoreAutorunForMount(mount))
|
||||
if (!shouldAutorunMount(mount, true))
|
||||
return;
|
||||
|
||||
let setting = this._getAutorunSettingForType(contentTypes[0]);
|
||||
@ -499,23 +530,22 @@ const AutorunTransientSource = new Lang.Class({
|
||||
Name: 'AutorunTransientSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(mount, apps) {
|
||||
this.parent(mount.get_name());
|
||||
|
||||
_init: function(manager, mount, apps) {
|
||||
this._manager = manager;
|
||||
this.mount = mount;
|
||||
this.apps = apps;
|
||||
|
||||
this._notification = new AutorunTransientNotification(this);
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this.parent(mount.get_name());
|
||||
|
||||
this._notification = new AutorunTransientNotification(this._manager, this);
|
||||
|
||||
// add ourselves as a source, and popup the notification
|
||||
Main.messageTray.add(this);
|
||||
this.notify(this._notification);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ gicon: this.mount.get_icon(),
|
||||
icon_size: this.ICON_SIZE });
|
||||
getIcon: function() {
|
||||
return this.mount.get_icon();
|
||||
}
|
||||
});
|
||||
|
||||
@ -523,9 +553,10 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
Name: 'AutorunTransientNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
_init: function(manager, source) {
|
||||
this.parent(source, source.title, null, { customContent: true });
|
||||
|
||||
this._manager = manager;
|
||||
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
||||
vertical: true });
|
||||
this.addActor(this._box);
|
||||
@ -577,7 +608,7 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
|
||||
_buttonForEject: function() {
|
||||
let box = new St.BoxLayout();
|
||||
let icon = new St.Icon({ icon_name: 'media-eject',
|
||||
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||
style_class: 'hotplug-notification-item-icon' });
|
||||
box.add(icon);
|
||||
|
||||
@ -594,10 +625,11 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
style_class: 'hotplug-notification-item' });
|
||||
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
Main.autorunManager.ejectMount(this._mount);
|
||||
this._manager.ejectMount(this._mount);
|
||||
}));
|
||||
|
||||
return button;
|
||||
}
|
||||
});
|
||||
|
||||
const Component = AutorunManager;
|
227
js/ui/components/keyring.js
Normal file
@ -0,0 +1,227 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gcr = imports.gi.Gcr;
|
||||
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
|
||||
let prompter = null;
|
||||
|
||||
const KeyringDialog = new Lang.Class({
|
||||
Name: 'KeyringDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ styleClass: 'prompt-dialog' });
|
||||
|
||||
this.prompt = new Shell.KeyringPrompt();
|
||||
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
||||
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
||||
this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox);
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
|
||||
mainContentBox.add(icon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
||||
vertical: true });
|
||||
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' });
|
||||
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._messageBox.add(subject,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let description = new St.Label({ style_class: 'prompt-dialog-description' });
|
||||
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
description.clutter_text.line_wrap = true;
|
||||
this.prompt.bind_property('description', description, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
this._messageBox.add(description,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._controlTable = null;
|
||||
|
||||
let buttons = [{ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true
|
||||
}]
|
||||
|
||||
this.setButtons(buttons);
|
||||
this._cancelButton = buttons[0].button;
|
||||
this._continueButton = buttons[1].button;
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
_buildControlTable: function() {
|
||||
let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
|
||||
let row = 0;
|
||||
|
||||
if (this.prompt.password_visible) {
|
||||
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||
label.set_text(_("Password:"));
|
||||
table.add(label, { row: row, col: 0,
|
||||
x_expand: false, x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: '',
|
||||
can_focus: true});
|
||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
|
||||
table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
|
||||
row++;
|
||||
} else {
|
||||
this._passwordEntry = null;
|
||||
}
|
||||
|
||||
if (this.prompt.confirm_visible) {
|
||||
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||
label.set_text(_("Type again:"));
|
||||
table.add(label, { row: row, col: 0,
|
||||
x_expand: false, x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: '',
|
||||
can_focus: true});
|
||||
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
|
||||
this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
|
||||
table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
|
||||
row++;
|
||||
} else {
|
||||
this._confirmEntry = null;
|
||||
}
|
||||
|
||||
this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null);
|
||||
this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null);
|
||||
|
||||
if (this.prompt.choice_visible) {
|
||||
let choice = new CheckBox.CheckBox();
|
||||
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
|
||||
table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
|
||||
row++;
|
||||
}
|
||||
|
||||
let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
warning.clutter_text.line_wrap = true;
|
||||
table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
|
||||
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
if (this._controlTable) {
|
||||
this._controlTable.destroy_all_children();
|
||||
this._controlTable.destroy();
|
||||
}
|
||||
|
||||
this._controlTable = table;
|
||||
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
||||
},
|
||||
|
||||
_ensureOpen: function() {
|
||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||
// already open - it just returns true without side-effects
|
||||
if (this.open())
|
||||
return true;
|
||||
|
||||
// The above fail if e.g. unable to get input grab
|
||||
//
|
||||
// In an ideal world this wouldn't happen (because the
|
||||
// Shell is in complete control of the session) but that's
|
||||
// just not how things work right now.
|
||||
|
||||
log('keyringPrompt: Failed to show modal dialog.' +
|
||||
' Dismissing prompt request');
|
||||
this.prompt.cancel()
|
||||
return false;
|
||||
},
|
||||
|
||||
_onShowPassword: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._passwordEntry.grab_key_focus();
|
||||
},
|
||||
|
||||
_onShowConfirm: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._continueButton.grab_key_focus();
|
||||
},
|
||||
|
||||
_onHidePrompt: function(prompt) {
|
||||
this.close();
|
||||
},
|
||||
|
||||
_onPasswordActivate: function() {
|
||||
if (this.prompt.confirm_visible)
|
||||
this._confirmEntry.grab_key_focus();
|
||||
else
|
||||
this._onContinueButton();
|
||||
},
|
||||
|
||||
_onConfirmActivate: function() {
|
||||
this._onContinueButton();
|
||||
},
|
||||
|
||||
_onContinueButton: function() {
|
||||
this.prompt.complete();
|
||||
},
|
||||
|
||||
_onCancelButton: function() {
|
||||
this.prompt.cancel();
|
||||
},
|
||||
});
|
||||
|
||||
const KeyringPrompter = new Lang.Class({
|
||||
Name: 'KeyringPrompter',
|
||||
|
||||
_init: function() {
|
||||
this._prompter = new Gcr.SystemPrompter();
|
||||
this._prompter.connect('new-prompt', function(prompter) {
|
||||
let dialog = new KeyringDialog();
|
||||
return dialog.prompt;
|
||||
});
|
||||
this._dbusId = null;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._prompter.register(Gio.DBus.session);
|
||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._prompter.unregister(false);
|
||||
Gio.DBus.session.unown_name(this._dbusId);
|
||||
}
|
||||
});
|
||||
|
||||
const Component = KeyringPrompter;
|
@ -1,26 +1,9 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
@ -28,16 +11,19 @@ const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
|
||||
const VPN_UI_GROUP = 'VPN Plugin UI';
|
||||
|
||||
const NetworkSecretDialog = new Lang.Class({
|
||||
Name: 'NetworkSecretDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(agent, requestId, connection, settingName, hints) {
|
||||
this.parent({ styleClass: 'polkit-dialog' });
|
||||
_init: function(agent, requestId, connection, settingName, hints, contentOverride) {
|
||||
this.parent({ styleClass: 'prompt-dialog' });
|
||||
|
||||
this._agent = agent;
|
||||
this._requestId = requestId;
|
||||
@ -45,9 +31,12 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
this._settingName = settingName;
|
||||
this._hints = hints;
|
||||
|
||||
if (contentOverride)
|
||||
this._content = contentOverride;
|
||||
else
|
||||
this._content = this._getContent();
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
{ x_fill: true,
|
||||
@ -60,19 +49,19 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(messageBox,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
let subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||
let subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
|
||||
text: this._content.title });
|
||||
messageBox.add(subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
if (this._content.message != null) {
|
||||
let descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||
let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||
text: this._content.message,
|
||||
// HACK: for reasons unknown to me, the label
|
||||
// is not asked the correct height for width,
|
||||
@ -93,12 +82,12 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
let pos = 0;
|
||||
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||
let secret = this._content.secrets[i];
|
||||
let label = new St.Label({ style_class: 'polkit-dialog-password-label',
|
||||
let label = new St.Label({ style_class: 'prompt-dialog-password-label',
|
||||
text: secret.label });
|
||||
|
||||
let reactive = secret.key != null;
|
||||
|
||||
secret.entry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||
secret.entry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: secret.value, can_focus: reactive,
|
||||
reactive: reactive });
|
||||
ShellEntry.addContextMenu(secret.entry,
|
||||
@ -127,7 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
} else
|
||||
secret.valid = true;
|
||||
|
||||
secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START });
|
||||
secretTable.add(label, { row: pos, col: 0,
|
||||
x_expand: false, x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
||||
pos++;
|
||||
|
||||
@ -139,7 +131,7 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
|
||||
this._okButton = { label: _("Connect"),
|
||||
action: Lang.bind(this, this._onOk),
|
||||
key: Clutter.KEY_Return,
|
||||
default: true
|
||||
};
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
@ -158,10 +150,6 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
|
||||
this._okButton.button.reactive = valid;
|
||||
this._okButton.button.can_focus = valid;
|
||||
if (valid)
|
||||
this._okButton.button.remove_style_pseudo_class('disabled');
|
||||
else
|
||||
this._okButton.button.add_style_pseudo_class('disabled');
|
||||
},
|
||||
|
||||
_onOk: function() {
|
||||
@ -174,14 +162,14 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
this._agent.respond(this._requestId, false);
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.CONFIRMED);
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
// do nothing if not valid
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._agent.respond(this._requestId, true);
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
this.close(global.get_current_time());
|
||||
},
|
||||
|
||||
@ -357,19 +345,284 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const VPNRequestHandler = new Lang.Class({
|
||||
Name: 'VPNRequestHandler',
|
||||
|
||||
_init: function(agent, requestId, authHelper, serviceType, connection, hints, flags) {
|
||||
this._agent = agent;
|
||||
this._requestId = requestId;
|
||||
this._connection = connection;
|
||||
this._pluginOutBuffer = [];
|
||||
this._title = null;
|
||||
this._description = null;
|
||||
this._content = [ ];
|
||||
this._shellDialog = null;
|
||||
|
||||
let connectionSetting = connection.get_setting_connection();
|
||||
|
||||
let argv = [ authHelper.fileName,
|
||||
'-u', connectionSetting.uuid,
|
||||
'-n', connectionSetting.id,
|
||||
'-s', serviceType
|
||||
];
|
||||
if (authHelper.externalUIMode)
|
||||
argv.push('--external-ui-mode');
|
||||
if (flags & NMClient.SecretAgentGetSecretsFlags.ALLOW_INTERACTION)
|
||||
argv.push('-i');
|
||||
if (flags & NMClient.SecretAgentGetSecretsFlags.REQUEST_NEW)
|
||||
argv.push('-r');
|
||||
|
||||
this._newStylePlugin = authHelper.externalUIMode;
|
||||
|
||||
try {
|
||||
let [success, pid, stdin, stdout, stderr] =
|
||||
GLib.spawn_async_with_pipes(null, /* pwd */
|
||||
argv,
|
||||
null, /* envp */
|
||||
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null /* child_setup */);
|
||||
|
||||
this._childPid = pid;
|
||||
this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
|
||||
this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
|
||||
// We need this one too, even if don't actually care of what the process
|
||||
// has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
|
||||
// is kept open indefinitely
|
||||
let stderrStream = new Gio.UnixInputStream({ fd: stderr, close_fd: true });
|
||||
stderrStream.close(null);
|
||||
this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
|
||||
|
||||
if (this._newStylePlugin)
|
||||
this._readStdoutNewStyle();
|
||||
else
|
||||
this._readStdoutOldStyle();
|
||||
|
||||
this._childWatch = GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid,
|
||||
Lang.bind(this, this._vpnChildFinished));
|
||||
|
||||
this._writeConnection();
|
||||
} catch(e) {
|
||||
logError(e, 'error while spawning VPN auth helper');
|
||||
|
||||
this._agent.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function(respond) {
|
||||
if (respond)
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
|
||||
if (this._newStylePlugin && this._shellDialog) {
|
||||
this._shellDialog.close(global.get_current_time());
|
||||
this._shellDialog.destroy();
|
||||
} else {
|
||||
try {
|
||||
this._stdin.write('QUIT\n\n', null);
|
||||
} catch(e) { /* ignore broken pipe errors */ }
|
||||
}
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._destroyed)
|
||||
return;
|
||||
|
||||
GLib.source_remove(this._childWatch);
|
||||
|
||||
this._stdin.close(null);
|
||||
// Stdout is closed when we finish reading from it
|
||||
|
||||
this._destroyed = true;
|
||||
},
|
||||
|
||||
_vpnChildFinished: function(pid, status, requestObj) {
|
||||
if (this._newStylePlugin) {
|
||||
// For new style plugin, all work is done in the async reading functions
|
||||
// Just reap the process here
|
||||
return;
|
||||
}
|
||||
|
||||
let [exited, exitStatus] = Shell.util_wifexited(status);
|
||||
|
||||
if (exited) {
|
||||
if (exitStatus != 0)
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
else
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.CONFIRMED);
|
||||
} else
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_vpnChildProcessLineOldStyle: function(line) {
|
||||
if (this._previousLine != undefined) {
|
||||
// Two consecutive newlines mean that the child should be closed
|
||||
// (the actual newlines are eaten by Gio.DataInputStream)
|
||||
// Send a termination message
|
||||
if (line == '' && this._previousLine == '') {
|
||||
try {
|
||||
this._stdin.write('QUIT\n\n', null);
|
||||
} catch(e) { /* ignore broken pipe errors */ }
|
||||
} else {
|
||||
this._agent.set_password(this._requestId, this._previousLine, line);
|
||||
this._previousLine = undefined;
|
||||
}
|
||||
} else {
|
||||
this._previousLine = line;
|
||||
}
|
||||
},
|
||||
|
||||
_readStdoutOldStyle: function() {
|
||||
this._dataStdout.read_line_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(stream, result) {
|
||||
let [line, len] = this._dataStdout.read_line_finish_utf8(result);
|
||||
|
||||
if (line == null) {
|
||||
// end of file
|
||||
this._stdout.close(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this._vpnChildProcessLineOldStyle(line);
|
||||
|
||||
// try to read more!
|
||||
this._readStdoutOldStyle();
|
||||
}));
|
||||
},
|
||||
|
||||
_readStdoutNewStyle: function() {
|
||||
this._dataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(stream, result) {
|
||||
let cnt = this._dataStdout.fill_finish(result);
|
||||
|
||||
if (cnt == 0) {
|
||||
// end of file
|
||||
this._showNewStyleDialog();
|
||||
|
||||
this._stdout.close(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to read more
|
||||
this._dataStdout.set_buffer_size(2 * this._dataStdout.get_buffer_size());
|
||||
this._readStdoutNewStyle();
|
||||
}));
|
||||
},
|
||||
|
||||
_showNewStyleDialog: function() {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
let contentOverride;
|
||||
|
||||
try {
|
||||
let data = this._dataStdout.peek_buffer();
|
||||
keyfile.load_from_data(data.toString(), data.length,
|
||||
GLib.KeyFileFlags.NONE);
|
||||
|
||||
if (keyfile.get_integer(VPN_UI_GROUP, 'Version') != 2)
|
||||
throw new Error('Invalid plugin keyfile version, is %d');
|
||||
|
||||
contentOverride = { title: keyfile.get_string(VPN_UI_GROUP, 'Title'),
|
||||
message: keyfile.get_string(VPN_UI_GROUP, 'Description'),
|
||||
secrets: [] };
|
||||
|
||||
let [groups, len] = keyfile.get_groups();
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
if (groups[i] == VPN_UI_GROUP)
|
||||
continue;
|
||||
|
||||
let value = keyfile.get_string(groups[i], 'Value');
|
||||
let shouldAsk = keyfile.get_boolean(groups[i], 'ShouldAsk');
|
||||
|
||||
if (shouldAsk) {
|
||||
contentOverride.secrets.push({ label: keyfile.get_string(groups[i], 'Label'),
|
||||
key: groups[i],
|
||||
value: value,
|
||||
password: keyfile.get_boolean(groups[i], 'IsSecret')
|
||||
});
|
||||
} else {
|
||||
if (!value.length) // Ignore empty secrets
|
||||
continue;
|
||||
|
||||
this._agent.set_password(this._requestId, groups[i], value);
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
logError(e, 'error while reading VPN plugin output keyfile');
|
||||
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (contentOverride.secrets.length) {
|
||||
// Only show the dialog if we actually have something to ask
|
||||
this._shellDialog = new NetworkSecretDialog(this._agent, this._requestId, this._connection, 'vpn', [], contentOverride);
|
||||
this._shellDialog.open(global.get_current_time());
|
||||
} else {
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.CONFIRMED);
|
||||
}
|
||||
},
|
||||
|
||||
_writeConnection: function() {
|
||||
let vpnSetting = this._connection.get_setting_vpn();
|
||||
|
||||
try {
|
||||
vpnSetting.foreach_data_item(Lang.bind(this, function(key, value) {
|
||||
this._stdin.write('DATA_KEY=' + key + '\n', null);
|
||||
this._stdin.write('DATA_VAL=' + (value || '') + '\n\n', null);
|
||||
}));
|
||||
vpnSetting.foreach_secret(Lang.bind(this, function(key, value) {
|
||||
this._stdin.write('SECRET_KEY=' + key + '\n', null);
|
||||
this._stdin.write('SECRET_VAL=' + (value || '') + '\n\n', null);
|
||||
}));
|
||||
this._stdin.write('DONE\n\n', null);
|
||||
} catch(e) {
|
||||
logError(e, 'internal error while writing connection to helper');
|
||||
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const NetworkAgent = new Lang.Class({
|
||||
Name: 'NetworkAgent',
|
||||
|
||||
_init: function() {
|
||||
this._native = new Shell.NetworkAgent({ auto_register: true,
|
||||
this._native = new Shell.NetworkAgent({ auto_register: false,
|
||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||
|
||||
this._dialogs = { };
|
||||
this._vpnRequests = { };
|
||||
|
||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints) {
|
||||
enable: function() {
|
||||
this._native.auto_register = true;
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
let requestId;
|
||||
|
||||
for (requestId in this._dialogs)
|
||||
this._dialogs[requestId].cancel();
|
||||
this._dialogs = { };
|
||||
|
||||
for (requestId in this._vpnRequests)
|
||||
this._vpnRequests[requestId].cancel(true);
|
||||
this._vpnRequests = { };
|
||||
|
||||
this._native.auto_register = false;
|
||||
if (this._native.registered)
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||
if (settingName == 'vpn') {
|
||||
this._vpnRequest(requestId, connection, hints, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
|
||||
dialog.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._dialogs[requestId];
|
||||
@ -379,7 +632,78 @@ const NetworkAgent = new Lang.Class({
|
||||
},
|
||||
|
||||
_cancelRequest: function(agent, requestId) {
|
||||
if (this._dialogs[requestId]) {
|
||||
this._dialogs[requestId].close(global.get_current_time());
|
||||
this._dialogs[requestId].destroy();
|
||||
delete this._dialogs[requestId];
|
||||
} else if (this._vpnRequests[requestId]) {
|
||||
this._vpnRequests[requestId].cancel(false);
|
||||
delete this._vpnRequests[requestId];
|
||||
}
|
||||
},
|
||||
|
||||
_vpnRequest: function(requestId, connection, hints, flags) {
|
||||
let vpnSetting = connection.get_setting_vpn();
|
||||
let serviceType = vpnSetting.service_type;
|
||||
|
||||
this._buildVPNServiceCache();
|
||||
|
||||
let binary = this._vpnBinaries[serviceType];
|
||||
if (!binary) {
|
||||
log('Invalid VPN service type (cannot find authentication binary)');
|
||||
|
||||
/* cancel the auth process */
|
||||
this._native.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
this._vpnRequests[requestId] = new VPNRequestHandler(this._native, requestId, binary, serviceType, connection, hints, flags);
|
||||
},
|
||||
|
||||
_buildVPNServiceCache: function() {
|
||||
if (this._vpnCacheBuilt)
|
||||
return;
|
||||
|
||||
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 info;
|
||||
|
||||
while ((info = fileEnum.next_file(null))) {
|
||||
let name = info.get_name();
|
||||
if (name.substr(-5) != '.name')
|
||||
continue;
|
||||
|
||||
try {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
keyfile.load_from_file(dir.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;
|
||||
try {
|
||||
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
let path = binary;
|
||||
if (!GLib.path_is_absolute(path)) {
|
||||
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
|
||||
}
|
||||
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
|
||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
|
||||
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()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
logError(e, 'error while enumerating VPN auth helpers');
|
||||
}
|
||||
}
|
||||
});
|
||||
const Component = NetworkAgent;
|
@ -1,24 +1,4 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2010 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
@ -33,15 +13,19 @@ const Mainloop = imports.mainloop;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
const PolkitAgent = imports.gi.PolkitAgent;
|
||||
|
||||
const Components = imports.ui.components;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
|
||||
const DIALOG_ICON_SIZE = 48;
|
||||
|
||||
const AuthenticationDialog = new Lang.Class({
|
||||
Name: 'AuthenticationDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(actionId, message, cookie, userNames) {
|
||||
this.parent({ styleClass: 'polkit-dialog' });
|
||||
this.parent({ styleClass: 'prompt-dialog' });
|
||||
|
||||
this.actionId = actionId;
|
||||
this.message = message;
|
||||
@ -49,7 +33,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._wasDismissed = false;
|
||||
this._completed = false;
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
{ x_fill: true,
|
||||
@ -62,19 +46,19 @@ const AuthenticationDialog = new Lang.Class({
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(messageBox,
|
||||
{ y_align: St.Align.START });
|
||||
{ expand: true, y_align: St.Align.START });
|
||||
|
||||
this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||
this._subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
|
||||
text: _("Authentication Required") });
|
||||
|
||||
messageBox.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||
text: message });
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
@ -117,9 +101,11 @@ const AuthenticationDialog = new Lang.Class({
|
||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||
vertical: false });
|
||||
messageBox.add(userBox);
|
||||
this._userIcon = new St.Icon();
|
||||
this._userIcon.hide();
|
||||
userBox.add(this._userIcon,
|
||||
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
|
||||
{ iconSize: DIALOG_ICON_SIZE,
|
||||
styleClass: 'polkit-dialog-user-icon' });
|
||||
this._userAvatar.actor.hide();
|
||||
userBox.add(this._userAvatar.actor,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
@ -135,11 +121,11 @@ const AuthenticationDialog = new Lang.Class({
|
||||
|
||||
this._onUserChanged();
|
||||
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
|
||||
messageBox.add(this._passwordBox);
|
||||
this._passwordLabel = new St.Label(({ style_class: 'polkit-dialog-password-label' }));
|
||||
this._passwordBox.add(this._passwordLabel);
|
||||
this._passwordEntry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: "",
|
||||
can_focus: true});
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
@ -149,13 +135,13 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this.setInitialKeyFocus(this._passwordEntry);
|
||||
this._passwordBox.hide();
|
||||
|
||||
this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' });
|
||||
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._errorMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._errorMessageLabel);
|
||||
this._errorMessageLabel.hide();
|
||||
|
||||
this._infoMessageLabel = new St.Label({ style_class: 'polkit-dialog-info-label' });
|
||||
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });
|
||||
this._infoMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._infoMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._infoMessageLabel);
|
||||
@ -165,8 +151,9 @@ const AuthenticationDialog = new Lang.Class({
|
||||
* infoMessage and errorMessageLabel - but it is still invisible because
|
||||
* gnome-shell.css sets the color to be transparent
|
||||
*/
|
||||
this._nullMessageLabel = new St.Label({ style_class: 'polkit-dialog-null-label',
|
||||
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
|
||||
text: 'abc'});
|
||||
this._nullMessageLabel.add_style_class_name('hidden');
|
||||
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._nullMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._nullMessageLabel);
|
||||
@ -177,7 +164,8 @@ const AuthenticationDialog = new Lang.Class({
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
|
||||
this._doneEmitted = false;
|
||||
@ -268,7 +256,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
|
||||
_onSessionRequest: function(session, request, echo_on) {
|
||||
// Cheap localization trick
|
||||
if (request == 'Password:')
|
||||
if (request == 'Password:' || request == 'Password: ')
|
||||
this._passwordLabel.set_text(_("Password:"));
|
||||
else
|
||||
this._passwordLabel.set_text(request);
|
||||
@ -311,19 +299,9 @@ const AuthenticationDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onUserChanged: function() {
|
||||
if (this._user.is_loaded) {
|
||||
if (this._userIcon) {
|
||||
let iconFileName = this._user.get_icon_file();
|
||||
let iconFile = Gio.file_new_for_path(iconFileName);
|
||||
let icon;
|
||||
if (iconFile.query_exists(null)) {
|
||||
icon = new Gio.FileIcon({file: iconFile});
|
||||
} else {
|
||||
icon = new Gio.ThemedIcon({name: 'avatar-default'});
|
||||
}
|
||||
this._userIcon.set_gicon (icon);
|
||||
this._userIcon.show();
|
||||
}
|
||||
if (this._user.is_loaded && this._userAvatar) {
|
||||
this._userAvatar.update();
|
||||
this._userAvatar.actor.show();
|
||||
}
|
||||
},
|
||||
|
||||
@ -339,11 +317,20 @@ const AuthenticationAgent = new Lang.Class({
|
||||
Name: 'AuthenticationAgent',
|
||||
|
||||
_init: function() {
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
this._handle = null;
|
||||
this._native = new Shell.PolkitAuthenticationAgent();
|
||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._native.register();
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||
@ -401,6 +388,4 @@ const AuthenticationAgent = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
function init() {
|
||||
let agent = new AuthenticationAgent();
|
||||
}
|
||||
const Component = AuthenticationAgent;
|
62
js/ui/components/recorder.js
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Recorder = new Lang.Class({
|
||||
Name: 'Recorder',
|
||||
|
||||
_init: function() {
|
||||
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||
this._recorder = null;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
Main.wm.addKeybinding('toggle-recording',
|
||||
this._bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._toggleRecorder));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
Main.wm.removeKeybinding('toggle-recording');
|
||||
},
|
||||
|
||||
_ensureRecorder: function() {
|
||||
if (this._recorder == null)
|
||||
this._recorder = new Shell.Recorder({ stage: global.stage });
|
||||
return this._recorder;
|
||||
},
|
||||
|
||||
_toggleRecorder: function() {
|
||||
let recorder = this._ensureRecorder();
|
||||
if (recorder.is_recording()) {
|
||||
recorder.close();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
|
||||
/* Translators: this is a filename used for screencast recording */
|
||||
// xgettext:no-c-format
|
||||
recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
|
||||
let pipeline = this._recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const Component = Recorder;
|
@ -11,10 +11,10 @@ const Tpl = imports.gi.TelepathyLogger;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const Params = imports.misc.params;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
// See Notification.appendMessage
|
||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
||||
@ -33,11 +33,6 @@ const NotificationDirection = {
|
||||
RECEIVED: 'chat-received'
|
||||
};
|
||||
|
||||
// This is GNOME Shell's implementation of the Telepathy 'Client'
|
||||
// interface. Specifically, the shell is a Telepathy 'Observer', which
|
||||
// lets us see messages even if they belong to another app (eg,
|
||||
// Empathy).
|
||||
|
||||
function makeMessageFromTpMessage(tpMessage, direction) {
|
||||
let [text, flags] = tpMessage.to_text();
|
||||
|
||||
@ -68,8 +63,8 @@ function makeMessageFromTplEvent(event) {
|
||||
};
|
||||
}
|
||||
|
||||
const Client = new Lang.Class({
|
||||
Name: 'Client',
|
||||
const TelepathyClient = new Lang.Class({
|
||||
Name: 'TelepathyClient',
|
||||
|
||||
_init: function() {
|
||||
// channel path -> ChatSource
|
||||
@ -94,9 +89,9 @@ const Client = new Lang.Class({
|
||||
// channel matching its filters is detected.
|
||||
// The second argument, recover, means _observeChannels will be run
|
||||
// for any existing channel as well.
|
||||
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
|
||||
'name': 'GnomeShell',
|
||||
'uniquify-name': true })
|
||||
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
|
||||
account_manager: this._accountManager,
|
||||
uniquify_name: true });
|
||||
this._tpClient.set_observe_channels_func(
|
||||
Lang.bind(this, this._observeChannels));
|
||||
this._tpClient.set_approve_channels_func(
|
||||
@ -104,6 +99,10 @@ const Client = new Lang.Class({
|
||||
this._tpClient.set_handle_channels_func(
|
||||
Lang.bind(this, this._handleChannels));
|
||||
|
||||
// Watch subscription requests and connection errors
|
||||
this._subscriptionSource = null;
|
||||
this._accountSource = null;
|
||||
|
||||
// Workaround for gjs not supporting GPtrArray in signals.
|
||||
// See BGO bug #653941 for context.
|
||||
this._tpClient.set_contact_list_changed_func(
|
||||
@ -113,23 +112,28 @@ const Client = new Lang.Class({
|
||||
// needed
|
||||
this._tpClient.set_delegated_channels_callback(
|
||||
Lang.bind(this, this._delegatedChannelsCb));
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
try {
|
||||
this._tpClient.register();
|
||||
} catch (e) {
|
||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||
}
|
||||
|
||||
// Watch subscription requests and connection errors
|
||||
this._subscriptionSource = null;
|
||||
this._accountSource = null;
|
||||
|
||||
this._accountManager.connect('account-validity-changed',
|
||||
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
|
||||
Lang.bind(this, this._accountValidityChanged));
|
||||
|
||||
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
|
||||
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._tpClient.unregister();
|
||||
this._accountManager.disconnect(this._accountManagerValidityChangedId);
|
||||
this._accountManagerValidityChangedId = 0;
|
||||
},
|
||||
|
||||
_observeChannels: function(observer, account, conn, channels,
|
||||
dispatchOp, requests, context) {
|
||||
let len = channels.length;
|
||||
@ -137,6 +141,9 @@ const Client = new Lang.Class({
|
||||
let channel = channels[i];
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
if (channel.get_invalidated())
|
||||
continue;
|
||||
|
||||
/* Only observe contact text channels */
|
||||
if ((!(channel instanceof Tp.TextChannel)) ||
|
||||
targetHandleType != Tp.HandleType.CONTACT)
|
||||
@ -186,6 +193,9 @@ const Client = new Lang.Class({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (channel.get_invalidated())
|
||||
continue;
|
||||
|
||||
// 'notify' will be true when coming from an actual HandleChannels
|
||||
// call, and not when from a successful Claim call. The point is
|
||||
// we don't want to notify for a channel we just claimed which
|
||||
@ -210,13 +220,15 @@ const Client = new Lang.Class({
|
||||
// We can only approve the rooms if we have been invited to it
|
||||
let selfContact = channel.group_get_self_contact();
|
||||
if (selfContact == null) {
|
||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
||||
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||
message: 'Not invited to the room' }));
|
||||
return;
|
||||
}
|
||||
|
||||
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
|
||||
if (!invited) {
|
||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
||||
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||
message: 'Not invited to the room' }));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -236,13 +248,21 @@ const Client = new Lang.Class({
|
||||
let channel = channels[0];
|
||||
let chanType = channel.get_channel_type();
|
||||
|
||||
if (channel.get_invalidated()) {
|
||||
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||
message: 'Channel is invalidated' }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
|
||||
this._approveTextChannel(account, conn, channel, dispatchOp, context);
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA ||
|
||||
chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT')
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
|
||||
this._approveCall(account, conn, channel, dispatchOp, context);
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
||||
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
||||
else
|
||||
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||
message: 'Unsupported channel type' }));
|
||||
},
|
||||
|
||||
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
||||
@ -270,8 +290,7 @@ const Client = new Lang.Class({
|
||||
|
||||
let props = channel.borrow_immutable_properties();
|
||||
|
||||
if (props['org.freedesktop.Telepathy.Channel.Type.Call.DRAFT.InitialVideo'] ||
|
||||
props[Tp.PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO])
|
||||
if (props[Tp.PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO])
|
||||
isVideo = true;
|
||||
|
||||
// We got the TpContact
|
||||
@ -364,25 +383,12 @@ const Client = new Lang.Class({
|
||||
}
|
||||
|
||||
/* Display notification to ask user to accept/reject request */
|
||||
let source = this._ensureSubscriptionSource();
|
||||
let source = this._ensureAppSource();
|
||||
|
||||
let notif = new SubscriptionRequestNotification(source, contact);
|
||||
source.notify(notif);
|
||||
},
|
||||
|
||||
_ensureSubscriptionSource: function() {
|
||||
if (this._subscriptionSource == null) {
|
||||
this._subscriptionSource = new MultiNotificationSource(
|
||||
_("Subscription request"), 'gtk-dialog-question');
|
||||
Main.messageTray.add(this._subscriptionSource);
|
||||
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._subscriptionSource = null;
|
||||
}));
|
||||
}
|
||||
|
||||
return this._subscriptionSource;
|
||||
},
|
||||
|
||||
_accountConnectionStatusNotifyCb: function(account) {
|
||||
let connectionError = account.connection_error;
|
||||
|
||||
@ -396,7 +402,7 @@ const Client = new Lang.Class({
|
||||
return;
|
||||
|
||||
/* Display notification that account failed to connect */
|
||||
let source = this._ensureAccountSource();
|
||||
let source = this._ensureAppSource();
|
||||
|
||||
notif = new AccountNotification(source, account, connectionError);
|
||||
this._accountNotifications[account.get_object_path()] = notif;
|
||||
@ -406,17 +412,16 @@ const Client = new Lang.Class({
|
||||
source.notify(notif);
|
||||
},
|
||||
|
||||
_ensureAccountSource: function() {
|
||||
if (this._accountSource == null) {
|
||||
this._accountSource = new MultiNotificationSource(
|
||||
_("Connection error"), 'gtk-dialog-error');
|
||||
Main.messageTray.add(this._accountSource);
|
||||
this._accountSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._accountSource = null;
|
||||
_ensureAppSource: function() {
|
||||
if (this._appSource == null) {
|
||||
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
||||
Main.messageTray.add(this._appSource);
|
||||
this._appSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._appSource = null;
|
||||
}));
|
||||
}
|
||||
|
||||
return this._accountSource;
|
||||
return this._appSource;
|
||||
}
|
||||
});
|
||||
|
||||
@ -425,14 +430,13 @@ const ChatSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(account, conn, channel, contact, client) {
|
||||
this.parent(contact.get_alias());
|
||||
|
||||
this.isChat = true;
|
||||
|
||||
this._account = account;
|
||||
this._contact = contact;
|
||||
this._client = client;
|
||||
|
||||
this.parent(contact.get_alias());
|
||||
|
||||
this.isChat = true;
|
||||
this._pendingMessages = [];
|
||||
|
||||
this._conn = conn;
|
||||
@ -453,8 +457,6 @@ const ChatSource = new Lang.Class({
|
||||
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
|
||||
this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
|
||||
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
|
||||
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
|
||||
@ -466,6 +468,22 @@ const ChatSource = new Lang.Class({
|
||||
this._getLogMessages();
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
let item;
|
||||
|
||||
let rightClickMenu = this.parent();
|
||||
item = new PopupMenu.PopupMenuItem('');
|
||||
item.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||
item.label.set_text(this.isMuted ? _("Unmute") : _("Mute"));
|
||||
}));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this.setMuted(!this.isMuted);
|
||||
this.emit('done-displaying-content', false);
|
||||
}));
|
||||
rightClickMenu.add(item.actor);
|
||||
return rightClickMenu;
|
||||
},
|
||||
|
||||
_updateAlias: function() {
|
||||
let oldAlias = this.title;
|
||||
let newAlias = this._contact.get_alias();
|
||||
@ -477,33 +495,55 @@ const ChatSource = new Lang.Class({
|
||||
this._notification.appendAliasChange(oldAlias, newAlias);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||
this._iconBox._size = this.ICON_SIZE;
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
getIcon: function() {
|
||||
let file = this._contact.get_avatar_file();
|
||||
|
||||
if (file) {
|
||||
let uri = file.get_uri();
|
||||
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
|
||||
return new Gio.FileIcon({ file: file });
|
||||
} else {
|
||||
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this._iconBox._size });
|
||||
return new Gio.ThemedIcon({ name: 'avatar-default' });
|
||||
}
|
||||
},
|
||||
|
||||
return this._iconBox;
|
||||
getSecondaryIcon: function() {
|
||||
let iconName;
|
||||
let presenceType = this._contact.get_presence_type();
|
||||
|
||||
switch (presenceType) {
|
||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||
iconName = 'user-available';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.BUSY:
|
||||
iconName = 'user-busy';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.OFFLINE:
|
||||
iconName = 'user-offline';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.HIDDEN:
|
||||
iconName = 'user-invisible';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.AWAY:
|
||||
iconName = 'user-away';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||
iconName = 'user-idle';
|
||||
break;
|
||||
default:
|
||||
iconName = 'user-offline';
|
||||
}
|
||||
return new Gio.ThemedIcon({ name: iconName });
|
||||
},
|
||||
|
||||
_updateAvatarIcon: function() {
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
|
||||
this.iconUpdated();
|
||||
this._notification.update(this._notification.title, null, { customContent: true });
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
if (this._client.is_handling_channel(this._channel)) {
|
||||
// We are handling the channel, try to pass it to Empathy
|
||||
this._client.delegate_channels_async([this._channel], global.get_current_time(), '', null);
|
||||
this._client.delegate_channels_async([this._channel],
|
||||
global.get_current_time(),
|
||||
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
||||
}
|
||||
else {
|
||||
// We are not the handler, just ask to present the channel
|
||||
@ -517,10 +557,10 @@ const ChatSource = new Lang.Class({
|
||||
_getLogMessages: function() {
|
||||
let logManager = Tpl.LogManager.dup_singleton();
|
||||
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
|
||||
Shell.get_contact_events(logManager,
|
||||
this._account, entity,
|
||||
SCROLLBACK_HISTORY_LINES,
|
||||
Lang.bind(this, this._displayPendingMessages));
|
||||
|
||||
logManager.get_filtered_events_async(this._account, entity,
|
||||
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
|
||||
null, Lang.bind(this, this._displayPendingMessages));
|
||||
},
|
||||
|
||||
_displayPendingMessages: function(logManager, result) {
|
||||
@ -542,7 +582,7 @@ const ChatSource = new Lang.Class({
|
||||
this._pendingMessages.push(message);
|
||||
}
|
||||
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
|
||||
let showTimestamp = false;
|
||||
|
||||
@ -588,8 +628,17 @@ const ChatSource = new Lang.Class({
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_updateCount: function() {
|
||||
this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0);
|
||||
/* All messages are new messages for Telepathy sources */
|
||||
get count() {
|
||||
return this._pendingMessages.length;
|
||||
},
|
||||
|
||||
get unseenCount() {
|
||||
return this.count;
|
||||
},
|
||||
|
||||
get countVisible() {
|
||||
return this.count > 0;
|
||||
},
|
||||
|
||||
_messageReceived: function(channel, message) {
|
||||
@ -597,7 +646,7 @@ const ChatSource = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._pendingMessages.push(message);
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
|
||||
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
||||
this._notification.appendMessage(message);
|
||||
@ -659,38 +708,14 @@ const ChatSource = new Lang.Class({
|
||||
},
|
||||
|
||||
_presenceChanged: function (contact, presence, status, message) {
|
||||
let msg, shouldNotify, title;
|
||||
|
||||
if (this._presence == presence)
|
||||
return;
|
||||
let msg, title;
|
||||
|
||||
title = GLib.markup_escape_text(this.title, -1);
|
||||
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE) {
|
||||
msg = _("%s is online.").format(title);
|
||||
shouldNotify = (this._presence == Tp.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Tp.ConnectionPresenceType.OFFLINE) {
|
||||
presence = Tp.ConnectionPresenceType.OFFLINE;
|
||||
msg = _("%s is offline.").format(title);
|
||||
shouldNotify = (this._presence != Tp.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Tp.ConnectionPresenceType.AWAY ||
|
||||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
|
||||
msg = _("%s is away.").format(title);
|
||||
shouldNotify = false;
|
||||
} else if (presence == Tp.ConnectionPresenceType.BUSY) {
|
||||
msg = _("%s is busy.").format(title);
|
||||
shouldNotify = false;
|
||||
} else
|
||||
return;
|
||||
|
||||
this._presence = presence;
|
||||
this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
|
||||
|
||||
if (message)
|
||||
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
||||
|
||||
this._notification.appendPresence(msg, shouldNotify);
|
||||
if (shouldNotify)
|
||||
this.notify();
|
||||
},
|
||||
|
||||
_pendingRemoved: function(channel, message) {
|
||||
@ -698,17 +723,14 @@ const ChatSource = new Lang.Class({
|
||||
|
||||
if (idx >= 0) {
|
||||
this._pendingMessages.splice(idx, 1);
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
}
|
||||
else
|
||||
throw new Error('Message not in our pending list: ' + message);
|
||||
},
|
||||
|
||||
_ackMessages: function() {
|
||||
// Don't clear our messages here, tp-glib will send a
|
||||
// 'pending-message-removed' for each one.
|
||||
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
|
||||
this._channel.ack_all_pending_messages_finish(result);}));
|
||||
this._channel.ack_all_pending_messages_async(null);
|
||||
}
|
||||
});
|
||||
|
||||
@ -717,7 +739,7 @@ const ChatNotification = new Lang.Class({
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
this.parent(source, source.title, null, { customContent: true });
|
||||
this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() });
|
||||
this.setResident(true);
|
||||
|
||||
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
||||
@ -726,16 +748,27 @@ const ChatNotification = new Lang.Class({
|
||||
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
|
||||
this.setActionArea(this._responseEntry);
|
||||
|
||||
this._oldMaxScrollAdjustment = 0;
|
||||
this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
|
||||
this.focused = true;
|
||||
}));
|
||||
this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
|
||||
this.focused = false;
|
||||
this.emit('unfocused');
|
||||
}));
|
||||
|
||||
this._createScrollArea();
|
||||
this._lastGroup = null;
|
||||
this._lastGroupActor = null;
|
||||
|
||||
// Keep track of the bottom position for the current adjustment and
|
||||
// force a scroll to the bottom if things change while we were at the
|
||||
// bottom
|
||||
this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
|
||||
this._scrollArea.add_style_class_name('chat-notification-scrollview');
|
||||
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
|
||||
let currentValue = adjustment.value + adjustment.page_size;
|
||||
if (currentValue == this._oldMaxScrollAdjustment)
|
||||
if (adjustment.value == this._oldMaxScrollValue)
|
||||
this.scrollTo(St.Side.BOTTOM);
|
||||
this._oldMaxScrollAdjustment = adjustment.upper;
|
||||
this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
|
||||
}));
|
||||
|
||||
this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
|
||||
@ -810,7 +843,7 @@ const ChatNotification = new Lang.Class({
|
||||
let groups = this._contentArea.get_children();
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let group = groups[i];
|
||||
if (group.get_children().length == 0)
|
||||
if (group.get_n_children() == 0)
|
||||
group.destroy();
|
||||
}
|
||||
},
|
||||
@ -860,6 +893,8 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
this._lastGroupActor.add(body, props.childProps);
|
||||
|
||||
this.updated();
|
||||
|
||||
let timestamp = props.timestamp;
|
||||
this._history.unshift({ actor: body, time: timestamp,
|
||||
realMessage: group != 'meta' });
|
||||
@ -885,24 +920,30 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
let format;
|
||||
|
||||
// Show a week day and time if date is in the last week
|
||||
if (daysAgo < 1 || (daysAgo < 7 && now.getDay() != date.getDay())) {
|
||||
/* Translators: this is a time format string followed by a date.
|
||||
If applicable, replace %X with a strftime format valid for your
|
||||
locale, without seconds. */
|
||||
// Show only the hour if date is on today
|
||||
if(daysAgo < 1){
|
||||
format = "<b>%H:%M</b>";
|
||||
}
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if(daysAgo <2){
|
||||
/* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent at <b>%X</b> on <b>%A</b>");
|
||||
format = _("<b>Yesterday</b>, <b>%H:%M</b>");
|
||||
}
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7) {
|
||||
/* Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30*/
|
||||
// xgettext:no-c-format
|
||||
format = _("<b>%A</b>, <b>%H:%M</b>");
|
||||
|
||||
} else if (date.getYear() == now.getYear()) {
|
||||
/* Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
shown when you get a chat message in the same year. */
|
||||
/* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
|
||||
format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>");
|
||||
} else {
|
||||
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
shown when you get a chat message in a different year. */
|
||||
/* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
|
||||
format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
|
||||
}
|
||||
|
||||
return date.toLocaleFormat(format);
|
||||
@ -915,8 +956,7 @@ const ChatNotification = new Lang.Class({
|
||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'],
|
||||
childProps: { expand: true, x_fill: false,
|
||||
x_align: St.Align.END },
|
||||
childProps: { expand: true, x_fill: false },
|
||||
noTimestamp: true,
|
||||
timestamp: lastMessageTime });
|
||||
|
||||
@ -925,19 +965,6 @@ const ChatNotification = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
appendPresence: function(text, asTitle) {
|
||||
if (asTitle)
|
||||
this.update(text, null, { customContent: true, titleMarkup: true });
|
||||
else
|
||||
this.update(this.source.title, null, { customContent: true });
|
||||
|
||||
let label = this._append({ body: text,
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'] });
|
||||
|
||||
this._filterMessages();
|
||||
},
|
||||
|
||||
appendAliasChange: function(oldAlias, newAlias) {
|
||||
oldAlias = GLib.markup_escape_text(oldAlias, -1);
|
||||
newAlias = GLib.markup_escape_text(newAlias, -1);
|
||||
@ -1007,10 +1034,9 @@ const ApproverSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(dispatchOp, text, gicon) {
|
||||
this.parent(text);
|
||||
|
||||
this._gicon = gicon;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this.parent(text);
|
||||
|
||||
this._dispatchOp = dispatchOp;
|
||||
|
||||
@ -1031,10 +1057,8 @@ const ApproverSource = new Lang.Class({
|
||||
this.parent();
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
getIcon: function() {
|
||||
return this._gicon;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1092,10 +1116,10 @@ const AudioVideoNotification = new Lang.Class({
|
||||
/* translators: argument is a contact name like Alice for example. */
|
||||
title = _("Call from %s").format(contact.get_alias());
|
||||
|
||||
this.parent(this, source, title, null, { customContent: true });
|
||||
this.parent(source, title, null, { customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this.addButton('reject', _("Reject"));
|
||||
this.addButton('reject', _("Decline"));
|
||||
/* translators: this is a button label (verb), not a noun */
|
||||
this.addButton('answer', _("Answer"));
|
||||
|
||||
@ -1123,8 +1147,7 @@ const FileTransferNotification = new Lang.Class({
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, dispatchOp, channel, contact) {
|
||||
this.parent(this,
|
||||
source,
|
||||
this.parent(source,
|
||||
/* To translators: The first parameter is
|
||||
* the contact's alias and the second one is the
|
||||
* file name. The string will be something
|
||||
@ -1157,47 +1180,13 @@ const FileTransferNotification = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
// A notification source that can embed multiple notifications
|
||||
const MultiNotificationSource = new Lang.Class({
|
||||
Name: 'MultiNotificationSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(title, icon) {
|
||||
this.parent(title);
|
||||
|
||||
this._icon = icon;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this._nbNotifications = 0;
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
this.parent(notification);
|
||||
|
||||
this._nbNotifications += 1;
|
||||
|
||||
// Display the source while there is at least one notification
|
||||
notification.connect('destroy', Lang.bind(this, function () {
|
||||
this._nbNotifications -= 1;
|
||||
|
||||
if (this._nbNotifications == 0)
|
||||
this.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string(this._icon),
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
});
|
||||
|
||||
// Subscription request
|
||||
const SubscriptionRequestNotification = new Lang.Class({
|
||||
Name: 'SubscriptionRequestNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, contact) {
|
||||
this.parent(this, source,
|
||||
this.parent(source,
|
||||
/* To translators: The parameter is the contact's alias */
|
||||
_("%s would like permission to see when you are online").format(contact.get_alias()),
|
||||
null, { customContent: true });
|
||||
@ -1220,7 +1209,6 @@ const SubscriptionRequestNotification = new Lang.Class({
|
||||
}
|
||||
else {
|
||||
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: iconBox._size });
|
||||
}
|
||||
|
||||
@ -1324,7 +1312,7 @@ _connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED)]
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST)]
|
||||
= _("Connection has been lost");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)]
|
||||
= _("This resource is already connected to the server");
|
||||
= _("This account is already connected to the server");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED)]
|
||||
= _("Connection has been replaced by a new connection using the same resource");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.REGISTRATION_EXISTS)]
|
||||
@ -1337,6 +1325,8 @@ _connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE)]
|
||||
= _("Certificate uses an insecure cipher algorithm or is cryptographically weak");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED)]
|
||||
= _("The length of the server certificate, or the depth of the server certificate chain, exceed the limits imposed by the cryptography library");
|
||||
_connectionErrorMessages['org.freedesktop.DBus.Error.NoReply']
|
||||
= _("Internal error");
|
||||
|
||||
const AccountNotification = new Lang.Class({
|
||||
Name: 'AccountNotification',
|
||||
@ -1346,32 +1336,21 @@ const AccountNotification = new Lang.Class({
|
||||
this.parent(source,
|
||||
/* translators: argument is the account name, like
|
||||
* name@jabber.org for example. */
|
||||
_("Connection to %s failed").format(account.get_display_name()),
|
||||
null, { customContent: true });
|
||||
|
||||
this._label = new St.Label();
|
||||
this.addActor(this._label);
|
||||
this._updateMessage(connectionError);
|
||||
_("Unable to connect to %s").format(account.get_display_name()),
|
||||
this._getMessage(connectionError));
|
||||
|
||||
this._account = account;
|
||||
|
||||
this.addButton('reconnect', _("Reconnect"));
|
||||
this.addButton('edit', _("Edit account"));
|
||||
this.addButton('view', _("View account"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'reconnect':
|
||||
// If it fails again, a new notification should pop up with the
|
||||
// new error.
|
||||
account.reconnect_async(null, null);
|
||||
break;
|
||||
case 'edit':
|
||||
case 'view':
|
||||
let cmd = '/usr/bin/empathy-accounts'
|
||||
+ ' --select-account=%s'
|
||||
.format(account.get_path_suffix());
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
|
||||
null);
|
||||
app_info.launch([], null, null);
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||
app_info.launch([], global.create_app_launch_context());
|
||||
break;
|
||||
}
|
||||
this.destroy();
|
||||
@ -1392,19 +1371,19 @@ const AccountNotification = new Lang.Class({
|
||||
if (status == Tp.ConnectionStatus.CONNECTED) {
|
||||
this.destroy();
|
||||
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
|
||||
this._updateMessage(account.connection_error);
|
||||
this.update(this.title, this._getMessage(account.connection_error));
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updateMessage: function(connectionError) {
|
||||
_getMessage: function(connectionError) {
|
||||
let message;
|
||||
if (connectionError in _connectionErrorMessages) {
|
||||
message = _connectionErrorMessages[connectionError];
|
||||
} else {
|
||||
message = _("Unknown reason");
|
||||
}
|
||||
this._label.set_text(message);
|
||||
return message;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -1426,3 +1405,4 @@ const AccountNotification = new Lang.Class({
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
const Component = TelepathyClient;
|
@ -1,180 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Folks = imports.gi.Folks
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Util = imports.misc.util;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
const ICON_SIZE = 81;
|
||||
|
||||
function launchContact(id) {
|
||||
Util.spawn(['gnome-contacts', '-i', id]);
|
||||
}
|
||||
|
||||
|
||||
/* This class represents a shown contact search result in the overview */
|
||||
const Contact = new Lang.Class({
|
||||
Name: 'Contact',
|
||||
|
||||
_init: function(id) {
|
||||
this._contactSys = Shell.ContactSystem.get_default();
|
||||
this.individual = this._contactSys.get_individual(id);
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'contact',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
|
||||
let content = new St.BoxLayout( { style_class: 'contact-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: ICON_SIZE,
|
||||
style_class: 'contact-icon' });
|
||||
if (this.individual.avatar != null)
|
||||
icon.gicon = this.individual.avatar;
|
||||
else
|
||||
icon.icon_name = 'avatar-default';
|
||||
|
||||
content.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let details = new St.BoxLayout({ style_class: 'contact-details',
|
||||
vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let email = this._contactSys.get_email_for_display(this.individual);
|
||||
let aliasText = this.individual.alias ||
|
||||
this.individual.full_name ||
|
||||
this.individual.nickname ||
|
||||
email ||
|
||||
_("Unknown");
|
||||
let aliasLabel = new St.Label({ text: aliasText,
|
||||
style_class: 'contact-details-alias' });
|
||||
details.add(aliasLabel, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let presence = this._createPresence(this.individual.presence_type);
|
||||
details.add(presence, { x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
},
|
||||
|
||||
_createPresence: function(presence) {
|
||||
let text;
|
||||
let iconName;
|
||||
|
||||
switch(presence) {
|
||||
case Folks.PresenceType.AVAILABLE:
|
||||
text = _("Available");
|
||||
iconName = 'user-available';
|
||||
break;
|
||||
case Folks.PresenceType.AWAY:
|
||||
case Folks.PresenceType.EXTENDED_AWAY:
|
||||
text = _("Away");
|
||||
iconName = 'user-away';
|
||||
break;
|
||||
case Folks.PresenceType.BUSY:
|
||||
text = _("Busy");
|
||||
iconName = 'user-busy';
|
||||
break;
|
||||
default:
|
||||
text = _("Offline");
|
||||
iconName = 'user-offline';
|
||||
}
|
||||
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: 16,
|
||||
style_class: 'contact-details-status-icon' });
|
||||
let label = new St.Label({ text: text });
|
||||
|
||||
let box = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'contact-details-status' });
|
||||
box.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
box.add(label, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
return box;
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
let tc = St.TextureCache.get_default();
|
||||
let icon = this.individual.avatar;
|
||||
|
||||
if (icon != null) {
|
||||
return tc.load_gicon(null, icon, size);
|
||||
} else {
|
||||
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/* Searches for and returns contacts */
|
||||
const ContactSearchProvider = new Lang.Class({
|
||||
Name: 'ContactSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("CONTACTS"));
|
||||
this._contactSys = Shell.ContactSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMeta: function(id) {
|
||||
let contact = new Contact(id);
|
||||
return { 'id': id,
|
||||
'name': contact.alias,
|
||||
'createIcon': function(size) {
|
||||
return contact.createIcon(size);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._contactSys.initial_search(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._contactSys.subsearch(previousResults, terms);
|
||||
},
|
||||
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
let contact = new Contact(resultMeta.id);
|
||||
return contact.actor;
|
||||
},
|
||||
|
||||
createResultContainerActor: function() {
|
||||
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
grid.actor.style_class = 'contact-grid';
|
||||
|
||||
let actor = new SearchDisplay.GridSearchResults(this, grid);
|
||||
return actor;
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
launchContact(id);
|
||||
}
|
||||
});
|
@ -1,15 +1,14 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AltTab = imports.ui.altTab;
|
||||
const Main = imports.ui.main;
|
||||
const SwitcherPopup = imports.ui.switcherPopup;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
@ -27,7 +26,9 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._items = [];
|
||||
this._focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
this.addGroup(global.window_group, _("Windows"),
|
||||
'emblem-documents-symbolic', { sortGroup: SortGroup.TOP,
|
||||
focusCallback: Lang.bind(this, this._focusWindows) });
|
||||
},
|
||||
|
||||
addGroup: function(root, name, icon, params) {
|
||||
@ -41,11 +42,13 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
|
||||
this._items.push(item);
|
||||
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
||||
this._focusManager.add_group(root);
|
||||
if (root instanceof St.Widget)
|
||||
global.focus_manager.add_group(root);
|
||||
},
|
||||
|
||||
removeGroup: function(root) {
|
||||
this._focusManager.remove_group(root);
|
||||
if (root instanceof St.Widget)
|
||||
global.focus_manager.remove_group(root);
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].root == root) {
|
||||
this._items.splice(i, 1);
|
||||
@ -54,17 +57,16 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
focusGroup: function(item) {
|
||||
focusGroup: function(item, timestamp) {
|
||||
if (item.focusCallback) {
|
||||
item.focusCallback(timestamp);
|
||||
} else {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
|
||||
if (item.window)
|
||||
Main.activateWindow(item.window);
|
||||
else if (item.focusCallback)
|
||||
item.focusCallback();
|
||||
else
|
||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Sort the items into a consistent order; panel first, tray last,
|
||||
@ -75,24 +77,14 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
if (a.sortGroup != b.sortGroup)
|
||||
return a.sortGroup - b.sortGroup;
|
||||
|
||||
let y;
|
||||
if (a.x == undefined) {
|
||||
if (a.window)
|
||||
a.x = a.window.get_compositor_private().x;
|
||||
else
|
||||
[a.x, y] = a.proxy.get_transformed_position();
|
||||
}
|
||||
if (b.x == undefined) {
|
||||
if (b.window)
|
||||
b.x = b.window.get_compositor_private().x;
|
||||
else
|
||||
[b.x, y] = b.proxy.get_transformed_position();
|
||||
}
|
||||
let ax, bx, y;
|
||||
[ax, y] = a.proxy.get_transformed_position();
|
||||
[bx, y] = b.proxy.get_transformed_position();
|
||||
|
||||
return a.x - b.x;
|
||||
return ax - bx;
|
||||
},
|
||||
|
||||
popup: function(backwards, mask) {
|
||||
popup: function(backward, binding, mask) {
|
||||
// Start with the set of focus groups that are currently mapped
|
||||
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
||||
|
||||
@ -110,8 +102,7 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ window: windows[i],
|
||||
name: windows[i].title,
|
||||
items.push({ name: windows[i].title,
|
||||
iconActor: icon,
|
||||
sortGroup: SortGroup.MIDDLE });
|
||||
}
|
||||
@ -123,187 +114,63 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
items.sort(Lang.bind(this, this._sortItems));
|
||||
|
||||
if (!this._popup) {
|
||||
this._popup = new CtrlAltTabPopup();
|
||||
this._popup.show(items, backwards, mask);
|
||||
this._popup = new CtrlAltTabPopup(items);
|
||||
this._popup.show(backward, binding, mask);
|
||||
|
||||
this._popup.actor.connect('destroy',
|
||||
Lang.bind(this, function() {
|
||||
this._popup = null;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_focusWindows: function(timestamp) {
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
global.stage.key_focus = null;
|
||||
global.screen.focus_default_window(timestamp);
|
||||
}
|
||||
});
|
||||
|
||||
function mod(a, b) {
|
||||
return (a + b) % b;
|
||||
}
|
||||
|
||||
const CtrlAltTabPopup = new Lang.Class({
|
||||
Name: 'CtrlAltTabPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
|
||||
_init : function() {
|
||||
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._haveModal = false;
|
||||
this._modifierMask = 0;
|
||||
this._selection = 0;
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
alloc.min_size = primary.width;
|
||||
alloc.natural_size = primary.width;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
alloc.min_size = primary.height;
|
||||
alloc.natural_size = primary.height;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childBox = new Clutter.ActorBox();
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
||||
let vPadding = this.actor.get_theme_node().get_vertical_padding();
|
||||
let hPadding = this.actor.get_theme_node().get_horizontal_padding();
|
||||
|
||||
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
|
||||
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
|
||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
||||
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||
this._switcher.actor.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
show : function(items, startBackwards, mask) {
|
||||
if (!Main.pushModal(this.actor))
|
||||
return false;
|
||||
this._haveModal = true;
|
||||
this._modifierMask = AltTab.primaryModifier(mask);
|
||||
|
||||
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||
|
||||
this._items = items;
|
||||
this._switcher = new CtrlAltTabSwitcher(items);
|
||||
this.actor.add_actor(this._switcher.actor);
|
||||
|
||||
if (startBackwards)
|
||||
this._selection = this._items.length - 1;
|
||||
this._select(this._selection);
|
||||
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
if (!(mods & this._modifierMask)) {
|
||||
this._finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
_createSwitcher: function() {
|
||||
this._switcherList = new CtrlAltTabSwitcher(this._items);
|
||||
return true;
|
||||
},
|
||||
|
||||
_next : function() {
|
||||
return mod(this._selection + 1, this._items.length);
|
||||
_initialSelection: function(backward, binding) {
|
||||
if (binding == 'switch-panels') {
|
||||
if (backward)
|
||||
this._selectedIndex = this._items.length - 1;
|
||||
} else if (binding == 'switch-panels-backward') {
|
||||
if (!backward)
|
||||
this._selectedIndex = this._items.length - 1;
|
||||
}
|
||||
this._select(this._selectedIndex);
|
||||
},
|
||||
|
||||
_previous : function() {
|
||||
return mod(this._selection - 1, this._items.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
if (shift && keysym == Clutter.KEY_Tab)
|
||||
keysym = Clutter.ISO_Left_Tab;
|
||||
|
||||
if (keysym == Clutter.KEY_Escape)
|
||||
this.destroy();
|
||||
else if (keysym == Clutter.KEY_Tab)
|
||||
this._select(this._next());
|
||||
else if (keysym == Clutter.KEY_ISO_Left_Tab)
|
||||
_keyPressHandler: function(keysym, backwards, action) {
|
||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS)
|
||||
this._select(backwards ? this._previous() : this._next());
|
||||
else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
|
||||
this._select(backwards ? this._next() : this._previous());
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Right)
|
||||
else if (keysym == Clutter.Right)
|
||||
this._select(this._next());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
let state = mods & this._modifierMask;
|
||||
|
||||
if (state == 0)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
_finish : function(time) {
|
||||
this.parent(time);
|
||||
Main.ctrlAltTabManager.focusGroup(this._items[this._selectedIndex], time);
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
this.destroy();
|
||||
|
||||
Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
|
||||
},
|
||||
|
||||
_popModal: function() {
|
||||
if (this._haveModal) {
|
||||
Main.popModal(this.actor);
|
||||
this._haveModal = false;
|
||||
}
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this._popModal();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
this._popModal();
|
||||
if (this._keyPressEventId)
|
||||
this.actor.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
this.actor.disconnect(this._keyReleaseEventId);
|
||||
},
|
||||
|
||||
_select : function(num) {
|
||||
this._selection = num;
|
||||
this._switcher.highlight(num);
|
||||
}
|
||||
});
|
||||
|
||||
const CtrlAltTabSwitcher = new Lang.Class({
|
||||
Name: 'CtrlAltTabSwitcher',
|
||||
Extends: AltTab.SwitcherList,
|
||||
Extends: SwitcherPopup.SwitcherList,
|
||||
|
||||
_init : function(items) {
|
||||
this.parent(true);
|
||||
@ -319,7 +186,6 @@ const CtrlAltTabSwitcher = new Lang.Class({
|
||||
let icon = item.iconActor;
|
||||
if (!icon) {
|
||||
icon = new St.Icon({ icon_name: item.iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: POPUP_APPICON_SIZE });
|
||||
}
|
||||
box.add(icon, { x_fill: false, y_fill: false } );
|
||||
|
357
js/ui/dash.js
@ -21,6 +21,17 @@ const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
|
||||
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
||||
|
||||
function getAppFromSource(source) {
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
return source.app;
|
||||
} else if (source.metaWindow) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
return tracker.get_window_app(source.metaWindow);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// A container like StBin, but taking the child's scale into account
|
||||
// when requesting a size
|
||||
const DashItemContainer = new Lang.Class({
|
||||
@ -36,7 +47,11 @@ const DashItemContainer = new Lang.Class({
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._label = null;
|
||||
this._labelText = "";
|
||||
this.label = new St.Label({ style_class: 'dash-label'});
|
||||
this.label.hide();
|
||||
Main.layoutManager.addChrome(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.child = null;
|
||||
this._childScale = 1;
|
||||
@ -91,32 +106,33 @@ const DashItemContainer = new Lang.Class({
|
||||
},
|
||||
|
||||
showLabel: function() {
|
||||
if (this._label == null)
|
||||
if (!this._labelText)
|
||||
return;
|
||||
|
||||
this._label.opacity = 0;
|
||||
this._label.show();
|
||||
this.label.set_text(this._labelText);
|
||||
this.label.opacity = 0;
|
||||
this.label.show();
|
||||
|
||||
let [stageX, stageY] = this.actor.get_transformed_position();
|
||||
|
||||
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
||||
|
||||
let labelHeight = this._label.get_height();
|
||||
let labelHeight = this.label.get_height();
|
||||
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
||||
|
||||
let y = stageY + yOffset;
|
||||
|
||||
let node = this._label.get_theme_node();
|
||||
let node = this.label.get_theme_node();
|
||||
let xOffset = node.get_length('-x-offset');
|
||||
|
||||
let x;
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL)
|
||||
x = stageX - this._label.get_width() - xOffset;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
x = stageX - this.label.get_width() - xOffset;
|
||||
else
|
||||
x = stageX + this.actor.get_width() + xOffset;
|
||||
|
||||
this._label.set_position(x, y);
|
||||
Tweener.addTween(this._label,
|
||||
this.label.set_position(x, y);
|
||||
Tweener.addTween(this.label,
|
||||
{ opacity: 255,
|
||||
time: DASH_ITEM_LABEL_SHOW_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
@ -124,22 +140,17 @@ const DashItemContainer = new Lang.Class({
|
||||
},
|
||||
|
||||
setLabelText: function(text) {
|
||||
if (this._label == null)
|
||||
this._label = new St.Label({ style_class: 'dash-label'});
|
||||
|
||||
this._label.set_text(text);
|
||||
Main.layoutManager.addChrome(this._label);
|
||||
this._label.hide();
|
||||
this._labelText = text;
|
||||
this.child.accessible_name = text;
|
||||
},
|
||||
|
||||
hideLabel: function () {
|
||||
this._label.opacity = 255;
|
||||
Tweener.addTween(this._label,
|
||||
Tweener.addTween(this.label,
|
||||
{ opacity: 0,
|
||||
time: DASH_ITEM_LABEL_HIDE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._label.hide();
|
||||
this.label.hide();
|
||||
})
|
||||
});
|
||||
},
|
||||
@ -148,7 +159,7 @@ const DashItemContainer = new Lang.Class({
|
||||
if (this.child == actor)
|
||||
return;
|
||||
|
||||
this.actor.destroy_children();
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
this.child = actor;
|
||||
this.actor.add_actor(this.child);
|
||||
@ -168,7 +179,17 @@ const DashItemContainer = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.label)
|
||||
this.label.destroy();
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
animateOutAndDestroy: function() {
|
||||
if (this.label)
|
||||
this.label.destroy();
|
||||
|
||||
if (this.child == null) {
|
||||
this.actor.destroy();
|
||||
return;
|
||||
@ -217,52 +238,76 @@ const DashItemContainer = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const RemoveFavoriteIcon = new Lang.Class({
|
||||
Name: 'RemoveFavoriteIcon',
|
||||
const ShowAppsIcon = new Lang.Class({
|
||||
Name: 'ShowAppsIcon',
|
||||
Extends: DashItemContainer,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
|
||||
this.toggleButton = new St.Button({ style_class: 'show-apps',
|
||||
track_hover: true,
|
||||
can_focus: true,
|
||||
toggle_mode: true });
|
||||
this._iconActor = null;
|
||||
this.icon = new IconGrid.BaseIcon(_("Remove"),
|
||||
this.icon = new IconGrid.BaseIcon(_("Show Applications"),
|
||||
{ setSizeManually: true,
|
||||
showLabel: false,
|
||||
createIcon: Lang.bind(this, this._createIcon) });
|
||||
this._iconBin.set_child(this.icon.actor);
|
||||
this._iconBin._delegate = this;
|
||||
this.toggleButton.add_actor(this.icon.actor);
|
||||
this.toggleButton._delegate = this;
|
||||
|
||||
this.setChild(this._iconBin);
|
||||
this.setChild(this.toggleButton);
|
||||
this.setDragApp(null);
|
||||
},
|
||||
|
||||
_createIcon: function(size) {
|
||||
this._iconActor = new St.Icon({ icon_name: 'user-trash',
|
||||
style_class: 'remove-favorite-icon',
|
||||
icon_size: size });
|
||||
this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
|
||||
icon_size: size,
|
||||
style_class: 'show-apps-icon',
|
||||
track_hover: true });
|
||||
return this._iconActor;
|
||||
},
|
||||
|
||||
setHover: function(hovered) {
|
||||
this._iconBin.set_hover(hovered);
|
||||
if (this._iconActor)
|
||||
this._iconActor.set_hover(hovered);
|
||||
_canRemoveApp: function(app) {
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
let id = app.get_id();
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
||||
return isFavorite;
|
||||
},
|
||||
|
||||
setDragApp: function(app) {
|
||||
let canRemove = this._canRemoveApp(app);
|
||||
|
||||
this.toggleButton.set_hover(canRemove);
|
||||
if (this._iconActor)
|
||||
this._iconActor.set_hover(canRemove);
|
||||
|
||||
if (canRemove)
|
||||
this.setLabelText(_("Remove from Favorites"));
|
||||
else
|
||||
this.setLabelText(_("Show Applications"));
|
||||
},
|
||||
|
||||
// Rely on the dragged item being a favorite
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
let app = getAppFromSource(source);
|
||||
if (app == null)
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let id = app.get_id();
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
||||
if (!isFavorite)
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
},
|
||||
|
||||
acceptDrop: function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
app = appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
app = tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
let app = getAppFromSource(source);
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
let id = app.get_id();
|
||||
|
||||
@ -286,6 +331,39 @@ const DragPlaceholderItem = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const DashActor = new Lang.Class({
|
||||
Name: 'DashActor',
|
||||
Extends: St.Widget,
|
||||
|
||||
_init: function() {
|
||||
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
this.parent({ name: 'dash',
|
||||
layout_manager: layout,
|
||||
clip_to_allocation: true });
|
||||
},
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
let contentBox = this.get_theme_node().get_content_box(box);
|
||||
let availWidth = contentBox.x2 - contentBox.x1;
|
||||
|
||||
this.set_allocation(box, flags);
|
||||
|
||||
let [appIcons, showAppsButton] = this.get_children();
|
||||
let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = contentBox.x1;
|
||||
childBox.y1 = contentBox.y1;
|
||||
childBox.x2 = contentBox.x2;
|
||||
childBox.y2 = contentBox.y2 - showAppsNatHeight;
|
||||
appIcons.allocate(childBox, flags);
|
||||
|
||||
childBox.y1 = contentBox.y2 - showAppsNatHeight;
|
||||
childBox.y2 = contentBox.y2;
|
||||
showAppsButton.allocate(childBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const Dash = new Lang.Class({
|
||||
Name: 'Dash',
|
||||
|
||||
@ -297,17 +375,25 @@ const Dash = new Lang.Class({
|
||||
this._dragPlaceholder = null;
|
||||
this._dragPlaceholderPos = -1;
|
||||
this._animatingPlaceholdersCount = 0;
|
||||
this._favRemoveTarget = null;
|
||||
this._showLabelTimeoutId = 0;
|
||||
this._resetHoverTimeoutId = 0;
|
||||
this._labelShowing = false;
|
||||
|
||||
this._box = new St.BoxLayout({ name: 'dash',
|
||||
vertical: true,
|
||||
this._container = new DashActor();
|
||||
this._box = new St.BoxLayout({ vertical: true,
|
||||
clip_to_allocation: true });
|
||||
this._box._delegate = this;
|
||||
this._container.add_actor(this._box);
|
||||
|
||||
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
|
||||
this._showAppsIcon = new ShowAppsIcon();
|
||||
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
||||
this._hookUpLabel(this._showAppsIcon);
|
||||
|
||||
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||||
|
||||
this._container.add_actor(this._showAppsIcon.actor);
|
||||
|
||||
this.actor = new St.Bin({ child: this._container });
|
||||
this.actor.connect('notify::height', Lang.bind(this,
|
||||
function() {
|
||||
if (this._maxHeight != this.actor.height)
|
||||
@ -317,7 +403,6 @@ const Dash = new Lang.Class({
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||
@ -360,53 +445,25 @@ const Dash = new Lang.Class({
|
||||
|
||||
_endDrag: function() {
|
||||
this._clearDragPlaceholder();
|
||||
if (this._favRemoveTarget) {
|
||||
this._favRemoveTarget.animateOutAndDestroy();
|
||||
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
this._favRemoveTarget = null;
|
||||
}));
|
||||
this._adjustIconSize();
|
||||
}
|
||||
this._showAppsIcon.setDragApp(null);
|
||||
DND.removeDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_onDragMotion: function(dragEvent) {
|
||||
let app = null;
|
||||
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.lookup_app(dragEvent.source.getId());
|
||||
else if (dragEvent.source.metaWindow)
|
||||
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
||||
else
|
||||
let app = getAppFromSource(dragEvent.source);
|
||||
if (app == null)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
let id = app.get_id();
|
||||
let showAppsHovered =
|
||||
this._showAppsIcon.actor.contains(dragEvent.targetActor);
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
let srcIsFavorite = (id in favorites);
|
||||
|
||||
if (srcIsFavorite &&
|
||||
dragEvent.source.actor &&
|
||||
this.actor.contains (dragEvent.source.actor) &&
|
||||
this._favRemoveTarget == null) {
|
||||
this._favRemoveTarget = new RemoveFavoriteIcon();
|
||||
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
||||
this._box.add(this._favRemoveTarget.actor);
|
||||
this._adjustIconSize();
|
||||
this._favRemoveTarget.animateIn();
|
||||
}
|
||||
|
||||
let favRemoveHovered = false;
|
||||
if (this._favRemoveTarget)
|
||||
favRemoveHovered =
|
||||
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
|
||||
|
||||
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
|
||||
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
|
||||
this._clearDragPlaceholder();
|
||||
|
||||
if (this._favRemoveTarget)
|
||||
this._favRemoveTarget.setHover(favRemoveHovered);
|
||||
if (showAppsHovered)
|
||||
this._showAppsIcon.setDragApp(app);
|
||||
else
|
||||
this._showAppsIcon.setDragApp(null);
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
@ -422,34 +479,63 @@ const Dash = new Lang.Class({
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_hookUpLabel: function(item) {
|
||||
item.child.connect('notify::hover', Lang.bind(this, function() {
|
||||
this._onHover(item);
|
||||
}));
|
||||
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
this._labelShowing = false;
|
||||
item.hideLabel();
|
||||
}));
|
||||
},
|
||||
|
||||
_createAppItem: function(app) {
|
||||
let display = new AppDisplay.AppWellIcon(app,
|
||||
let appIcon = new AppDisplay.AppWellIcon(app,
|
||||
{ setSizeManually: true,
|
||||
showLabel: false });
|
||||
display._draggable.connect('drag-begin',
|
||||
appIcon._draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
display.actor.opacity = 50;
|
||||
appIcon.actor.opacity = 50;
|
||||
}));
|
||||
display._draggable.connect('drag-end',
|
||||
appIcon._draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
display.actor.opacity = 255;
|
||||
appIcon.actor.opacity = 255;
|
||||
}));
|
||||
appIcon.connect('menu-state-changed',
|
||||
Lang.bind(this, function(appIcon, opened) {
|
||||
this._itemMenuStateChanged(item, opened);
|
||||
}));
|
||||
|
||||
let item = new DashItemContainer();
|
||||
item.setChild(display.actor);
|
||||
item.setChild(appIcon.actor);
|
||||
|
||||
// Override default AppWellIcon label_actor, now the
|
||||
// accessible_name is set at DashItemContainer.setLabelText
|
||||
appIcon.actor.label_actor = null;
|
||||
item.setLabelText(app.get_name());
|
||||
|
||||
display.icon.setIconSize(this.iconSize);
|
||||
display.actor.connect('notify::hover',
|
||||
Lang.bind(this, function() {
|
||||
this._onHover(item, display)
|
||||
}));
|
||||
appIcon.icon.setIconSize(this.iconSize);
|
||||
this._hookUpLabel(item);
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
_onHover: function (item, display) {
|
||||
if (display.actor.get_hover() && !display.isMenuUp) {
|
||||
_itemMenuStateChanged: function(item, opened) {
|
||||
// When the menu closes, it calls sync_hover, which means
|
||||
// that the notify::hover handler does everything we need to.
|
||||
if (opened) {
|
||||
if (this._showLabelTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._showLabelTimeoutId);
|
||||
this._showLabelTimeoutId = 0;
|
||||
}
|
||||
|
||||
item.hideLabel();
|
||||
}
|
||||
},
|
||||
|
||||
_onHover: function (item) {
|
||||
if (item.child.get_hover()) {
|
||||
if (this._showLabelTimeoutId == 0) {
|
||||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||
@ -490,18 +576,12 @@ const Dash = new Lang.Class({
|
||||
!actor._delegate.animatingOut;
|
||||
});
|
||||
|
||||
if (iconChildren.length == 0) {
|
||||
this._box.add_style_pseudo_class('empty');
|
||||
return;
|
||||
}
|
||||
|
||||
this._box.remove_style_pseudo_class('empty');
|
||||
iconChildren.push(this._showAppsIcon.actor);
|
||||
|
||||
if (this._maxHeight == -1)
|
||||
return;
|
||||
|
||||
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let themeNode = this._container.get_theme_node();
|
||||
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
|
||||
x2: 42 /* whatever */,
|
||||
y2: this._maxHeight });
|
||||
@ -527,7 +607,6 @@ const Dash = new Lang.Class({
|
||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
||||
}
|
||||
|
||||
|
||||
// Subtract icon padding and box spacing from the available height
|
||||
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
||||
(iconChildren.length - 1) * spacing;
|
||||
@ -558,8 +637,10 @@ const Dash = new Lang.Class({
|
||||
icon.setIconSize(this.iconSize);
|
||||
|
||||
// Don't animate the icon size change when the overview
|
||||
// is not visible or when initially filling the dash
|
||||
if (!Main.overview.visible || !this._shownInitially)
|
||||
// is transitioning, not visible or when initially filling
|
||||
// the dash
|
||||
if (!Main.overview.visible || Main.overview.animationInProgress ||
|
||||
!this._shownInitially)
|
||||
continue;
|
||||
|
||||
let [targetWidth, targetHeight] = icon.icon.get_size();
|
||||
@ -677,17 +758,18 @@ const Dash = new Lang.Class({
|
||||
}
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++)
|
||||
this._box.insert_actor(addedItems[i].item.actor,
|
||||
this._box.insert_child_at_index(addedItems[i].item.actor,
|
||||
addedItems[i].pos);
|
||||
|
||||
for (let i = 0; i < removedActors.length; i++) {
|
||||
let item = removedActors[i]._delegate;
|
||||
|
||||
// Don't animate item removal when the overview is hidden
|
||||
if (Main.overview.visible)
|
||||
// Don't animate item removal when the overview is transitioning
|
||||
// or hidden
|
||||
if (Main.overview.visible && !Main.overview.animationInProgress)
|
||||
item.animateOutAndDestroy();
|
||||
else
|
||||
item.actor.destroy();
|
||||
item.destroy();
|
||||
}
|
||||
|
||||
this._adjustIconSize();
|
||||
@ -699,8 +781,9 @@ const Dash = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't animate item addition when the overview is hidden
|
||||
if (!Main.overview.visible)
|
||||
// Don't animate item addition when the overview is transitioning
|
||||
// or hidden
|
||||
if (!Main.overview.visible || Main.overview.animationInProgress)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++)
|
||||
@ -716,11 +799,7 @@ const Dash = new Lang.Class({
|
||||
},
|
||||
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.lookup_app(source.getId());
|
||||
else if (source.metaWindow)
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
let app = getAppFromSource(source);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_window_backed())
|
||||
@ -743,20 +822,10 @@ const Dash = new Lang.Class({
|
||||
numChildren--;
|
||||
}
|
||||
|
||||
let pos = Math.round(y * numChildren / boxHeight);
|
||||
let pos = Math.floor(y * numChildren / boxHeight);
|
||||
|
||||
if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
|
||||
if (this._animatingPlaceholdersCount > 0) {
|
||||
let appChildren = children.filter(function(actor) {
|
||||
return actor._delegate &&
|
||||
actor._delegate.child &&
|
||||
actor._delegate.child._delegate &&
|
||||
actor._delegate.child._delegate.app;
|
||||
});
|
||||
this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
|
||||
} else {
|
||||
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
||||
this._dragPlaceholderPos = pos;
|
||||
}
|
||||
|
||||
// Don't allow positioning before or after self
|
||||
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
||||
@ -787,12 +856,20 @@ const Dash = new Lang.Class({
|
||||
this._dragPlaceholder = new DragPlaceholderItem();
|
||||
this._dragPlaceholder.child.set_width (this.iconSize);
|
||||
this._dragPlaceholder.child.set_height (this.iconSize / 2);
|
||||
this._box.insert_actor(this._dragPlaceholder.actor,
|
||||
this._box.insert_child_at_index(this._dragPlaceholder.actor,
|
||||
this._dragPlaceholderPos);
|
||||
if (fadeIn)
|
||||
this._dragPlaceholder.animateIn();
|
||||
}
|
||||
|
||||
// Remove the drag placeholder if we are not in the
|
||||
// "favorites zone"
|
||||
if (pos > numFavorites && this._dragPlaceholder) {
|
||||
this._clearDragPlaceholder();
|
||||
}
|
||||
if (!this._dragPlaceholder)
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let srcIsFavorite = (favPos != -1);
|
||||
|
||||
if (srcIsFavorite)
|
||||
@ -803,12 +880,7 @@ const Dash = new Lang.Class({
|
||||
|
||||
// Draggable target interface
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
app = this._appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
let app = getAppFromSource(source);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_window_backed()) {
|
||||
@ -835,6 +907,11 @@ const Dash = new Lang.Class({
|
||||
favPos++;
|
||||
}
|
||||
|
||||
// No drag placeholder means we don't wan't to favorite the app
|
||||
// and we are dragging it to its original position
|
||||
if (!this._dragPlaceholder)
|
||||
return true;
|
||||
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||
function () {
|
||||
let appFavorites = AppFavorites.getAppFavorites();
|
||||
|
@ -2,12 +2,14 @@
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
@ -15,14 +17,6 @@ const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
// in org.gnome.shell.clock
|
||||
const CLOCK_SHOW_DATE_KEY = 'show-date';
|
||||
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
function _onVertSepRepaint (area)
|
||||
{
|
||||
@ -44,20 +38,23 @@ const DateMenuButton = new Lang.Class({
|
||||
Name: 'DateMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { showEvents: true });
|
||||
|
||||
_init: function() {
|
||||
let item;
|
||||
let hbox;
|
||||
let vbox;
|
||||
|
||||
let menuAlignment = 0.25;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
menuAlignment = 1.0 - menuAlignment;
|
||||
this.parent(menuAlignment);
|
||||
|
||||
this._clock = new St.Label();
|
||||
this.actor.add_actor(this._clock);
|
||||
// At this moment calendar menu is not keyboard navigable at
|
||||
// all (so not accessible), so it doesn't make sense to set as
|
||||
// role ATK_ROLE_MENU like other elements of the panel.
|
||||
this.actor.accessible_role = Atk.Role.LABEL;
|
||||
|
||||
this._clockDisplay = new St.Label();
|
||||
this.actor.add_actor(this._clockDisplay);
|
||||
|
||||
hbox = new St.BoxLayout({name: 'calendarArea' });
|
||||
this.menu.addActor(hbox);
|
||||
@ -69,19 +66,12 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
// Date
|
||||
this._date = new St.Label();
|
||||
this.actor.label_actor = this._clockDisplay;
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
if (params.showEvents) {
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
} else {
|
||||
this._eventSource = null;
|
||||
this._eventList = null;
|
||||
}
|
||||
|
||||
// Calendar
|
||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
||||
this._eventList = new Calendar.EventsList();
|
||||
this._calendar = new Calendar.Calendar();
|
||||
|
||||
this._calendar.connect('selected-date-changed',
|
||||
Lang.bind(this, function(calendar, date) {
|
||||
@ -99,17 +89,16 @@ const DateMenuButton = new Lang.Class({
|
||||
separator.setColumnWidths(1);
|
||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
item.actor.show_on_set_parent = false;
|
||||
item.actor.can_focus = false;
|
||||
item.actor.reparent(vbox);
|
||||
this._dateAndTimeSeparator = separator;
|
||||
}
|
||||
|
||||
if (params.showEvents) {
|
||||
// Add vertical separator
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(this._separator);
|
||||
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
|
||||
@ -119,11 +108,15 @@ const DateMenuButton = new Lang.Class({
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
}
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
this._calendarSettings.connect('changed::exec',
|
||||
Lang.bind(this, this._calendarSettingsChanged));
|
||||
this._calendarSettingsChanged();
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
@ -150,79 +143,75 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
// Track changes to clock settings
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// Start the clock
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
||||
this._updateClockAndDate();
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_calendarSettingsChanged: function() {
|
||||
let exec = this._calendarSettings.get_string('exec');
|
||||
let fullExec = GLib.find_program_in_path(exec);
|
||||
this._openCalendarItem.actor.visible = fullExec != null;
|
||||
},
|
||||
|
||||
_setEventsVisibility: function(visible) {
|
||||
this._openCalendarItem.actor.visible = visible;
|
||||
this._separator.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;
|
||||
this._eventList.actor.get_parent().show();
|
||||
} else {
|
||||
this.menu._arrowAlignment = 0.5;
|
||||
this._eventList.actor.get_parent().hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setEventSource: function(eventSource) {
|
||||
this._calendar.setEventSource(eventSource);
|
||||
this._eventList.setEventSource(eventSource);
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let eventSource;
|
||||
let showEvents = Main.sessionMode.showCalendarEvents;
|
||||
if (showEvents) {
|
||||
eventSource = new Calendar.DBusEventSource();
|
||||
} else {
|
||||
eventSource = null;
|
||||
}
|
||||
this._setEventSource(eventSource);
|
||||
this._setEventsVisibility(showEvents);
|
||||
|
||||
// This needs to be handled manually, as the code to
|
||||
// autohide separators doesn't work across the vbox
|
||||
this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings;
|
||||
},
|
||||
|
||||
_updateClockAndDate: function() {
|
||||
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
|
||||
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
|
||||
|
||||
let clockFormat;
|
||||
let dateFormat;
|
||||
|
||||
switch (format) {
|
||||
case '24h':
|
||||
if (showDate)
|
||||
/* Translators: This is the time format with date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||
: _("%a %b %e, %R");
|
||||
else
|
||||
/* Translators: This is the time format without date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %R:%S")
|
||||
: _("%a %R");
|
||||
break;
|
||||
case '12h':
|
||||
default:
|
||||
if (showDate)
|
||||
/* Translators: This is a time format with date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||
: _("%a %b %e, %l:%M %p");
|
||||
else
|
||||
/* Translators: This is a time format without date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||
: _("%a %l:%M %p");
|
||||
break;
|
||||
}
|
||||
|
||||
let displayDate = new Date();
|
||||
|
||||
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
|
||||
|
||||
this._clockDisplay.set_text(this._clock.clock);
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
dateFormat = _("%A %B %e, %Y");
|
||||
let dateFormat = _("%A %B %e, %Y");
|
||||
let displayDate = new Date();
|
||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||
|
||||
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
|
||||
return false;
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
let tool = calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool == 'evolution') {
|
||||
let tool = this._calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
Util.spawn(['evolution', '-c', 'calendar']);
|
||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||
app.activate();
|
||||
} else {
|
||||
let needTerm = calendarSettings.get_boolean('needs-term');
|
||||
let needTerm = this._calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||
let term = terminalSettings.get_string('exec');
|
||||
|
41
js/ui/dnd.js
@ -103,8 +103,8 @@ const _Draggable = new Lang.Class({
|
||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||
|
||||
// During the drag, we eat enter/leave events so that actors don't prelight or show
|
||||
// tooltips. But we remember the actors that we first left/last entered so we can
|
||||
// During the drag, we eat enter/leave events so that actors don't prelight.
|
||||
// But we remember the actors that we first left/last entered so we can
|
||||
// fix up the hover state after the drag ends.
|
||||
this._firstLeaveActor = null;
|
||||
this._lastEnterActor = null;
|
||||
@ -120,12 +120,6 @@ const _Draggable = new Lang.Class({
|
||||
return false;
|
||||
|
||||
this._buttonDown = true;
|
||||
// special case St.Button: grabbing the pointer would mess up the
|
||||
// internal state, so we start the drag manually on hover change
|
||||
if (this.actor instanceof St.Button)
|
||||
this.actor.connect('notify::hover',
|
||||
Lang.bind(this, this._onButtonHoverChanged));
|
||||
else
|
||||
this._grabActor();
|
||||
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
@ -135,15 +129,6 @@ const _Draggable = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonHoverChanged: function(button) {
|
||||
if (button.hover || !button.pressed)
|
||||
return;
|
||||
|
||||
button.fake_release();
|
||||
this.startDrag(this._dragStartX, this._dragStartY,
|
||||
global.get_current_time());
|
||||
},
|
||||
|
||||
_grabActor: function() {
|
||||
Clutter.grab_pointer(this.actor);
|
||||
this._onEventId = this.actor.connect('event',
|
||||
@ -232,6 +217,13 @@ const _Draggable = new Lang.Class({
|
||||
currentDraggable = this;
|
||||
this._dragInProgress = true;
|
||||
|
||||
// Special-case St.Button: the pointer grab messes with the internal
|
||||
// state, so force a reset to a reasonable state here
|
||||
if (this.actor instanceof St.Button) {
|
||||
this.actor.fake_release();
|
||||
this.actor.hover = false;
|
||||
}
|
||||
|
||||
this.emit('drag-begin', time);
|
||||
if (this._onEventId)
|
||||
this._ungrabActor();
|
||||
@ -242,7 +234,11 @@ const _Draggable = new Lang.Class({
|
||||
this._dragY = this._dragStartY = stageY;
|
||||
|
||||
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
||||
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
|
||||
this._dragActor = this.actor._delegate.getDragActor();
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
||||
// Drag actor does not always have to be the same as actor. For example drag actor
|
||||
// can be an image that's part of the actor. So to perform "snap back" correctly we need
|
||||
// to know what was the drag actor source.
|
||||
@ -271,12 +267,17 @@ const _Draggable = new Lang.Class({
|
||||
this._dragOffsetY = this._dragActor.y - this._dragStartY;
|
||||
} else {
|
||||
this._dragActor = this.actor;
|
||||
|
||||
this._dragActorSource = undefined;
|
||||
this._dragOrigParent = this.actor.get_parent();
|
||||
this._dragOrigX = this._dragActor.x;
|
||||
this._dragOrigY = this._dragActor.y;
|
||||
this._dragOrigScale = this._dragActor.scale_x;
|
||||
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
||||
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
|
||||
this._dragOffsetX = actorStageX - this._dragStartX;
|
||||
this._dragOffsetY = actorStageY - this._dragStartY;
|
||||
@ -288,10 +289,6 @@ const _Draggable = new Lang.Class({
|
||||
scaledHeight / this.actor.height);
|
||||
}
|
||||
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
||||
this._dragOrigOpacity = this._dragActor.opacity;
|
||||
if (this._dragActorOpacity != undefined)
|
||||
this._dragActor.opacity = this._dragActorOpacity;
|
||||
|
@ -1,44 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const DocInfo = imports.misc.docInfo;
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const DocSearchProvider = new Lang.Class({
|
||||
Name: 'DocSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function(name) {
|
||||
this.parent(_("RECENT ITEMS"));
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
},
|
||||
|
||||
getResultMeta: function(resultId) {
|
||||
let docInfo = this._docManager.lookupByUri(resultId);
|
||||
if (!docInfo)
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': docInfo.name,
|
||||
'createIcon': function(size) {
|
||||
return docInfo.createIcon(size);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let docInfo = this._docManager.lookupByUri(id);
|
||||
docInfo.launch(params.workspace);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._docManager.initialSearch(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._docManager.subsearch(previousResults, terms);
|
||||
}
|
||||
});
|
@ -31,10 +31,10 @@ const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
|
||||
let _endSessionDialog = null;
|
||||
|
||||
@ -90,7 +90,7 @@ const shutdownDialogContent = {
|
||||
label: C_("button", "Restart") },
|
||||
{ signal: 'ConfirmedShutdown',
|
||||
label: C_("button", "Power Off") }],
|
||||
iconName: 'system-shutdown',
|
||||
iconName: 'system-shutdown-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
};
|
||||
|
||||
@ -105,7 +105,7 @@ const restartDialogContent = {
|
||||
endDescription: _("Restarting the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart") }],
|
||||
iconName: 'system-shutdown',
|
||||
iconName: 'view-refresh-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
};
|
||||
|
||||
@ -116,30 +116,12 @@ const DialogContent = {
|
||||
};
|
||||
|
||||
function findAppFromInhibitor(inhibitor) {
|
||||
let desktopFile = inhibitor.app_id;
|
||||
let [desktopFile] = inhibitor.GetAppIdSync();
|
||||
|
||||
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
|
||||
desktopFile += '.desktop';
|
||||
|
||||
let candidateDesktopFiles = [];
|
||||
|
||||
candidateDesktopFiles.push(desktopFile);
|
||||
candidateDesktopFiles.push('gnome-' + desktopFile);
|
||||
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let app = null;
|
||||
for (let i = 0; i < candidateDesktopFiles.length; i++) {
|
||||
try {
|
||||
app = appSystem.lookup_app(candidateDesktopFiles[i]);
|
||||
|
||||
if (app)
|
||||
break;
|
||||
} catch(e) {
|
||||
// ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
|
||||
}
|
||||
|
||||
const ListItem = new Lang.Class({
|
||||
@ -179,6 +161,7 @@ const ListItem = new Lang.Class({
|
||||
|
||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
||||
style_class: 'end-session-dialog-app-list-item-description' });
|
||||
this.actor.label_actor = this._nameLabel;
|
||||
textLayout.add(this._descriptionLabel,
|
||||
{ expand: true,
|
||||
x_fill: true });
|
||||
@ -298,21 +281,17 @@ const EndSessionDialog = new Lang.Class({
|
||||
scrollView.hide();
|
||||
|
||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(this._applicationList,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
scrollView.add_actor(this._applicationList);
|
||||
|
||||
this._applicationList.connect('actor-added',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 1)
|
||||
if (this._applicationList.get_n_children() == 1)
|
||||
scrollView.show();
|
||||
}));
|
||||
|
||||
this._applicationList.connect('actor-removed',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 0)
|
||||
if (this._applicationList.get_n_children() == 0)
|
||||
scrollView.hide();
|
||||
}));
|
||||
|
||||
@ -325,42 +304,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_setIconFromFile: function(iconFile, styleClass) {
|
||||
if (styleClass)
|
||||
this._iconBin.set_style_class_name(styleClass);
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
this._iconBin.child = null;
|
||||
if (iconFile) {
|
||||
this._iconBin.show();
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
||||
'background-size: contain;');
|
||||
} else {
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setIconFromName: function(iconName, styleClass) {
|
||||
if (styleClass)
|
||||
this._iconBin.set_style_class_name(styleClass);
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
if (iconName != null) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
||||
iconName,
|
||||
St.IconType.SYMBOLIC,
|
||||
_DIALOG_ICON_SIZE);
|
||||
|
||||
this._iconBin.child = icon;
|
||||
this._iconBin.show();
|
||||
} else {
|
||||
this._iconBin.child = null;
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateContent: function() {
|
||||
_updateDescription: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
@ -370,17 +314,6 @@ const EndSessionDialog = new Lang.Class({
|
||||
let subject = dialogContent.subject;
|
||||
let description;
|
||||
|
||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
||||
else
|
||||
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
|
||||
} else if (dialogContent.iconName) {
|
||||
this._setIconFromName(dialogContent.iconName,
|
||||
dialogContent.iconStyleClass);
|
||||
}
|
||||
|
||||
if (this._inhibitors.length > 0) {
|
||||
this._stopTimer();
|
||||
description = dialogContent.inhibitedDescription;
|
||||
@ -413,6 +346,27 @@ const EndSessionDialog = new Lang.Class({
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
},
|
||||
|
||||
_updateContent: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
if (dialogContent.iconName) {
|
||||
this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName,
|
||||
icon_size: _DIALOG_ICON_SIZE,
|
||||
style_class: dialogContent.iconStyleClass });
|
||||
} else {
|
||||
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
|
||||
{ iconSize: _DIALOG_ICON_SIZE,
|
||||
styleClass: dialogContent.iconStyleClass });
|
||||
this._iconBin.child = avatarWidget.actor;
|
||||
avatarWidget.update();
|
||||
}
|
||||
|
||||
this._updateDescription();
|
||||
},
|
||||
|
||||
_updateButtons: function() {
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||
@ -459,7 +413,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
{ _secondsLeft: 0,
|
||||
time: this._secondsLeft,
|
||||
transition: 'linear',
|
||||
onUpdate: Lang.bind(this, this._updateContent),
|
||||
onUpdate: Lang.bind(this, this._updateDescription),
|
||||
onComplete: Lang.bind(this, function() {
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||
@ -482,7 +436,8 @@ const EndSessionDialog = new Lang.Class({
|
||||
let app = findAppFromInhibitor(inhibitor);
|
||||
|
||||
if (app) {
|
||||
let item = new ListItem(app, inhibitor.reason);
|
||||
let [reason] = inhibitor.GetReasonSync();
|
||||
let item = new ListItem(app, reason);
|
||||
item.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
this.close(global.get_current_time());
|
||||
@ -501,11 +456,11 @@ const EndSessionDialog = new Lang.Class({
|
||||
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._inhibitors = [];
|
||||
this._applicationList.destroy_children();
|
||||
this._applicationList.destroy_all_children();
|
||||
this._type = type;
|
||||
|
||||
if (!(this._type in DialogContent)) {
|
||||
invocation.report_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
|
||||
invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
|
||||
"Unknown dialog type requested");
|
||||
return;
|
||||
}
|
||||
@ -521,7 +476,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._updateButtons();
|
||||
|
||||
if (!this.open(timestamp)) {
|
||||
invocation.report_dbus_error('org.gnome.Shell.ModalDialog.GrabError',
|
||||
invocation.return_dbus_error('org.gnome.Shell.ModalDialog.GrabError',
|
||||
"Cannot grab pointer and keyboard");
|
||||
return;
|
||||
}
|
||||
|
@ -39,20 +39,23 @@ function _patchContainerClass(containerClass) {
|
||||
};
|
||||
}
|
||||
|
||||
function _makeLoggingFunc(func) {
|
||||
return function() {
|
||||
return func([].join.call(arguments, ', '));
|
||||
};
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Add some bindings to the global JS namespace; (gjs keeps the web
|
||||
// browser convention of having that namespace be called 'window'.)
|
||||
window.global = Shell.Global.get();
|
||||
|
||||
window.log = _makeLoggingFunc(window.log);
|
||||
|
||||
window._ = Gettext.gettext;
|
||||
window.C_ = Gettext.pgettext;
|
||||
window.ngettext = Gettext.ngettext;
|
||||
|
||||
// Set the default direction for St widgets (this needs to be done before any use of St)
|
||||
if (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL) {
|
||||
St.Widget.set_default_direction(St.TextDirection.RTL);
|
||||
}
|
||||
|
||||
// Miscellaneous monkeypatching
|
||||
_patchContainerClass(St.BoxLayout);
|
||||
_patchContainerClass(St.Table);
|
||||
@ -64,10 +67,14 @@ function init() {
|
||||
let origToString = Object.prototype.toString;
|
||||
Object.prototype.toString = function() {
|
||||
let base = origToString.call(this);
|
||||
try {
|
||||
if ('actor' in this && this.actor instanceof Clutter.Actor)
|
||||
return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1));
|
||||
else
|
||||
return base;
|
||||
} catch(e) {
|
||||
return base;
|
||||
}
|
||||
};
|
||||
|
||||
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
|
||||
@ -83,7 +90,7 @@ function init() {
|
||||
}
|
||||
|
||||
// OK, now things are initialized enough that we can import shell JS
|
||||
const Format = imports.misc.format;
|
||||
const Format = imports.format;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
Tweener.init();
|
||||
|
270
js/ui/extensionDownloader.js
Normal file
@ -0,0 +1,270 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Soup = imports.gi.Soup;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const _signals = ExtensionSystem._signals;
|
||||
|
||||
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
||||
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
||||
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||
const REPOSITORY_URL_UPDATE = REPOSITORY_URL_BASE + '/update-info/';
|
||||
|
||||
let _httpSession;
|
||||
|
||||
function installExtension(uuid, invocation) {
|
||||
let params = { uuid: uuid,
|
||||
shell_version: Config.PACKAGE_VERSION };
|
||||
|
||||
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
|
||||
|
||||
_httpSession.queue_message(message, function(session, message) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code);
|
||||
invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let info;
|
||||
try {
|
||||
info = JSON.parse(message.response_body.data);
|
||||
} catch (e) {
|
||||
ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e);
|
||||
invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let dialog = new InstallExtensionDialog(uuid, info, invocation);
|
||||
dialog.open(global.get_current_time());
|
||||
});
|
||||
}
|
||||
|
||||
function uninstallExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
// Don't try to uninstall system extensions
|
||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||
return false;
|
||||
|
||||
if (!ExtensionSystem.unloadExtension(extension))
|
||||
return false;
|
||||
|
||||
FileUtils.recursivelyDeleteDir(extension.dir, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
errback('DownloadExtensionError', message.status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!dir.query_exists(null))
|
||||
dir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
errback('CreateExtensionDirectoryError', e);
|
||||
return;
|
||||
}
|
||||
|
||||
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
|
||||
let contents = message.response_body.flatten().get_as_bytes();
|
||||
stream.output_stream.write_bytes(contents, null);
|
||||
stream.close(null);
|
||||
let [success, pid] = GLib.spawn_async(null,
|
||||
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
|
||||
null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
|
||||
if (!success) {
|
||||
errback('ExtractExtensionError');
|
||||
return;
|
||||
}
|
||||
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||
GLib.spawn_close_pid(pid);
|
||||
|
||||
if (status != 0)
|
||||
errback('ExtractExtensionError');
|
||||
else
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function updateExtension(uuid) {
|
||||
// This gets a bit tricky. We want the update to be seamless -
|
||||
// if we have any error during downloading or extracting, we
|
||||
// want to not unload the current version.
|
||||
|
||||
let oldExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
|
||||
let newExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
|
||||
|
||||
let params = { shell_version: Config.PACKAGE_VERSION };
|
||||
|
||||
let url = REPOSITORY_URL_DOWNLOAD.format(uuid);
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
|
||||
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, function() {
|
||||
let oldExtension = ExtensionUtils.extensions[uuid];
|
||||
let extensionDir = oldExtension.dir;
|
||||
|
||||
if (!ExtensionSystem.unloadExtension(oldExtension))
|
||||
return;
|
||||
|
||||
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
|
||||
FileUtils.recursivelyMoveDir(newExtensionTmpDir, extensionDir);
|
||||
|
||||
let extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
|
||||
|
||||
try {
|
||||
ExtensionSystem.loadExtension(extension);
|
||||
} catch(e) {
|
||||
ExtensionSystem.unloadExtension(extension);
|
||||
|
||||
logError(e, 'Error loading extension %s'.format(uuid));
|
||||
|
||||
FileUtils.recursivelyDeleteDir(extensionDir, false);
|
||||
FileUtils.recursivelyMoveDir(oldExtensionTmpDir, extensionDir);
|
||||
|
||||
// Restore what was there before. We can't do much if we
|
||||
// fail here.
|
||||
ExtensionSystem.loadExtension(oldExtension);
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true);
|
||||
}, function(code, message) {
|
||||
log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : ''));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function checkForUpdates() {
|
||||
let metadatas = {};
|
||||
for (let uuid in ExtensionUtils.extensions) {
|
||||
metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata;
|
||||
}
|
||||
|
||||
let params = { shell_version: Config.PACKAGE_VERSION,
|
||||
installed: JSON.stringify(metadatas) };
|
||||
|
||||
let url = REPOSITORY_URL_UPDATE;
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
_httpSession.queue_message(message, function(session, message) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK)
|
||||
return;
|
||||
|
||||
let operations = JSON.parse(message.response_body.data);
|
||||
for (let uuid in operations) {
|
||||
let operation = operations[uuid];
|
||||
if (operation == 'blacklist')
|
||||
uninstallExtension(uuid);
|
||||
else if (operation == 'upgrade' || operation == 'downgrade')
|
||||
updateExtension(uuid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const InstallExtensionDialog = new Lang.Class({
|
||||
Name: 'InstallExtensionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(uuid, info, invocation) {
|
||||
this.parent({ styleClass: 'extension-dialog' });
|
||||
|
||||
this._uuid = uuid;
|
||||
this._info = info;
|
||||
this._invocation = invocation;
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this._onCancelButtonPressed),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Install"),
|
||||
action: Lang.bind(this, this._onInstallButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
|
||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
|
||||
|
||||
let box = new St.BoxLayout();
|
||||
this.contentLayout.add(box);
|
||||
|
||||
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
|
||||
let icon = new St.Icon({ gicon: gicon });
|
||||
box.add(icon);
|
||||
|
||||
let label = new St.Label({ text: message });
|
||||
box.add(label);
|
||||
},
|
||||
|
||||
_onCancelButtonPressed: function(button, event) {
|
||||
this.close(global.get_current_time());
|
||||
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||
},
|
||||
|
||||
_onInstallButtonPressed: function(button, event) {
|
||||
let params = { shell_version: Config.PACKAGE_VERSION };
|
||||
|
||||
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
|
||||
let uuid = this._uuid;
|
||||
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
|
||||
let invocation = this._invocation;
|
||||
function errback(code, message) {
|
||||
invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
|
||||
}
|
||||
|
||||
function callback() {
|
||||
// Add extension to 'enabled-extensions' for the user, always...
|
||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1) {
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
}
|
||||
|
||||
let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
|
||||
|
||||
try {
|
||||
ExtensionSystem.loadExtension(extension);
|
||||
} catch(e) {
|
||||
uninstallExtension(uuid);
|
||||
errback('LoadExtensionError', e);
|
||||
return;
|
||||
}
|
||||
|
||||
invocation.return_value(GLib.Variant.new('(s)', 'successful'));
|
||||
}
|
||||
|
||||
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
||||
}));
|
||||
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
||||
function init() {
|
||||
_httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
|
||||
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
|
||||
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||
}
|
@ -3,18 +3,12 @@
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Soup = imports.gi.Soup;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const API_VERSION = 1;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
@ -29,47 +23,10 @@ const ExtensionState = {
|
||||
UNINSTALLED: 99
|
||||
};
|
||||
|
||||
const ExtensionType = {
|
||||
SYSTEM: 1,
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
||||
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
||||
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||
|
||||
const _httpSession = new Soup.SessionAsync();
|
||||
|
||||
// The unfortunate state of gjs, gobject-introspection and libsoup
|
||||
// means that I have to do a hack to add a feature.
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||
|
||||
if (Soup.Session.prototype.add_feature != null)
|
||||
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||
|
||||
function _getCertFile() {
|
||||
let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']);
|
||||
if (GLib.file_test(localCert, GLib.FileTest.EXISTS))
|
||||
return localCert;
|
||||
else
|
||||
return Config.SHELL_SYSTEM_CA_FILE;
|
||||
}
|
||||
|
||||
_httpSession.ssl_ca_file = _getCertFile();
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensionMeta = {};
|
||||
// Maps uuid -> importer object (extension directory tree)
|
||||
const extensions = {};
|
||||
// Maps uuid -> extension state object (returned from init())
|
||||
const extensionStateObjs = {};
|
||||
// Contains the order that extensions were enabled in.
|
||||
const extensionOrder = [];
|
||||
|
||||
// Arrays of uuids
|
||||
var enabledExtensions;
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
// Contains the order that extensions were enabled in.
|
||||
const extensionOrder = [];
|
||||
|
||||
// We don't really have a class to add signals on. So, create
|
||||
// a simple dummy object, add the signal methods, and export those
|
||||
@ -80,142 +37,19 @@ Signals.addSignalMethods(_signals);
|
||||
const connect = Lang.bind(_signals, _signals.connect);
|
||||
const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
// UUID => Array of error messages
|
||||
var errors = {};
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
* @current: the version we have
|
||||
*
|
||||
* Check if a component is compatible for an extension.
|
||||
* @required is an array, and at least one version must match.
|
||||
* @current must be in the format <major>.<minor>.<point>.<micro>
|
||||
* <micro> is always ignored
|
||||
* <point> is ignored if <minor> is even (so you can target the
|
||||
* whole stable release)
|
||||
* <minor> and <major> must match
|
||||
* Each target version must be at least <major> and <minor>
|
||||
*/
|
||||
function versionCheck(required, current) {
|
||||
let currentArray = current.split('.');
|
||||
let major = currentArray[0];
|
||||
let minor = currentArray[1];
|
||||
let point = currentArray[2];
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
let requiredArray = required[i].split('.');
|
||||
if (requiredArray[0] == major &&
|
||||
requiredArray[1] == minor &&
|
||||
(requiredArray[2] == point ||
|
||||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function installExtensionFromUUID(uuid, version_tag) {
|
||||
let params = { uuid: uuid,
|
||||
version_tag: version_tag,
|
||||
shell_version: Config.PACKAGE_VERSION,
|
||||
api_version: API_VERSION.toString() };
|
||||
|
||||
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
|
||||
|
||||
_httpSession.queue_message(message,
|
||||
function(session, message) {
|
||||
let info = JSON.parse(message.response_body.data);
|
||||
let dialog = new InstallExtensionDialog(uuid, version_tag, info.name);
|
||||
dialog.open(global.get_current_time());
|
||||
});
|
||||
}
|
||||
|
||||
function uninstallExtensionFromUUID(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
return false;
|
||||
|
||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||
// but it will be removed on next reboot, and hopefully nothing
|
||||
// broke too much.
|
||||
disableExtension(uuid);
|
||||
|
||||
// Don't try to uninstall system extensions
|
||||
if (meta.type != ExtensionType.PER_USER)
|
||||
return false;
|
||||
|
||||
meta.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
|
||||
delete extensionMeta[uuid];
|
||||
|
||||
// Importers are marked as PERMANENT, so we can't do this.
|
||||
// delete extensions[uuid];
|
||||
extensions[uuid] = undefined;
|
||||
|
||||
delete extensionStateObjs[uuid];
|
||||
delete errors[uuid];
|
||||
|
||||
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(meta.path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function gotExtensionZipFile(session, message, uuid) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: use a GFile mkstemp-type method once one exists
|
||||
let fd, tmpzip;
|
||||
try {
|
||||
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'tempfile: ' + e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let stream = new Gio.UnixOutputStream({ fd: fd });
|
||||
let dir = userExtensionsDir.get_child(uuid);
|
||||
Shell.write_soup_message_to_stream(stream, message);
|
||||
stream.close(null);
|
||||
let [success, pid] = GLib.spawn_async(null,
|
||||
['unzip', '-uod', dir.get_path(), '--', tmpzip],
|
||||
null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
|
||||
if (!success) {
|
||||
logExtensionError(uuid, 'extract: could not extract');
|
||||
return;
|
||||
}
|
||||
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||
GLib.spawn_close_pid(pid);
|
||||
|
||||
// Add extension to 'enabled-extensions' for the user, always...
|
||||
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1) {
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
}
|
||||
|
||||
loadExtension(dir, ExtensionType.PER_USER, true);
|
||||
});
|
||||
}
|
||||
var initted = false;
|
||||
var enabled;
|
||||
|
||||
function disableExtension(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
if (meta.state != ExtensionState.ENABLED)
|
||||
if (extension.state != ExtensionState.ENABLED)
|
||||
return;
|
||||
|
||||
let extensionState = extensionStateObjs[uuid];
|
||||
|
||||
// "Rebase" the extension order by disabling and then enabling extensions
|
||||
// in order to help prevent conflicts.
|
||||
|
||||
@ -231,217 +65,177 @@ function disableExtension(uuid) {
|
||||
for (let i = 0; i < orderReversed.length; i++) {
|
||||
let uuid = orderReversed[i];
|
||||
try {
|
||||
extensionStateObjs[uuid].disable();
|
||||
ExtensionUtils.extensions[uuid].stateObj.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
extensionState.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
if (extension.stylesheet) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||
}
|
||||
|
||||
extension.stateObj.disable();
|
||||
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
let uuid = order[i];
|
||||
try {
|
||||
extensionStateObjs[uuid].enable();
|
||||
ExtensionUtils.extensions[uuid].stateObj.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
extensionOrder.splice(orderIdx, 1);
|
||||
|
||||
meta.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function enableExtension(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
if (meta.state == ExtensionState.INITIALIZED) {
|
||||
loadExtension(meta.dir, meta.type, true);
|
||||
return;
|
||||
}
|
||||
if (extension.state == ExtensionState.INITIALIZED)
|
||||
initExtension(uuid);
|
||||
|
||||
if (meta.state != ExtensionState.DISABLED)
|
||||
if (extension.state != ExtensionState.DISABLED)
|
||||
return;
|
||||
|
||||
let extensionState = extensionStateObjs[uuid];
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
try {
|
||||
extensionState.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
extension.stylesheet = stylesheetFile;
|
||||
}
|
||||
|
||||
extension.stateObj.enable();
|
||||
|
||||
extension.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function logExtensionError(uuid, error) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
}
|
||||
let message = '' + error;
|
||||
|
||||
function logExtensionError(uuid, message, state) {
|
||||
if (!errors[uuid]) errors[uuid] = [];
|
||||
errors[uuid].push(message);
|
||||
global.logError('Extension "%s" had error: %s'.format(uuid, message));
|
||||
state = state || ExtensionState.ERROR;
|
||||
extension.state = ExtensionState.ERROR;
|
||||
if (!extension.errors)
|
||||
extension.errors = [];
|
||||
extension.errors.push(message);
|
||||
|
||||
log('Extension "%s" had error: %s'.format(uuid, message));
|
||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||
error: message,
|
||||
state: state });
|
||||
state: extension.state });
|
||||
}
|
||||
|
||||
function loadExtension(dir, type, enabled) {
|
||||
let info;
|
||||
let uuid = dir.get_basename();
|
||||
|
||||
let metadataFile = dir.get_child('metadata.json');
|
||||
if (!metadataFile.query_exists(null)) {
|
||||
logExtensionError(uuid, 'Missing metadata.json');
|
||||
return;
|
||||
}
|
||||
|
||||
let metadataContents;
|
||||
try {
|
||||
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
let meta;
|
||||
try {
|
||||
meta = JSON.parse(metadataContents);
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions[uuid] != undefined) {
|
||||
logExtensionError(uuid, 'extension already loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta['url']) {
|
||||
global.log('Warning: Missing "url" property in metadata.json');
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
extensionMeta[uuid] = meta;
|
||||
meta.type = type;
|
||||
meta.dir = dir;
|
||||
meta.path = dir.get_path();
|
||||
meta.error = '';
|
||||
|
||||
function loadExtension(extension) {
|
||||
// Default to error, we set success as the last step
|
||||
meta.state = ExtensionState.ERROR;
|
||||
extension.state = ExtensionState.ERROR;
|
||||
|
||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
||||
meta.state = ExtensionState.OUT_OF_DATE;
|
||||
return;
|
||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||
extension.state = ExtensionState.OUT_OF_DATE;
|
||||
} else {
|
||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||
if (enabled) {
|
||||
initExtension(extension.uuid);
|
||||
if (extension.state == ExtensionState.DISABLED)
|
||||
enableExtension(extension.uuid);
|
||||
} else {
|
||||
extension.state = ExtensionState.INITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
meta.state = ExtensionState.INITIALIZED;
|
||||
return;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function unloadExtension(extension) {
|
||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||
// but it will be removed on next reboot, and hopefully nothing
|
||||
// broke too much.
|
||||
disableExtension(extension.uuid);
|
||||
|
||||
extension.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
|
||||
delete ExtensionUtils.extensions[extension.uuid];
|
||||
return true;
|
||||
}
|
||||
|
||||
function reloadExtension(oldExtension) {
|
||||
// Grab the things we'll need to pass to createExtensionObject
|
||||
// to reload it.
|
||||
let { uuid: uuid, dir: dir, type: type } = oldExtension;
|
||||
|
||||
// Then unload the old extension.
|
||||
unloadExtension(oldExtension);
|
||||
|
||||
// Now, recreate the extension and load it.
|
||||
let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||
loadExtension(newExtension);
|
||||
}
|
||||
|
||||
function initExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
let dir = extension.dir;
|
||||
|
||||
if (!extension)
|
||||
throw new Error("Extension was not properly created. Call loadExtension first");
|
||||
|
||||
let extensionJs = dir.get_child('extension.js');
|
||||
if (!extensionJs.query_exists(null)) {
|
||||
logExtensionError(uuid, 'Missing extension.js');
|
||||
return;
|
||||
}
|
||||
let stylesheetPath = null;
|
||||
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||
let theme = themeContext.get_theme();
|
||||
let stylesheetFile = dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
try {
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!extensionJs.query_exists(null))
|
||||
throw new Error('Missing extension.js');
|
||||
|
||||
let extensionModule;
|
||||
let extensionState = null;
|
||||
try {
|
||||
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
||||
extensionModule = extensions[meta.uuid].extension;
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
logExtensionError(uuid, e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extensionModule.init) {
|
||||
logExtensionError(uuid, 'missing \'init\' function');
|
||||
return;
|
||||
}
|
||||
ExtensionUtils.installImporter(extension);
|
||||
extensionModule = extension.imports.extension;
|
||||
|
||||
try {
|
||||
extensionState = extensionModule.init(meta);
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
|
||||
return;
|
||||
if (extensionModule.init) {
|
||||
extensionState = extensionModule.init(extension);
|
||||
}
|
||||
|
||||
if (!extensionState)
|
||||
extensionState = extensionModule;
|
||||
extensionStateObjs[uuid] = extensionState;
|
||||
extension.stateObj = extensionState;
|
||||
|
||||
if (!extensionState.enable) {
|
||||
logExtensionError(uuid, 'missing \'enable\' function');
|
||||
return;
|
||||
}
|
||||
if (!extensionState.disable) {
|
||||
logExtensionError(uuid, 'missing \'disable\' function');
|
||||
return;
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-loaded', uuid);
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.DISABLED;
|
||||
function getEnabledExtensions() {
|
||||
let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
if (!Array.isArray(Main.sessionMode.enabledExtensions))
|
||||
return extensions;
|
||||
|
||||
enableExtension(uuid);
|
||||
|
||||
_signals.emit('extension-loaded', meta.uuid);
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
global.log('Loaded extension ' + meta.uuid);
|
||||
return Main.sessionMode.enabledExtensions.concat(extensions);
|
||||
}
|
||||
|
||||
function onEnabledExtensionsChanged() {
|
||||
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
let newEnabledExtensions = getEnabledExtensions();
|
||||
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
// Find and enable all the newly enabled extensions: UUIDs found in the
|
||||
// new setting, but not in the old one.
|
||||
newEnabledExtensions.filter(function(uuid) {
|
||||
return enabledExtensions.indexOf(uuid) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
try {
|
||||
enableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||
@ -449,122 +243,74 @@ function onEnabledExtensionsChanged() {
|
||||
enabledExtensions.filter(function(item) {
|
||||
return newEnabledExtensions.indexOf(item) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
try {
|
||||
disableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
}
|
||||
|
||||
function init() {
|
||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||
try {
|
||||
if (!userExtensionsDir.query_exists(null))
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
}
|
||||
|
||||
function _loadExtensions() {
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
}
|
||||
enabledExtensions = getEnabledExtensions();
|
||||
|
||||
function _loadExtensionsIn(dir, type) {
|
||||
let fileEnum;
|
||||
let file, info;
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', function(signals, extension) {
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
loadExtension(extension);
|
||||
} catch(e) {
|
||||
global.logError('' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let name = info.get_name();
|
||||
let child = dir.get_child(name);
|
||||
let enabled = enabledExtensions.indexOf(name) != -1;
|
||||
loadExtension(child, type, enabled);
|
||||
}
|
||||
fileEnum.close(null);
|
||||
}
|
||||
|
||||
function loadExtensions() {
|
||||
let systemDataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||
let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
|
||||
let dir = Gio.file_new_for_path(dirPath);
|
||||
if (dir.query_exists(null))
|
||||
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
|
||||
}
|
||||
_loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
|
||||
}
|
||||
|
||||
const InstallExtensionDialog = new Lang.Class({
|
||||
Name: 'InstallExtensionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(uuid, version_tag, name) {
|
||||
this.parent({ styleClass: 'extension-dialog' });
|
||||
|
||||
this._uuid = uuid;
|
||||
this._version_tag = version_tag;
|
||||
this._name = name;
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this._onCancelButtonPressed),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Install"),
|
||||
action: Lang.bind(this, this._onInstallButtonPressed)
|
||||
}]);
|
||||
|
||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(name);
|
||||
|
||||
this._descriptionLabel = new St.Label({ text: message });
|
||||
|
||||
this.contentLayout.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
},
|
||||
|
||||
_onCancelButtonPressed: function(button, event) {
|
||||
this.close(global.get_current_time());
|
||||
|
||||
// Even though the extension is already "uninstalled", send through
|
||||
// a state-changed signal for any users who want to know if the install
|
||||
// went through correctly -- using proper async DBus would block more
|
||||
// traditional clients like the plugin
|
||||
let meta = { uuid: this._uuid,
|
||||
state: ExtensionState.UNINSTALLED,
|
||||
error: '' };
|
||||
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
},
|
||||
|
||||
_onInstallButtonPressed: function(button, event) {
|
||||
let meta = { uuid: this._uuid,
|
||||
state: ExtensionState.DOWNLOADING,
|
||||
error: '' };
|
||||
|
||||
extensionMeta[this._uuid] = meta;
|
||||
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
|
||||
let params = { version_tag: this._version_tag,
|
||||
shell_version: Config.PACKAGE_VERSION,
|
||||
api_version: API_VERSION.toString() };
|
||||
|
||||
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
|
||||
_httpSession.queue_message(message,
|
||||
Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, this._uuid);
|
||||
}));
|
||||
|
||||
this.close(global.get_current_time());
|
||||
logExtensionError(extension.uuid, e);
|
||||
}
|
||||
});
|
||||
finder.scanExtensions();
|
||||
}
|
||||
|
||||
function enableAllExtensions() {
|
||||
if (enabled)
|
||||
return;
|
||||
|
||||
if (!initted) {
|
||||
_loadExtensions();
|
||||
initted = true;
|
||||
} else {
|
||||
enabledExtensions.forEach(function(uuid) {
|
||||
enableExtension(uuid);
|
||||
});
|
||||
}
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
function disableAllExtensions() {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
if (initted) {
|
||||
enabledExtensions.forEach(function(uuid) {
|
||||
disableExtension(uuid);
|
||||
});
|
||||
}
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
function _sessionUpdated() {
|
||||
// For now sessionMode.allowExtensions controls extensions from both the
|
||||
// 'enabled-extensions' preference and the sessionMode.enabledExtensions
|
||||
// property; it might make sense to make enabledExtensions independent
|
||||
// from allowExtensions in the future
|
||||
if (Main.sessionMode.allowExtensions) {
|
||||
if (initted)
|
||||
onEnabledExtensionsChanged();
|
||||
enableAllExtensions();
|
||||
} else {
|
||||
disableAllExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
Main.sessionMode.connect('updated', _sessionUpdated);
|
||||
_sessionUpdated();
|
||||
}
|
||||
|
45
js/ui/flashspot.js
Normal file
@ -0,0 +1,45 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
||||
});
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
_onFireShowComplete: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
366
js/ui/grabHelper.js
Normal file
@ -0,0 +1,366 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
function _navigateActor(actor) {
|
||||
if (!actor)
|
||||
return;
|
||||
|
||||
let needsGrab = true;
|
||||
if (actor instanceof St.Widget)
|
||||
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
if (needsGrab)
|
||||
actor.grab_key_focus();
|
||||
}
|
||||
|
||||
// GrabHelper:
|
||||
// @owner: the actor that owns the GrabHelper
|
||||
// @params: optional parameters to pass to Main.pushModal()
|
||||
//
|
||||
// Creates a new GrabHelper object, for dealing with keyboard and pointer grabs
|
||||
// associated with a set of actors.
|
||||
//
|
||||
// Note that the grab can be automatically dropped at any time by the user, and
|
||||
// your code just needs to deal with it; you shouldn't adjust behavior directly
|
||||
// after you call ungrab(), but instead pass an 'onUngrab' callback when you
|
||||
// call grab().
|
||||
const GrabHelper = new Lang.Class({
|
||||
Name: 'GrabHelper',
|
||||
|
||||
_init: function(owner, params) {
|
||||
this._owner = owner;
|
||||
this._modalParams = params;
|
||||
|
||||
this._grabStack = [];
|
||||
|
||||
this._actors = [];
|
||||
this._capturedEventId = 0;
|
||||
this._keyFocusNotifyId = 0;
|
||||
this._focusWindowChangedId = 0;
|
||||
this._ignoreRelease = false;
|
||||
|
||||
this._modalCount = 0;
|
||||
this._grabFocusCount = 0;
|
||||
},
|
||||
|
||||
// addActor:
|
||||
// @actor: an actor
|
||||
//
|
||||
// Adds @actor to the set of actors that are allowed to process events
|
||||
// during a grab.
|
||||
addActor: function(actor) {
|
||||
actor.__grabHelperDestroyId = actor.connect('destroy', Lang.bind(this, function() { this.removeActor(actor); }));
|
||||
this._actors.push(actor);
|
||||
},
|
||||
|
||||
// removeActor:
|
||||
// @actor: an actor
|
||||
//
|
||||
// Removes @actor from the set of actors that are allowed to
|
||||
// process events during a grab.
|
||||
removeActor: function(actor) {
|
||||
let index = this._actors.indexOf(actor);
|
||||
if (index != -1)
|
||||
this._actors.splice(index, 1);
|
||||
if (actor.__grabHelperDestroyId) {
|
||||
actor.disconnect(actor.__grabHelperDestroyId);
|
||||
delete actor.__grabHelperDestroyId;
|
||||
}
|
||||
},
|
||||
|
||||
_isWithinGrabbedActor: function(actor) {
|
||||
let currentActor = this.currentGrab.actor;
|
||||
while (actor) {
|
||||
if (this._actors.indexOf(actor) != -1)
|
||||
return true;
|
||||
if (actor == currentActor)
|
||||
return true;
|
||||
actor = actor.get_parent();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
get currentGrab() {
|
||||
return this._grabStack[this._grabStack.length - 1] || {};
|
||||
},
|
||||
|
||||
get grabbed() {
|
||||
return this._grabStack.length > 0;
|
||||
},
|
||||
|
||||
get grabStack() {
|
||||
return this._grabStack;
|
||||
},
|
||||
|
||||
_findStackIndex: function(actor) {
|
||||
if (!actor)
|
||||
return -1;
|
||||
|
||||
for (let i = 0; i < this._grabStack.length; i++) {
|
||||
if (this._grabStack[i].actor === actor)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_actorInGrabStack: function(actor) {
|
||||
while (actor) {
|
||||
let idx = this._findStackIndex(actor);
|
||||
if (idx >= 0)
|
||||
return idx;
|
||||
actor = actor.get_parent();
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
isActorGrabbed: function(actor) {
|
||||
return this._findStackIndex(actor) >= 0;
|
||||
},
|
||||
|
||||
// grab:
|
||||
// @params: A bunch of parameters, see below
|
||||
//
|
||||
// Grabs the mouse and keyboard, according to the GrabHelper's
|
||||
// parameters. If @newFocus is not %null, then the keyboard focus
|
||||
// is moved to the first #StWidget:can-focus widget inside it.
|
||||
//
|
||||
// The grab will automatically be dropped if:
|
||||
// - The user clicks outside the grabbed actors
|
||||
// - The user types Escape
|
||||
// - The keyboard focus is moved outside the grabbed actors
|
||||
// - A window is focused
|
||||
//
|
||||
// If @params.actor is not null, then it will be focused as the
|
||||
// new actor. If you attempt to grab an already focused actor, the
|
||||
// request to be focused will be ignored. The actor will not be
|
||||
// added to the grab stack, so do not call a paired ungrab().
|
||||
//
|
||||
// If @params contains { modal: true }, then grab() will push a modal
|
||||
// on the owner of the GrabHelper. As long as there is at least one
|
||||
// { modal: true } actor on the grab stack, the grab will be kept.
|
||||
// When the last { modal: true } actor is ungrabbed, then the modal
|
||||
// will be dropped. A modal grab can fail if there is already a grab
|
||||
// in effect from aother application; in this case the function returns
|
||||
// false and nothing happens. Non-modal grabs can never fail.
|
||||
//
|
||||
// If @params contains { grabFocus: true }, then if you call grab()
|
||||
// while the shell is outside the overview, it will set the stage
|
||||
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
|
||||
// revert it back, and re-focus the previously-focused window (if
|
||||
// another window hasn't been explicitly focused before then).
|
||||
grab: function(params) {
|
||||
params = Params.parse(params, { actor: null,
|
||||
modal: false,
|
||||
grabFocus: false,
|
||||
focus: null,
|
||||
onUngrab: null });
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||
let newFocus = params.actor;
|
||||
|
||||
if (this.isActorGrabbed(params.actor))
|
||||
return true;
|
||||
|
||||
params.savedFocus = focus;
|
||||
|
||||
if (params.modal && !this._takeModalGrab())
|
||||
return false;
|
||||
|
||||
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
||||
return false;
|
||||
|
||||
if (params.focus)
|
||||
params.focus.grab_key_focus();
|
||||
else if (hadFocus || params.grabFocus)
|
||||
_navigateActor(newFocus);
|
||||
|
||||
this._grabStack.push(params);
|
||||
return true;
|
||||
},
|
||||
|
||||
_takeModalGrab: function() {
|
||||
let firstGrab = (this._modalCount == 0);
|
||||
if (firstGrab) {
|
||||
if (!Main.pushModal(this._owner, this._modalParams))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
}
|
||||
|
||||
this._modalCount++;
|
||||
return true;
|
||||
},
|
||||
|
||||
_releaseModalGrab: function() {
|
||||
this._modalCount--;
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
},
|
||||
|
||||
_takeFocusGrab: function(hadFocus) {
|
||||
let firstGrab = (this._grabFocusCount == 0);
|
||||
if (firstGrab) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
|
||||
this._grabbedFromKeynav = hadFocus;
|
||||
this._preGrabInputMode = global.stage_input_mode;
|
||||
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
|
||||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
|
||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
|
||||
}
|
||||
|
||||
this._grabFocusCount++;
|
||||
return true;
|
||||
},
|
||||
|
||||
_releaseFocusGrab: function() {
|
||||
this._grabFocusCount--;
|
||||
if (this._grabFocusCount > 0)
|
||||
return;
|
||||
|
||||
if (this._keyFocusNotifyId > 0) {
|
||||
global.stage.disconnect(this._keyFocusNotifyId);
|
||||
this._keyFocusNotifyId = 0;
|
||||
}
|
||||
|
||||
if (!this._focusWindowChanged > 0) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
||||
this._focusWindowChangedId = 0;
|
||||
}
|
||||
|
||||
let prePopInputMode = global.stage_input_mode;
|
||||
|
||||
if (this._grabbedFromKeynav) {
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
||||
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
|
||||
global.screen.focus_default_window(global.get_current_time());
|
||||
},
|
||||
|
||||
// ignoreRelease:
|
||||
//
|
||||
// Make sure that the next button release event evaluated by the
|
||||
// capture event handler returns false. This is designed for things
|
||||
// like the ComboBoxMenu that go away on press, but need to eat
|
||||
// the next release event.
|
||||
ignoreRelease: function() {
|
||||
this._ignoreRelease = true;
|
||||
},
|
||||
|
||||
// ungrab:
|
||||
// @params: The parameters for the grab; see below.
|
||||
//
|
||||
// Pops an actor from the grab stack, potentially dropping the grab.
|
||||
//
|
||||
// If the actor that was popped from the grab stack was not the actor
|
||||
// That was passed in, this call is ignored.
|
||||
ungrab: function(params) {
|
||||
params = Params.parse(params, { actor: this.currentGrab.actor,
|
||||
isUser: false });
|
||||
|
||||
let grabStackIndex = this._findStackIndex(params.actor);
|
||||
if (grabStackIndex < 0)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||
|
||||
let poppedGrabs = this._grabStack.slice(grabStackIndex);
|
||||
// "Pop" all newly ungrabbed actors off the grab stack
|
||||
// by truncating the array.
|
||||
this._grabStack.length = grabStackIndex;
|
||||
|
||||
for (let i = poppedGrabs.length - 1; i >= 0; i--) {
|
||||
let poppedGrab = poppedGrabs[i];
|
||||
|
||||
if (poppedGrab.onUngrab)
|
||||
poppedGrab.onUngrab(params.isUser);
|
||||
|
||||
if (poppedGrab.modal)
|
||||
this._releaseModalGrab();
|
||||
|
||||
if (poppedGrab.grabFocus)
|
||||
this._releaseFocusGrab();
|
||||
}
|
||||
|
||||
if (hadFocus) {
|
||||
let poppedGrab = poppedGrabs[0];
|
||||
_navigateActor(poppedGrab.savedFocus);
|
||||
}
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
let button = press || release;
|
||||
|
||||
if (release && this._ignoreRelease) {
|
||||
this._ignoreRelease = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!button && this._modalCount == 0)
|
||||
return false;
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
|
||||
if (Main.keyboard.shouldTakeEvent(event))
|
||||
return false;
|
||||
|
||||
if (button) {
|
||||
// If we have a press event, ignore the next event,
|
||||
// which should be a release event.
|
||||
if (press)
|
||||
this._ignoreRelease = true;
|
||||
let i = this._actorInGrabStack(event.get_source()) + 1;
|
||||
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
|
||||
}
|
||||
|
||||
return this._modalCount > 0;
|
||||
},
|
||||
|
||||
_onKeyFocusChanged: function() {
|
||||
let focus = global.stage.key_focus;
|
||||
if (!focus || !this._isWithinGrabbedActor(focus))
|
||||
this.ungrab({ isUser: true });
|
||||
},
|
||||
|
||||
_focusWindowChanged: function() {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
if (metaDisplay.focus_window != null)
|
||||
this.ungrab({ isUser: true });
|
||||
}
|
||||
});
|
228
js/ui/ibusCandidatePopup.js
Normal file
@ -0,0 +1,228 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const IBus = imports.gi.IBus;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const MAX_CANDIDATES_PER_PAGE = 16;
|
||||
|
||||
const CandidateArea = new Lang.Class({
|
||||
Name: 'CandidateArea',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ reactive: false });
|
||||
|
||||
// St.Table exhibits some sizing problems so let's go with a
|
||||
// clutter layout manager for now.
|
||||
this._table = new Clutter.Actor();
|
||||
this.addActor(this._table);
|
||||
|
||||
this._tableLayout = new Clutter.TableLayout();
|
||||
this._table.set_layout_manager(this._tableLayout);
|
||||
|
||||
this._indexLabels = [];
|
||||
this._candidateLabels = [];
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
|
||||
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
|
||||
}
|
||||
|
||||
this._orientation = -1;
|
||||
this._cursorPosition = 0;
|
||||
},
|
||||
|
||||
_setOrientation: function(orientation) {
|
||||
if (this._orientation == orientation)
|
||||
return;
|
||||
|
||||
this._orientation = orientation;
|
||||
|
||||
this._table.remove_all_children();
|
||||
|
||||
if (this._orientation == IBus.Orientation.HORIZONTAL)
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
|
||||
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
|
||||
}
|
||||
else // VERTICAL || SYSTEM
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._tableLayout.pack(this._indexLabels[i], 0, i);
|
||||
this._tableLayout.pack(this._candidateLabels[i], 1, i);
|
||||
}
|
||||
},
|
||||
|
||||
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
|
||||
this._setOrientation(orientation);
|
||||
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
let visible = i < candidates.length;
|
||||
this._indexLabels[i].visible = visible;
|
||||
this._candidateLabels[i].visible = visible;
|
||||
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
|
||||
this._candidateLabels[i].text = candidates[i];
|
||||
}
|
||||
|
||||
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
|
||||
this._cursorPosition = cursorPosition;
|
||||
if (cursorVisible)
|
||||
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
|
||||
},
|
||||
});
|
||||
|
||||
const CandidatePopup = new Lang.Class({
|
||||
Name: 'CandidatePopup',
|
||||
Extends: PopupMenu.PopupMenu,
|
||||
|
||||
_init: function() {
|
||||
this._cursor = new St.Bin({ opacity: 0 });
|
||||
Main.uiGroup.add_actor(this._cursor);
|
||||
|
||||
this.parent(this._cursor, 0, St.Side.TOP);
|
||||
this.actor.hide();
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
|
||||
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._preeditTextItem.actor.hide();
|
||||
this.addMenuItem(this._preeditTextItem);
|
||||
|
||||
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._auxTextItem.actor.hide();
|
||||
this.addMenuItem(this._auxTextItem);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._lookupTableItem = new CandidateArea();
|
||||
this._lookupTableItem.actor.hide();
|
||||
this.addMenuItem(this._lookupTableItem);
|
||||
|
||||
this._panelService = null;
|
||||
},
|
||||
|
||||
setPanelService: function(panelService) {
|
||||
this._panelService = panelService;
|
||||
if (!panelService)
|
||||
return;
|
||||
|
||||
panelService.connect('set-cursor-location',
|
||||
Lang.bind(this, function(ps, x, y, w, h) {
|
||||
this._cursor.set_position(x, y);
|
||||
this._cursor.set_size(w, h);
|
||||
}));
|
||||
panelService.connect('update-preedit-text',
|
||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||
if (visible)
|
||||
this._preeditTextItem.actor.show();
|
||||
else
|
||||
this._preeditTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
this._preeditTextItem.actor.label_actor.text = text.get_text();
|
||||
|
||||
let attrs = text.get_attributes();
|
||||
if (attrs)
|
||||
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
|
||||
attrs);
|
||||
}));
|
||||
panelService.connect('show-preedit-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._preeditTextItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-preedit-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._preeditTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('update-auxiliary-text',
|
||||
Lang.bind(this, function(ps, text, visible) {
|
||||
if (visible)
|
||||
this._auxTextItem.actor.show();
|
||||
else
|
||||
this._auxTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
this._auxTextItem.actor.label_actor.text = text.get_text();
|
||||
}));
|
||||
panelService.connect('show-auxiliary-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._auxTextItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-auxiliary-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._auxTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('update-lookup-table',
|
||||
Lang.bind(this, function(ps, lookupTable, visible) {
|
||||
if (visible)
|
||||
this._lookupTableItem.actor.show();
|
||||
else
|
||||
this._lookupTableItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
let cursorPos = lookupTable.get_cursor_pos();
|
||||
let pageSize = lookupTable.get_page_size();
|
||||
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
||||
let startIndex = page * pageSize;
|
||||
let endIndex = Math.min((page + 1) * pageSize,
|
||||
lookupTable.get_number_of_candidates());
|
||||
let indexes = [];
|
||||
let indexLabel;
|
||||
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
||||
indexes.push(indexLabel.get_text());
|
||||
|
||||
let candidates = [];
|
||||
for (let i = startIndex; i < endIndex; ++i)
|
||||
candidates.push(lookupTable.get_candidate(i).get_text());
|
||||
|
||||
this._lookupTableItem.setCandidates(indexes,
|
||||
candidates,
|
||||
lookupTable.get_orientation(),
|
||||
cursorPos % pageSize,
|
||||
lookupTable.is_cursor_visible());
|
||||
}));
|
||||
panelService.connect('show-lookup-table',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._lookupTableItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-lookup-table',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._lookupTableItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('focus-out',
|
||||
Lang.bind(this, function(ps) {
|
||||
this.close(BoxPointer.PopupAnimation.NONE);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let isVisible = (this._preeditTextItem.actor.visible ||
|
||||
this._auxTextItem.actor.visible ||
|
||||
this._lookupTableItem.actor.visible);
|
||||
|
||||
if (isVisible)
|
||||
this.open(BoxPointer.PopupAnimation.NONE);
|
||||
else
|
||||
this.close(BoxPointer.PopupAnimation.NONE);
|
||||
},
|
||||
|
||||
_setTextAttributes: function(clutterText, ibusAttrList) {
|
||||
let attr;
|
||||
for (let i = 0; attr = ibusAttrList.get(i); ++i)
|
||||
if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
|
||||
clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
|
||||
}
|
||||
});
|
@ -23,6 +23,8 @@ const BaseIcon = new Lang.Class({
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('style-changed',
|
||||
Lang.bind(this, this._onStyleChanged));
|
||||
this.actor.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._spacing = 0;
|
||||
|
||||
@ -52,6 +54,9 @@ const BaseIcon = new Lang.Class({
|
||||
this._setSizeManually = params.setSizeManually;
|
||||
|
||||
this.icon = null;
|
||||
|
||||
let cache = St.TextureCache.get_default();
|
||||
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
@ -146,12 +151,22 @@ const BaseIcon = new Lang.Class({
|
||||
size = found ? len : ICON_SIZE;
|
||||
}
|
||||
|
||||
// don't create icons unnecessarily
|
||||
if (size == this.iconSize &&
|
||||
this._iconBin.child)
|
||||
if (this.iconSize == size && this._iconBin.child)
|
||||
return;
|
||||
|
||||
this._createIconTexture(size);
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._iconThemeChangedId > 0) {
|
||||
let cache = St.TextureCache.get_default();
|
||||
cache.disconnect(this._iconThemeChangedId);
|
||||
this._iconThemeChangedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_onIconThemeChanged: function() {
|
||||
this._createIconTexture(this.iconSize);
|
||||
}
|
||||
});
|
||||
|
||||
@ -170,7 +185,7 @@ const IconGrid = new Lang.Class({
|
||||
vertical: true });
|
||||
// Pulled from CSS, but hardcode some defaults here
|
||||
this._spacing = 0;
|
||||
this._item_size = ICON_SIZE;
|
||||
this._hItemSize = this._vItemSize = ICON_SIZE;
|
||||
this._grid = new Shell.GenericContainer();
|
||||
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
@ -189,8 +204,8 @@ const IconGrid = new Lang.Class({
|
||||
// Kind of a lie, but not really an issue right now. If
|
||||
// we wanted to support some sort of hidden/overflow that would
|
||||
// need higher level design
|
||||
alloc.min_size = this._item_size;
|
||||
alloc.natural_size = nColumns * this._item_size + totalSpacing;
|
||||
alloc.min_size = this._hItemSize;
|
||||
alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
|
||||
},
|
||||
|
||||
_getVisibleChildren: function() {
|
||||
@ -203,7 +218,11 @@ const IconGrid = new Lang.Class({
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let children = this._getVisibleChildren();
|
||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
||||
let nColumns;
|
||||
if (forWidth < 0)
|
||||
nColumns = children.length;
|
||||
else
|
||||
nColumns = this._computeLayout(forWidth)[0];
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
nRows = Math.ceil(children.length / nColumns);
|
||||
@ -212,7 +231,7 @@ const IconGrid = new Lang.Class({
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
|
||||
let height = nRows * this._item_size + totalSpacing;
|
||||
let height = nRows * this._vItemSize + totalSpacing;
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
},
|
||||
@ -245,13 +264,13 @@ const IconGrid = new Lang.Class({
|
||||
= children[i].get_preferred_size();
|
||||
|
||||
/* Center the item in its allocation horizontally */
|
||||
let width = Math.min(this._item_size, childNaturalWidth);
|
||||
let width = Math.min(this._hItemSize, childNaturalWidth);
|
||||
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
||||
let height = Math.min(this._item_size, childNaturalHeight);
|
||||
let height = Math.min(this._vItemSize, childNaturalHeight);
|
||||
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||
let _x = box.x2 - (x + width);
|
||||
childBox.x1 = Math.floor(_x - childXSpacing);
|
||||
} else {
|
||||
@ -275,10 +294,10 @@ const IconGrid = new Lang.Class({
|
||||
}
|
||||
|
||||
if (columnIndex == 0) {
|
||||
y += this._item_size + this._spacing;
|
||||
y += this._vItemSize + this._spacing;
|
||||
x = box.x1 + leftPadding;
|
||||
} else {
|
||||
x += this._item_size + this._spacing;
|
||||
x += this._hItemSize + this._spacing;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -287,12 +306,16 @@ const IconGrid = new Lang.Class({
|
||||
return this._computeLayout(rowWidth)[0];
|
||||
},
|
||||
|
||||
getRowLimit: function() {
|
||||
return this._rowLimit;
|
||||
},
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||
(usedWidth + this._item_size <= forWidth)) {
|
||||
usedWidth += this._item_size + this._spacing;
|
||||
(usedWidth + this._hItemSize <= forWidth)) {
|
||||
usedWidth += this._hItemSize + this._spacing;
|
||||
nColumns += 1;
|
||||
}
|
||||
|
||||
@ -305,25 +328,27 @@ const IconGrid = new Lang.Class({
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
this._spacing = themeNode.get_length('spacing');
|
||||
this._item_size = themeNode.get_length('-shell-grid-item-size');
|
||||
this._hItemSize = themeNode.get_length('-shell-grid-horizontal-item-size') || ICON_SIZE;
|
||||
this._vItemSize = themeNode.get_length('-shell-grid-vertical-item-size') || ICON_SIZE;
|
||||
this._grid.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
this._grid.get_children().forEach(Lang.bind(this, function (child) {
|
||||
child.destroy();
|
||||
}));
|
||||
this._grid.destroy_all_children();
|
||||
},
|
||||
|
||||
addItem: function(actor) {
|
||||
addItem: function(actor, index) {
|
||||
if (index !== undefined)
|
||||
this._grid.insert_child_at_index(actor, index);
|
||||
else
|
||||
this._grid.add_actor(actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
return this._grid.get_children()[index];
|
||||
return this._grid.get_child_at_index(index);
|
||||
},
|
||||
|
||||
visibleItemsCount: function() {
|
||||
return this._grid.get_children().length - this._grid.get_n_skip_paint();
|
||||
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
|
||||
}
|
||||
});
|
||||
|
@ -20,25 +20,6 @@ const KEYBOARD_TYPE = 'keyboard-type';
|
||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
||||
|
||||
// Key constants taken from Antler
|
||||
// FIXME: ought to be moved into libcaribou
|
||||
const PRETTY_KEYS = {
|
||||
'BackSpace': '\u232b',
|
||||
'space': ' ',
|
||||
'Return': '\u23ce',
|
||||
'Caribou_Prefs': '\u2328',
|
||||
'Caribou_ShiftUp': '\u2b06',
|
||||
'Caribou_ShiftDown': '\u2b07',
|
||||
'Caribou_Emoticons': '\u263a',
|
||||
'Caribou_Symbols': '123',
|
||||
'Caribou_Symbols_More': '{#*',
|
||||
'Caribou_Alpha': 'Abc',
|
||||
'Tab': 'Tab',
|
||||
'Escape': 'Esc',
|
||||
'Control_L': 'Ctrl',
|
||||
'Alt_L': 'Alt'
|
||||
};
|
||||
|
||||
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
|
||||
<method name='Show'>
|
||||
<arg type='u' direction='in' />
|
||||
@ -93,22 +74,12 @@ const Key = new Lang.Class({
|
||||
this._getExtendedKeys();
|
||||
this.actor._extended_keys = this._extended_keyboard;
|
||||
this._boxPointer.actor.hide();
|
||||
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
|
||||
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||
}
|
||||
},
|
||||
|
||||
_makeKey: function () {
|
||||
let label = this._key.name;
|
||||
|
||||
if (label.length > 1) {
|
||||
let pretty = PRETTY_KEYS[label];
|
||||
if (pretty)
|
||||
label = pretty;
|
||||
else
|
||||
label = this._getUnichar(this._key);
|
||||
}
|
||||
|
||||
label = GLib.markup_escape_text(label, -1);
|
||||
let label = GLib.markup_escape_text(this._key.label, -1);
|
||||
let button = new St.Button ({ label: label,
|
||||
style_class: 'keyboard-key' });
|
||||
|
||||
@ -175,7 +146,7 @@ const Key = new Lang.Class({
|
||||
this.actor.fake_release();
|
||||
this._boxPointer.actor.raise_top();
|
||||
this._boxPointer.setPosition(this.actor, 0.5);
|
||||
this._boxPointer.show(true);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
this.actor.set_hover(false);
|
||||
if (!this._grabbed) {
|
||||
Main.pushModal(this.actor);
|
||||
@ -186,7 +157,7 @@ const Key = new Lang.Class({
|
||||
} else {
|
||||
if (this._grabbed)
|
||||
this._ungrab();
|
||||
this._boxPointer.hide(true);
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -200,8 +171,10 @@ const Keyboard = new Lang.Class({
|
||||
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
|
||||
|
||||
this.actor = null;
|
||||
this._focusInTray = false;
|
||||
this._focusInExtendedKeys = false;
|
||||
|
||||
this._timestamp = global.get_current_time();
|
||||
this._timestamp = global.display.get_current_time_roundtrip();
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||
|
||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||
@ -269,11 +242,16 @@ const Keyboard = new Lang.Class({
|
||||
|
||||
this._addKeys();
|
||||
|
||||
// Keys should be layout according to the group, not the
|
||||
// locale; as Caribou already provides the expected layout,
|
||||
// this means enforcing LTR for all locales.
|
||||
this.actor.text_direction = Clutter.TextDirection.LTR;
|
||||
|
||||
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
||||
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
|
||||
if (show)
|
||||
this.show();
|
||||
this.show(Main.layoutManager.focusIndex);
|
||||
else
|
||||
this._createSource();
|
||||
},
|
||||
@ -283,7 +261,15 @@ const Keyboard = new Lang.Class({
|
||||
|
||||
// Showing an extended key popup and clicking a key from the extended keys
|
||||
// will grab focus, but ignore that
|
||||
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
|
||||
let extendedKeysWereFocused = this._focusInExtendedKeys;
|
||||
this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key);
|
||||
if (this._focusInExtendedKeys || extendedKeysWereFocused)
|
||||
return;
|
||||
|
||||
// Ignore focus changes caused by message tray showing/hiding
|
||||
let trayWasFocused = this._focusInTray;
|
||||
this._focusInTray = (focus && Main.messageTray.actor.contains(focus));
|
||||
if (this._focusInTray || trayWasFocused)
|
||||
return;
|
||||
|
||||
let time = global.get_current_time();
|
||||
@ -334,6 +320,13 @@ const Keyboard = new Lang.Class({
|
||||
trayButton.reactive = true;
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, function() {
|
||||
trayButton.reactive = !Main.sessionMode.isLocked;
|
||||
if (Main.sessionMode.isLocked)
|
||||
trayButton.add_style_pseudo_class('grayed');
|
||||
else
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
|
||||
return trayButton;
|
||||
},
|
||||
@ -382,7 +375,7 @@ const Keyboard = new Lang.Class({
|
||||
if (!this._enableKeyboard)
|
||||
return;
|
||||
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
let monitor = Main.layoutManager.keyboardMonitor;
|
||||
let maxHeight = monitor.height / 3;
|
||||
this.actor.width = monitor.width;
|
||||
|
||||
@ -463,9 +456,15 @@ const Keyboard = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this._redraw();
|
||||
shouldTakeEvent: function(event) {
|
||||
let actor = event.get_source();
|
||||
return Main.layoutManager.keyboardBox.contains(actor) ||
|
||||
actor._extended_keys || actor.extended_key;
|
||||
},
|
||||
|
||||
show: function (monitor) {
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
Main.layoutManager.showKeyboard();
|
||||
this._destroySource();
|
||||
},
|
||||
@ -489,25 +488,41 @@ const Keyboard = new Lang.Class({
|
||||
this._moveTemporarily();
|
||||
},
|
||||
|
||||
// _compareTimestamp:
|
||||
//
|
||||
// Compare two timestamps taking into account
|
||||
// CURRENT_TIME (0)
|
||||
_compareTimestamp: function(one, two) {
|
||||
if (one == two)
|
||||
return 0;
|
||||
if (one == Clutter.CURRENT_TIME)
|
||||
return 1;
|
||||
if (two == Clutter.CURRENT_TIME)
|
||||
return -1;
|
||||
return one - two;
|
||||
},
|
||||
|
||||
// D-Bus methods
|
||||
Show: function(timestamp) {
|
||||
if (!this._enableKeyboard)
|
||||
return;
|
||||
|
||||
if (timestamp - this._timestamp < 0)
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.show();
|
||||
this.show(Main.layoutManager.focusIndex);
|
||||
},
|
||||
|
||||
Hide: function(timestamp) {
|
||||
if (!this._enableKeyboard)
|
||||
return;
|
||||
|
||||
if (timestamp - this._timestamp < 0)
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
},
|
||||
@ -536,16 +551,9 @@ const KeyboardSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(keyboard) {
|
||||
this.parent(_("Keyboard"));
|
||||
this._keyboard = keyboard;
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'input-keyboard',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
|
||||
this.keepTrayOnSummaryClick = true;
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
@ -558,6 +566,7 @@ const KeyboardSource = new Lang.Class({
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._keyboard.show();
|
||||
// Show the OSK below the message tray
|
||||
this._keyboard.show(Main.layoutManager.bottomIndex);
|
||||
}
|
||||
});
|
||||
|
362
js/ui/layout.js
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -8,50 +9,140 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
const STARTUP_ANIMATION_TIME = 0.2;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.5;
|
||||
const PLYMOUTH_TRANSITION_TIME = 1;
|
||||
|
||||
const MonitorConstraint = new Lang.Class({
|
||||
Name: 'MonitorConstraint',
|
||||
Extends: Clutter.Constraint,
|
||||
Properties: {'primary': GObject.ParamSpec.boolean('primary',
|
||||
'Primary', 'Track primary monitor',
|
||||
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
false),
|
||||
'index': GObject.ParamSpec.int('index',
|
||||
'Monitor index', 'Track specific monitor',
|
||||
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
-1, 64, -1)},
|
||||
|
||||
_init: function(props) {
|
||||
this._primary = false;
|
||||
this._index = -1;
|
||||
|
||||
this.parent(props);
|
||||
},
|
||||
|
||||
get primary() {
|
||||
return this._primary;
|
||||
},
|
||||
|
||||
set primary(v) {
|
||||
if (v)
|
||||
this._index = -1;
|
||||
this._primary = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
this.notify('primary');
|
||||
},
|
||||
|
||||
get index() {
|
||||
return this._index;
|
||||
},
|
||||
|
||||
set index(v) {
|
||||
this._primary = false;
|
||||
this._index = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
this.notify('index');
|
||||
},
|
||||
|
||||
vfunc_set_actor: function(actor) {
|
||||
if (actor) {
|
||||
if (!this._monitorsChangedId) {
|
||||
this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', Lang.bind(this, function() {
|
||||
this.actor.queue_relayout();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
if (this._monitorsChangedId)
|
||||
Main.layoutManager.disconnect(this._monitorsChangedId);
|
||||
this._monitorsChangedId = 0;
|
||||
}
|
||||
|
||||
this.parent(actor);
|
||||
},
|
||||
|
||||
vfunc_update_allocation: function(actor, actorBox) {
|
||||
if (!this._primary && this._index < 0)
|
||||
return;
|
||||
|
||||
let monitor;
|
||||
if (this._primary) {
|
||||
monitor = Main.layoutManager.primaryMonitor;
|
||||
} else {
|
||||
let index = Math.min(this._index, Main.layoutManager.monitors.length - 1);
|
||||
monitor = Main.layoutManager.monitors[index];
|
||||
}
|
||||
|
||||
actorBox.init_rect(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||
}
|
||||
});
|
||||
|
||||
const LayoutManager = new Lang.Class({
|
||||
Name: 'LayoutManager',
|
||||
|
||||
_init: function () {
|
||||
this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
|
||||
this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
||||
this.monitors = [];
|
||||
this.primaryMonitor = null;
|
||||
this.primaryIndex = -1;
|
||||
this._keyboardIndex = -1;
|
||||
this._hotCorners = [];
|
||||
this._background = null;
|
||||
this._leftPanelBarrier = 0;
|
||||
this._rightPanelBarrier = 0;
|
||||
this._trayBarrier = 0;
|
||||
|
||||
this._chrome = new Chrome(this);
|
||||
|
||||
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
|
||||
visible: false,
|
||||
clip_to_allocation: true,
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
});
|
||||
this.addChrome(this.screenShieldGroup);
|
||||
|
||||
this.panelBox = new St.BoxLayout({ name: 'panelBox',
|
||||
vertical: true });
|
||||
this.addChrome(this.panelBox, { affectsStruts: true });
|
||||
this.addChrome(this.panelBox, { affectsStruts: true,
|
||||
trackFullscreen: true });
|
||||
this.panelBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updatePanelBarriers));
|
||||
Lang.bind(this, this._panelBoxChanged));
|
||||
|
||||
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
|
||||
this.addChrome(this.trayBox, { visibleInFullscreen: true });
|
||||
this.trayBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updateTrayBarrier));
|
||||
this.trayBox = new St.Widget({ name: 'trayBox',
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.addChrome(this.trayBox);
|
||||
|
||||
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
|
||||
this.addChrome(this.keyboardBox);
|
||||
this._keyboardHeightNotifyId = 0;
|
||||
|
||||
global.screen.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
|
||||
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
|
||||
this.emit('primary-fullscreen-changed', state);
|
||||
}));
|
||||
},
|
||||
|
||||
// This is called by Main after everything else is constructed;
|
||||
@ -144,12 +235,14 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateBoxes: function() {
|
||||
this.screenShieldGroup.set_position(0, 0);
|
||||
this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
|
||||
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||
|
||||
this.keyboardBox.set_position(this.bottomMonitor.x,
|
||||
this.bottomMonitor.y + this.bottomMonitor.height);
|
||||
this.keyboardBox.set_size(this.bottomMonitor.width, -1);
|
||||
if (this.keyboardIndex < 0)
|
||||
this.keyboardIndex = this.primaryIndex;
|
||||
|
||||
this.trayBox.set_position(this.bottomMonitor.x,
|
||||
this.bottomMonitor.y + this.bottomMonitor.height);
|
||||
@ -163,6 +256,11 @@ const LayoutManager = new Lang.Class({
|
||||
this.bottomMonitor.width, this.bottomMonitor.height);
|
||||
},
|
||||
|
||||
_panelBoxChanged: function() {
|
||||
this.emit('panel-box-changed');
|
||||
this._updatePanelBarriers();
|
||||
},
|
||||
|
||||
_updatePanelBarriers: function() {
|
||||
if (this._leftPanelBarrier)
|
||||
global.destroy_pointer_barrier(this._leftPanelBarrier);
|
||||
@ -185,22 +283,6 @@ const LayoutManager = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateTrayBarrier: function() {
|
||||
let monitor = this.bottomMonitor;
|
||||
|
||||
if (this._trayBarrier)
|
||||
global.destroy_pointer_barrier(this._trayBarrier);
|
||||
|
||||
if (Main.messageTray) {
|
||||
this._trayBarrier =
|
||||
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
|
||||
monitor.x + monitor.width, monitor.y + monitor.height,
|
||||
4 /* BarrierNegativeX */);
|
||||
} else {
|
||||
this._trayBarrier = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
this._updateMonitors();
|
||||
this._updateBoxes();
|
||||
@ -223,35 +305,85 @@ const LayoutManager = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
get currentMonitor() {
|
||||
let index = global.screen.get_current_monitor();
|
||||
return this.monitors[index];
|
||||
},
|
||||
|
||||
get keyboardMonitor() {
|
||||
return this.monitors[this.keyboardIndex];
|
||||
},
|
||||
|
||||
get focusIndex() {
|
||||
let i = Main.layoutManager.primaryIndex;
|
||||
|
||||
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
|
||||
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
|
||||
let focusActor = global.stage.key_focus;
|
||||
if (focusActor)
|
||||
i = this._chrome.findIndexForActor(focusActor);
|
||||
} else {
|
||||
let focusWindow = global.display.focus_window;
|
||||
if (focusWindow)
|
||||
i = this._chrome.findIndexForWindow(focusWindow);
|
||||
}
|
||||
|
||||
if (focusWindow) {
|
||||
let wrect = focusWindow.get_outer_rect();
|
||||
for (let i = 0; i < this.monitors.length; i++) {
|
||||
let monitor = this.monitors[i];
|
||||
|
||||
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
|
||||
monitor.x + monitor.width > wrect.x &&
|
||||
monitor.y + monitor.height > wrect.y)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this.primaryIndex;
|
||||
},
|
||||
|
||||
get focusMonitor() {
|
||||
return this.monitors[this.focusIndex];
|
||||
},
|
||||
|
||||
set keyboardIndex(v) {
|
||||
this._keyboardIndex = v;
|
||||
this.keyboardBox.set_position(this.keyboardMonitor.x,
|
||||
this.keyboardMonitor.y + this.keyboardMonitor.height);
|
||||
this.keyboardBox.set_size(this.keyboardMonitor.width, -1);
|
||||
},
|
||||
|
||||
get keyboardIndex() {
|
||||
return this._keyboardIndex;
|
||||
},
|
||||
|
||||
_startupAnimation: function() {
|
||||
this.panelBox.translation_y = -this.panelBox.height;
|
||||
|
||||
let plymouthTransitionRunning = false;
|
||||
|
||||
// If we're the greeter, put up the xrootpmap actor
|
||||
// and fade it out to have a nice transition from plymouth
|
||||
// to the greeter. Otherwise, we'll just animate the panel,
|
||||
// as usual.
|
||||
if (Main.sessionMode.isGreeter) {
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
if (this._background != null) {
|
||||
Main.uiGroup.add_actor(this._background);
|
||||
Tweener.addTween(this._background,
|
||||
{ opacity: 0,
|
||||
time: PLYMOUTH_TRANSITION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: this._fadeBackgroundComplete,
|
||||
onCompleteScope: this });
|
||||
plymouthTransitionRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!plymouthTransitionRunning)
|
||||
this._fadeBackgroundComplete();
|
||||
},
|
||||
|
||||
_fadeBackgroundComplete: function() {
|
||||
// Don't animate the strut
|
||||
this._chrome.freezeUpdateRegions();
|
||||
|
||||
this.panelBox.anchor_y = this.panelBox.height;
|
||||
if (this._background != null) {
|
||||
this._background.destroy();
|
||||
this._background = null;
|
||||
}
|
||||
|
||||
Tweener.addTween(this.panelBox,
|
||||
{ anchor_y: 0,
|
||||
{ translation_y: 0,
|
||||
time: STARTUP_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._startupAnimationComplete,
|
||||
@ -260,11 +392,11 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_startupAnimationComplete: function() {
|
||||
this.emit('panel-box-changed');
|
||||
this._chrome.thawUpdateRegions();
|
||||
},
|
||||
|
||||
showKeyboard: function () {
|
||||
Main.messageTray.hide();
|
||||
this.keyboardBox.raise_top();
|
||||
Tweener.addTween(this.keyboardBox,
|
||||
{ anchor_y: this.keyboardBox.height,
|
||||
@ -273,11 +405,16 @@ const LayoutManager = new Lang.Class({
|
||||
onComplete: this._showKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
if (this.keyboardIndex == this.bottomIndex) {
|
||||
Tweener.addTween(this.trayBox,
|
||||
{ anchor_y: this.keyboardBox.height,
|
||||
time: KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
|
||||
this.emit('keyboard-visible-changed', true);
|
||||
},
|
||||
|
||||
_showKeyboardComplete: function() {
|
||||
@ -287,12 +424,12 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._keyboardHeightNotifyId = this.keyboardBox.connect('notify::height', Lang.bind(this, function () {
|
||||
this.keyboardBox.anchor_y = this.keyboardBox.height;
|
||||
if (this.keyboardIndex == this.bottomIndex)
|
||||
this.trayBox.anchor_y = this.keyboardBox.height;
|
||||
}));
|
||||
},
|
||||
|
||||
hideKeyboard: function (immediate) {
|
||||
Main.messageTray.hide();
|
||||
if (this._keyboardHeightNotifyId) {
|
||||
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
|
||||
this._keyboardHeightNotifyId = 0;
|
||||
@ -304,11 +441,16 @@ const LayoutManager = new Lang.Class({
|
||||
onComplete: this._hideKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
if (this.keyboardIndex == this.bottomIndex) {
|
||||
Tweener.addTween(this.trayBox,
|
||||
{ anchor_y: 0,
|
||||
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
|
||||
this.emit('keyboard-visible-changed', false);
|
||||
},
|
||||
|
||||
_hideKeyboardComplete: function() {
|
||||
@ -330,8 +472,10 @@ const LayoutManager = new Lang.Class({
|
||||
// the window manager struts. Changes to @actor's visibility will
|
||||
// NOT affect whether or not the strut is present, however.
|
||||
//
|
||||
// If %visibleInFullscreen in @params is %true, the actor will be
|
||||
// visible even when a fullscreen window should be covering it.
|
||||
// If %trackFullscreen in @params is %true, the actor's visibility
|
||||
// will be bound to the presence of fullscreen windows on the same
|
||||
// monitor (it will be hidden whenever a fullscreen window is visible,
|
||||
// and shown otherwise)
|
||||
addChrome: function(actor, params) {
|
||||
this._chrome.addActor(actor, params);
|
||||
},
|
||||
@ -345,10 +489,8 @@ const LayoutManager = new Lang.Class({
|
||||
// struts or input region to cover specific children.
|
||||
//
|
||||
// @params can have any of the same values as in addChrome(),
|
||||
// though some possibilities don't make sense (eg, trying to have
|
||||
// a %visibleInFullscreen child of a non-%visibleInFullscreen
|
||||
// parent). By default, @actor has the same params as its chrome
|
||||
// ancestor.
|
||||
// though some possibilities don't make sense. By default, @actor has
|
||||
// the same params as its chrome ancestor.
|
||||
trackChrome: function(actor, params) {
|
||||
this._chrome.trackActor(actor, params);
|
||||
},
|
||||
@ -370,8 +512,12 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
findMonitorForActor: function(actor) {
|
||||
return this._chrome.findMonitorForActor(actor);
|
||||
}
|
||||
return this.monitors[this._chrome.findIndexForActor(actor)];
|
||||
},
|
||||
|
||||
findMonitorForWindow: function(window) {
|
||||
return this.monitors[this._chrome.findIndexForWindow(window)];
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(LayoutManager.prototype);
|
||||
|
||||
@ -404,7 +550,7 @@ const HotCorner = new Lang.Class({
|
||||
|
||||
this.actor.add_actor(this._corner);
|
||||
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||
this._corner.set_position(this.actor.width - this._corner.width, 0);
|
||||
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
} else {
|
||||
@ -456,7 +602,7 @@ const HotCorner = new Lang.Class({
|
||||
|
||||
ripple._opacity = startOpacity;
|
||||
|
||||
if (ripple.get_direction() == St.TextDirection.RTL)
|
||||
if (ripple.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
|
||||
ripple.visible = true;
|
||||
@ -490,13 +636,15 @@ const HotCorner = new Lang.Class({
|
||||
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
if (source != Main.xdndHandler)
|
||||
return;
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||
this.rippleAnimation();
|
||||
Main.overview.showTemporarily();
|
||||
Main.overview.beginItemDrag(actor);
|
||||
}
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onCornerEntered : function() {
|
||||
@ -552,7 +700,7 @@ const HotCorner = new Lang.Class({
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
trackFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
@ -579,24 +727,13 @@ const Chrome = new Lang.Class({
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
|
||||
this._screenSaverActive = false;
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, function(proxy, senderName, [isActive]) {
|
||||
this._onScreenSaverActiveChanged(isActive);
|
||||
}));
|
||||
this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) {
|
||||
if (!err)
|
||||
this._onScreenSaverActiveChanged(result[0]);
|
||||
}));
|
||||
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
init: function() {
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
},
|
||||
|
||||
addActor: function(actor, params) {
|
||||
@ -682,28 +819,30 @@ const Chrome = new Lang.Class({
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
let newParent = actor.get_parent();
|
||||
if (!newParent)
|
||||
if (!newParent) {
|
||||
this._untrackActor(actor);
|
||||
else
|
||||
} else {
|
||||
let i = this._findActor(actor);
|
||||
let actorData = this._trackedActors[i];
|
||||
actorData.isToplevel = (newParent == Main.uiGroup);
|
||||
}
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i], visible;
|
||||
if (!actorData.trackFullscreen)
|
||||
continue;
|
||||
if (!actorData.isToplevel)
|
||||
continue;
|
||||
|
||||
if (this._screenSaverActive)
|
||||
visible = false;
|
||||
else if (this._inOverview)
|
||||
if (this._inOverview || !Main.sessionMode.hasWindows)
|
||||
visible = true;
|
||||
else if (!actorData.visibleInFullscreen &&
|
||||
this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
visible = false;
|
||||
else
|
||||
visible = true;
|
||||
Main.uiGroup.set_skip_paint(actorData.actor, !visible);
|
||||
actorData.actor.visible = visible;
|
||||
}
|
||||
},
|
||||
|
||||
@ -719,17 +858,17 @@ const Chrome = new Lang.Class({
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_relayout: function() {
|
||||
this._monitors = this._layoutManager.monitors;
|
||||
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
||||
|
||||
this._updateFullscreen();
|
||||
_sessionUpdated: function() {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_onScreenSaverActiveChanged: function(screenSaverActive) {
|
||||
this._screenSaverActive = screenSaverActive;
|
||||
_relayout: function() {
|
||||
this._monitors = this._layoutManager.monitors;
|
||||
this._primaryIndex = this._layoutManager.primaryIndex;
|
||||
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
||||
|
||||
this._updateFullscreen();
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
@ -742,32 +881,49 @@ const Chrome = new Lang.Class({
|
||||
let monitor = this._monitors[i];
|
||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
return i;
|
||||
}
|
||||
// If the center is not on a monitor, return the first overlapping monitor
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
return i;
|
||||
}
|
||||
// otherwise on no monitor
|
||||
return null;
|
||||
return -1;
|
||||
},
|
||||
|
||||
_findMonitorForWindow: function(window) {
|
||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
findIndexForWindow: function(window) {
|
||||
let rect = window.get_input_rect();
|
||||
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
|
||||
if (i >= 0)
|
||||
return i;
|
||||
return this._primaryIndex; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
// This call guarantees that we return some monitor to simplify usage of it
|
||||
// In practice all tracked actors should be visible on some monitor anyway
|
||||
findMonitorForActor: function(actor) {
|
||||
findIndexForActor: function(actor) {
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
let [w, h] = actor.get_transformed_size();
|
||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||
if (monitor)
|
||||
return monitor;
|
||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||
let i = this._findMonitorForRect(x, y, w, h);
|
||||
if (i >= 0)
|
||||
return i;
|
||||
return this._primaryIndex; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
findMonitorForWindow: function(window) {
|
||||
let rect = window.get_input_rect();
|
||||
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
|
||||
if (i >= 0)
|
||||
return this._monitors[i];
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
findMonitorForActor: function(actor) {
|
||||
return this._monitors[this.findIndexForActor(actor)];
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
@ -808,14 +964,15 @@ const Chrome = new Lang.Class({
|
||||
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
let metaWindow = window.meta_window;
|
||||
let layer = metaWindow.get_layer();
|
||||
|
||||
// Skip minimized windows
|
||||
if (!window.showing_on_its_workspace())
|
||||
continue;
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
let monitor = this.findMonitorForWindow(metaWindow);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
}
|
||||
@ -832,7 +989,7 @@ const Chrome = new Lang.Class({
|
||||
}
|
||||
|
||||
// Or whether it is monitor sized
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
let monitor = this.findMonitorForWindow(metaWindow);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
@ -849,6 +1006,8 @@ const Chrome = new Lang.Class({
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
@ -858,10 +1017,15 @@ const Chrome = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
|
||||
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
|
||||
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
|
||||
}
|
||||
},
|
||||
|
||||
updateRegions: function() {
|
||||
@ -976,3 +1140,5 @@ const Chrome = new Lang.Class({
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Signals.addSignalMethods(Chrome.prototype);
|
||||
|
@ -1,5 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
@ -7,6 +8,8 @@ const St = imports.gi.St;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const DEFAULT_FADE_FACTOR = 0.4;
|
||||
|
||||
/**
|
||||
* Lightbox:
|
||||
* @container: parent Clutter.Container
|
||||
@ -14,7 +17,8 @@ const Tweener = imports.ui.tweener;
|
||||
* - inhibitEvents: whether to inhibit events for @container
|
||||
* - width: shade actor width
|
||||
* - height: shade actor height
|
||||
* - fadeTime: seconds used to fade in/out
|
||||
* - fadeInTime: seconds used to fade in
|
||||
* - fadeOutTime: seconds used to fade out
|
||||
*
|
||||
* Lightbox creates a dark translucent "shade" actor to hide the
|
||||
* contents of @container, and allows you to specify particular actors
|
||||
@ -37,12 +41,16 @@ const Lightbox = new Lang.Class({
|
||||
params = Params.parse(params, { inhibitEvents: false,
|
||||
width: null,
|
||||
height: null,
|
||||
fadeTime: null
|
||||
fadeInTime: null,
|
||||
fadeOutTime: null,
|
||||
fadeFactor: DEFAULT_FADE_FACTOR
|
||||
});
|
||||
|
||||
this._container = container;
|
||||
this._children = container.get_children();
|
||||
this._fadeTime = params.fadeTime;
|
||||
this._fadeInTime = params.fadeInTime;
|
||||
this._fadeOutTime = params.fadeOutTime;
|
||||
this._fadeFactor = params.fadeFactor;
|
||||
this.actor = new St.Bin({ x: 0,
|
||||
y: 0,
|
||||
style_class: 'lightbox',
|
||||
@ -51,17 +59,17 @@ const Lightbox = new Lang.Class({
|
||||
container.add_actor(this.actor);
|
||||
this.actor.raise_top();
|
||||
this.actor.hide();
|
||||
this.shown = false;
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
if (params.width && params.height) {
|
||||
this.actor.width = params.width;
|
||||
this.actor.height = params.height;
|
||||
this._allocationChangedSignalId = 0;
|
||||
} else {
|
||||
this.actor.width = container.width;
|
||||
this.actor.height = container.height;
|
||||
this._allocationChangedSignalId = container.connect('allocation-changed', Lang.bind(this, this._allocationChanged));
|
||||
let constraint = new Clutter.BindConstraint({ source: container,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
this.actor.add_constraint(constraint);
|
||||
}
|
||||
|
||||
this._actorAddedSignalId = container.connect('actor-added', Lang.bind(this, this._actorAdded));
|
||||
@ -70,16 +78,6 @@ const Lightbox = new Lang.Class({
|
||||
this._highlighted = null;
|
||||
},
|
||||
|
||||
_allocationChanged : function(container, box, flags) {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
this.actor.width = this.width;
|
||||
this.actor.height = this.height;
|
||||
return false;
|
||||
}));
|
||||
this.width = this._container.width;
|
||||
this.height = this._container.height;
|
||||
},
|
||||
|
||||
_actorAdded : function(container, newChild) {
|
||||
let children = this._container.get_children();
|
||||
let myIndex = children.indexOf(this.actor);
|
||||
@ -103,24 +101,30 @@ const Lightbox = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this._fadeTime) {
|
||||
if (this._fadeInTime) {
|
||||
this.shown = false;
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: this._fadeTime,
|
||||
transition: 'easeOutQuad'
|
||||
{ opacity: 255 * this._fadeFactor,
|
||||
time: this._fadeInTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.shown = true;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.opacity = 255;
|
||||
this.actor.opacity = 255 * this._fadeFactor;
|
||||
this.shown = true;
|
||||
}
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this._fadeTime) {
|
||||
this.shown = false;
|
||||
if (this._fadeOutTime) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: this._fadeTime,
|
||||
time: this._fadeOutTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.actor.hide();
|
||||
@ -187,8 +191,6 @@ const Lightbox = new Lang.Class({
|
||||
* by destroying its container or by explicitly calling this.destroy().
|
||||
*/
|
||||
_onDestroy: function() {
|
||||
if (this._allocationChangedSignalId != 0)
|
||||
this._container.disconnect(this._allocationChangedSignalId);
|
||||
this._container.disconnect(this._actorAddedSignalId);
|
||||
this._container.disconnect(this._actorRemovedSignalId);
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Link = new Lang.Class({
|
||||
Name: 'Link',
|
||||
|
||||
_init : function(props) {
|
||||
let realProps = { reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link' };
|
||||
// The user can pass in reactive: false to override the above and get
|
||||
// a non-reactive link (a link to the current page, perhaps)
|
||||
Lang.copyProperties(props, realProps);
|
||||
|
||||
this.actor = new St.Button(realProps);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Link.prototype);
|
@ -12,15 +12,18 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const System = imports.system;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const Link = imports.ui.link;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
const JsParse = imports.misc.jsParse;
|
||||
|
||||
const CHEVRON = '>>> ';
|
||||
|
||||
/* Imports...feel free to add here as needed */
|
||||
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const GLib = imports.gi.GLib; ' +
|
||||
@ -35,7 +38,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
/* Utility functions...we should probably be able to use these
|
||||
* in the shell core code too. */
|
||||
'const stage = global.stage; ' +
|
||||
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
|
||||
/* Special lookingGlass functions */
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
@ -260,9 +262,8 @@ function objectToString(o) {
|
||||
|
||||
const ObjLink = new Lang.Class({
|
||||
Name: 'ObjLink',
|
||||
Extends: Link.Link,
|
||||
|
||||
_init: function(o, title) {
|
||||
_init: function(lookingGlass, o, title) {
|
||||
let text;
|
||||
if (title)
|
||||
text = title;
|
||||
@ -271,24 +272,30 @@ const ObjLink = new Lang.Class({
|
||||
text = GLib.markup_escape_text(text, -1);
|
||||
this._obj = o;
|
||||
|
||||
this.parent({ label: text });
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: text });
|
||||
this.actor.get_child().single_line_mode = true;
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
_onClicked: function (link) {
|
||||
Main.lookingGlass.inspectObject(this._obj, this.actor);
|
||||
this._lookingGlass.inspectObject(this._obj, this.actor);
|
||||
}
|
||||
});
|
||||
|
||||
const Result = new Lang.Class({
|
||||
Name: 'Result',
|
||||
|
||||
_init : function(command, o, index) {
|
||||
_init: function(lookingGlass, command, o, index) {
|
||||
this.index = index;
|
||||
this.o = o;
|
||||
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this._lookingGlass = lookingGlass;
|
||||
|
||||
let cmdTxt = new St.Label({ text: command });
|
||||
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||
@ -298,7 +305,7 @@ const Result = new Lang.Class({
|
||||
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
|
||||
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||
box.add(resultTxt);
|
||||
let objLink = new ObjLink(o);
|
||||
let objLink = new ObjLink(this._lookingGlass, o);
|
||||
box.add(objLink.actor);
|
||||
let line = new Clutter.Rectangle({ name: 'Separator' });
|
||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
||||
@ -310,16 +317,18 @@ const Result = new Lang.Class({
|
||||
const WindowList = new Lang.Class({
|
||||
Name: 'WindowList',
|
||||
|
||||
_init : function () {
|
||||
_init: function(lookingGlass) {
|
||||
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
||||
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
|
||||
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
_updateWindowList: function() {
|
||||
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
||||
this.actor.destroy_all_children();
|
||||
let windows = global.get_window_actors();
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
@ -331,7 +340,7 @@ const WindowList = new Lang.Class({
|
||||
}
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this.actor.add(box);
|
||||
let windowLink = new ObjLink(metaWindow, metaWindow.title);
|
||||
let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
|
||||
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
|
||||
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
|
||||
box.add(propsBox);
|
||||
@ -342,7 +351,7 @@ const WindowList = new Lang.Class({
|
||||
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
||||
propsBox.add(propBox);
|
||||
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
|
||||
let appLink = new ObjLink(app, app.get_id());
|
||||
let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
|
||||
propBox.add(appLink.actor, { y_fill: false });
|
||||
propBox.add(icon, { y_fill: false });
|
||||
} else {
|
||||
@ -356,18 +365,21 @@ Signals.addSignalMethods(WindowList.prototype);
|
||||
const ObjInspector = new Lang.Class({
|
||||
Name: 'ObjInspector',
|
||||
|
||||
_init : function () {
|
||||
_init: function(lookingGlass) {
|
||||
this._obj = null;
|
||||
this._previousObj = null;
|
||||
|
||||
this._parentList = [];
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
|
||||
x_fill: true, y_fill: true });
|
||||
this.actor.get_hscroll_bar().hide();
|
||||
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
|
||||
style_class: 'lg-dialog',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._container);
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
selectObject: function(obj, skipPrevious) {
|
||||
@ -377,7 +389,7 @@ const ObjInspector = new Lang.Class({
|
||||
this._previousObj = null;
|
||||
this._obj = obj;
|
||||
|
||||
this._container.get_children().forEach(function (child) { child.destroy(); });
|
||||
this._container.destroy_all_children();
|
||||
|
||||
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
|
||||
this._container.add_actor(hbox);
|
||||
@ -399,12 +411,19 @@ const ObjInspector = new Lang.Class({
|
||||
button.connect('clicked', Lang.bind(this, this.close));
|
||||
hbox.add(button);
|
||||
if (typeof(obj) == typeof({})) {
|
||||
let properties = [];
|
||||
for (let propName in obj) {
|
||||
properties.push(propName);
|
||||
}
|
||||
properties.sort();
|
||||
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
let propName = properties[i];
|
||||
let valueStr;
|
||||
let link;
|
||||
try {
|
||||
let prop = obj[propName];
|
||||
link = new ObjLink(prop).actor;
|
||||
link = new ObjLink(this._lookingGlass, prop).actor;
|
||||
} catch (e) {
|
||||
link = new St.Label({ text: '<error>' });
|
||||
}
|
||||
@ -425,10 +444,6 @@ const ObjInspector = new Lang.Class({
|
||||
this.actor.show();
|
||||
if (sourceActor) {
|
||||
this.actor.set_scale(0, 0);
|
||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
||||
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
||||
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
|
||||
Math.floor(sourceY + sourceHeight / 2));
|
||||
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
||||
transition: 'easeOutQuad',
|
||||
time: 0.2 });
|
||||
@ -449,7 +464,7 @@ const ObjInspector = new Lang.Class({
|
||||
_onInsert: function() {
|
||||
let obj = this._obj;
|
||||
this.close();
|
||||
Main.lookingGlass.insertObject(obj);
|
||||
this._lookingGlass.insertObject(obj);
|
||||
},
|
||||
|
||||
_onBack: function() {
|
||||
@ -457,9 +472,14 @@ const ObjInspector = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
function addBorderPaintHook(actor) {
|
||||
let signalId = actor.connect_after('paint',
|
||||
function () {
|
||||
const RedBorderEffect = new Lang.Class({
|
||||
Name: 'RedBorderEffect',
|
||||
Extends: Clutter.Effect,
|
||||
|
||||
vfunc_paint: function() {
|
||||
let actor = this.get_actor();
|
||||
actor.continue_paint();
|
||||
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
@ -475,16 +495,13 @@ function addBorderPaintHook(actor) {
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
},
|
||||
});
|
||||
|
||||
actor.queue_redraw();
|
||||
return signalId;
|
||||
}
|
||||
|
||||
const Inspector = new Lang.Class({
|
||||
Name: 'Inspector',
|
||||
|
||||
_init: function() {
|
||||
_init: function(lookingGlass) {
|
||||
let container = new Shell.GenericContainer({ width: 0,
|
||||
height: 0 });
|
||||
container.connect('allocate', Lang.bind(this, this._allocate));
|
||||
@ -498,9 +515,6 @@ const Inspector = new Lang.Class({
|
||||
this._displayText = new St.Label();
|
||||
eventHandler.add(this._displayText, { expand: true });
|
||||
|
||||
this._borderPaintTarget = null;
|
||||
this._borderPaintId = null;
|
||||
eventHandler.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
eventHandler.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
|
||||
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
@ -515,6 +529,8 @@ const Inspector = new Lang.Class({
|
||||
// out, or move the pointer outside of _pointerTarget.
|
||||
this._target = null;
|
||||
this._pointerTarget = null;
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
@ -535,18 +551,13 @@ const Inspector = new Lang.Class({
|
||||
},
|
||||
|
||||
_close: function() {
|
||||
Clutter.ungrab_pointer(this._eventHandler);
|
||||
Clutter.ungrab_keyboard(this._eventHandler);
|
||||
Clutter.ungrab_pointer();
|
||||
Clutter.ungrab_keyboard();
|
||||
this._eventHandler.destroy();
|
||||
this._eventHandler = null;
|
||||
this.emit('closed');
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function (actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._close();
|
||||
@ -615,56 +626,12 @@ const Inspector = new Lang.Class({
|
||||
this._displayText.text = '';
|
||||
this._displayText.text = position + ' ' + this._target;
|
||||
|
||||
if (this._borderPaintTarget != this._target) {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = this._target;
|
||||
this._borderPaintId = addBorderPaintHook(this._target);
|
||||
}
|
||||
this._lookingGlass.setBorderPaintTarget(this._target);
|
||||
}
|
||||
});
|
||||
|
||||
Signals.addSignalMethods(Inspector.prototype);
|
||||
|
||||
const ErrorLog = new Lang.Class({
|
||||
Name: 'ErrorLog',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout();
|
||||
this.text = new St.Label();
|
||||
this.actor.add(this.text);
|
||||
// We need to override StLabel's default ellipsization when
|
||||
// using line_wrap; otherwise ClutterText's layout is going
|
||||
// to constrain both the width and height, which prevents
|
||||
// scrolling.
|
||||
this.text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this.text.clutter_text.line_wrap = true;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
||||
},
|
||||
|
||||
_formatTime: function(d){
|
||||
function pad(n) { return n < 10 ? '0' + n : n; }
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z';
|
||||
},
|
||||
|
||||
_renderText: function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
let text = this.text.text;
|
||||
let stack = Main._getAndClearErrorStack();
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
let logItem = stack[i];
|
||||
text += logItem.category + ' t=' + this._formatTime(new Date(logItem.timestamp)) + ' ' + logItem.message + '\n';
|
||||
}
|
||||
this.text.text = text;
|
||||
}
|
||||
});
|
||||
|
||||
const Memory = new Lang.Class({
|
||||
Name: 'Memory',
|
||||
|
||||
@ -693,7 +660,7 @@ const Memory = new Lang.Class({
|
||||
|
||||
this._gcbutton = new St.Button({ label: 'Full GC',
|
||||
style_class: 'lg-obj-inspector-button' });
|
||||
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
|
||||
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
|
||||
this.actor.add(this._gcbutton, { x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
|
||||
@ -728,7 +695,7 @@ const Extensions = new Lang.Class({
|
||||
this._extensionsList.add(this._noExtensions);
|
||||
this.actor.add(this._extensionsList);
|
||||
|
||||
for (let uuid in ExtensionSystem.extensionMeta)
|
||||
for (let uuid in ExtensionUtils.extensions)
|
||||
this._loadExtension(null, uuid);
|
||||
|
||||
ExtensionSystem.connect('extension-loaded',
|
||||
@ -736,10 +703,10 @@ const Extensions = new Lang.Class({
|
||||
},
|
||||
|
||||
_loadExtension: function(o, uuid) {
|
||||
let extension = ExtensionSystem.extensionMeta[uuid];
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
// There can be cases where we create dummy extension metadata
|
||||
// that's not really a proper extension. Don't bother with these.
|
||||
if (!extension.name)
|
||||
if (!extension.metadata.name)
|
||||
return;
|
||||
|
||||
let extensionDisplay = this._createExtensionDisplay(extension);
|
||||
@ -751,32 +718,31 @@ const Extensions = new Lang.Class({
|
||||
},
|
||||
|
||||
_onViewSource: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
let file = Gio.file_new_for_path(meta.path);
|
||||
let uri = file.get_uri();
|
||||
let extension = actor._extension;
|
||||
let uri = extension.dir.get_uri();
|
||||
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
this._lookingGlass.close();
|
||||
},
|
||||
|
||||
_onWebPage: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
let extension = actor._extension;
|
||||
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
||||
this._lookingGlass.close();
|
||||
},
|
||||
|
||||
_onViewErrors: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
let extension = actor._extension;
|
||||
let shouldShow = !actor._isShowing;
|
||||
|
||||
if (shouldShow) {
|
||||
let errors = ExtensionSystem.errors[meta.uuid];
|
||||
let errors = extension.errors;
|
||||
let errorDisplay = new St.BoxLayout({ vertical: true });
|
||||
if (errors && errors.length) {
|
||||
for (let i = 0; i < errors.length; i ++)
|
||||
errorDisplay.add(new St.Label({ text: errors[i] }));
|
||||
} else {
|
||||
/* Translators: argument is an extension UUID. */
|
||||
let message = _("%s has not emitted any errors.").format(meta.uuid);
|
||||
let message = _("%s has not emitted any errors.").format(extension.uuid);
|
||||
errorDisplay.add(new St.Label({ text: message }));
|
||||
}
|
||||
|
||||
@ -809,40 +775,49 @@ const Extensions = new Lang.Class({
|
||||
return 'Unknown'; // Not translated, shouldn't appear
|
||||
},
|
||||
|
||||
_createExtensionDisplay: function(meta) {
|
||||
_createExtensionDisplay: function(extension) {
|
||||
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
|
||||
let name = new St.Label({ style_class: 'lg-extension-name',
|
||||
text: meta.name });
|
||||
text: extension.metadata.name });
|
||||
box.add(name, { expand: true });
|
||||
let description = new St.Label({ style_class: 'lg-extension-description',
|
||||
text: meta.description || 'No description' });
|
||||
text: extension.metadata.description || 'No description' });
|
||||
box.add(description, { expand: true });
|
||||
|
||||
let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' });
|
||||
box.add(metaBox);
|
||||
let stateString = this._stateToString(meta.state);
|
||||
let stateString = this._stateToString(extension.state);
|
||||
let state = new St.Label({ style_class: 'lg-extension-state',
|
||||
text: this._stateToString(meta.state) });
|
||||
text: this._stateToString(extension.state) });
|
||||
metaBox.add(state);
|
||||
|
||||
let viewsource = new Link.Link({ label: _("View Source") });
|
||||
viewsource.actor._extensionMeta = meta;
|
||||
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||
metaBox.add(viewsource.actor);
|
||||
let viewsource = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: _("View Source") });
|
||||
viewsource._extension = extension;
|
||||
viewsource.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||
metaBox.add(viewsource);
|
||||
|
||||
if (meta.url) {
|
||||
let webpage = new Link.Link({ label: _("Web Page") });
|
||||
webpage.actor._extensionMeta = meta;
|
||||
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
metaBox.add(webpage.actor);
|
||||
if (extension.metadata.url) {
|
||||
let webpage = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: _("Web Page") });
|
||||
webpage._extension = extension;
|
||||
webpage.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
metaBox.add(webpage);
|
||||
}
|
||||
|
||||
let viewerrors = new Link.Link({ label: _("Show Errors") });
|
||||
viewerrors.actor._extensionMeta = meta;
|
||||
viewerrors.actor._parentBox = box;
|
||||
viewerrors.actor._isShowing = false;
|
||||
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
|
||||
metaBox.add(viewerrors.actor);
|
||||
let viewerrors = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: _("Show Errors") });
|
||||
viewerrors._extension = extension;
|
||||
viewerrors._parentBox = box;
|
||||
viewerrors._isShowing = false;
|
||||
viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors));
|
||||
metaBox.add(viewerrors);
|
||||
|
||||
return box;
|
||||
}
|
||||
@ -853,8 +828,7 @@ const LookingGlass = new Lang.Class({
|
||||
|
||||
_init : function() {
|
||||
this._borderPaintTarget = null;
|
||||
this._borderPaintId = 0;
|
||||
this._borderDestroyId = 0;
|
||||
this._redBorderEffect = new RedBorderEffect();
|
||||
|
||||
this._open = false;
|
||||
|
||||
@ -884,19 +858,18 @@ const LookingGlass = new Lang.Class({
|
||||
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._queueResize));
|
||||
|
||||
this._objInspector = new ObjInspector();
|
||||
this._objInspector = new ObjInspector(this);
|
||||
Main.uiGroup.add_actor(this._objInspector.actor);
|
||||
this._objInspector.actor.hide();
|
||||
|
||||
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
||||
this.actor.add_actor(toolbar);
|
||||
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: 24 });
|
||||
toolbar.add_actor(inspectIcon);
|
||||
inspectIcon.reactive = true;
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector();
|
||||
let inspector = new Inspector(this);
|
||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||
target);
|
||||
@ -926,23 +899,16 @@ const LookingGlass = new Lang.Class({
|
||||
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
|
||||
this._evalBox.add_actor(this._entryArea);
|
||||
|
||||
let label = new St.Label({ text: 'js>>> ' });
|
||||
let label = new St.Label({ text: CHEVRON });
|
||||
this._entryArea.add(label);
|
||||
|
||||
this._entry = new St.Entry({ can_focus: true });
|
||||
ShellEntry.addContextMenu(this._entry);
|
||||
this._entryArea.add(this._entry, { expand: true });
|
||||
|
||||
this._windowList = new WindowList();
|
||||
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
|
||||
notebook.selectIndex(0);
|
||||
this._pushResult('<window selection>', window);
|
||||
}));
|
||||
this._windowList = new WindowList(this);
|
||||
notebook.appendPage('Windows', this._windowList.actor);
|
||||
|
||||
this._errorLog = new ErrorLog();
|
||||
notebook.appendPage('Errors', this._errorLog.actor);
|
||||
|
||||
this._memory = new Memory();
|
||||
notebook.appendPage('Memory', this._memory.actor);
|
||||
|
||||
@ -994,23 +960,22 @@ const LookingGlass = new Lang.Class({
|
||||
+ 'font-family: "' + fontDesc.get_family() + '";';
|
||||
},
|
||||
|
||||
setBorderPaintTarget: function(obj) {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.remove_effect(this._redBorderEffect);
|
||||
this._borderPaintTarget = obj;
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.add_effect(this._redBorderEffect);
|
||||
},
|
||||
|
||||
_pushResult: function(command, obj) {
|
||||
let index = this._results.length + this._offset;
|
||||
let result = new Result('>>> ' + command, obj, index);
|
||||
let result = new Result(this, CHEVRON + command, obj, index);
|
||||
this._results.push(result);
|
||||
this._resultsArea.add(result.actor);
|
||||
if (this._borderPaintTarget != null) {
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = null;
|
||||
}
|
||||
if (obj instanceof Clutter.Actor) {
|
||||
this._borderPaintTarget = obj;
|
||||
this._borderPaintId = addBorderPaintHook(obj);
|
||||
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
|
||||
this._borderDestroyId = 0;
|
||||
this._borderPaintTarget = null;
|
||||
}));
|
||||
}
|
||||
if (obj instanceof Clutter.Actor)
|
||||
this.setBorderPaintTarget(obj);
|
||||
|
||||
let children = this._resultsArea.get_children();
|
||||
if (children.length > this._maxItems) {
|
||||
this._results.shift();
|
||||
@ -1038,7 +1003,7 @@ const LookingGlass = new Lang.Class({
|
||||
actor.add(padBin);
|
||||
|
||||
this._completionActor = actor;
|
||||
this._evalBox.insert_before(this._completionActor, this._entryArea);
|
||||
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
||||
}
|
||||
|
||||
this._completionText.set_text(completions.join(', '));
|
||||
@ -1124,8 +1089,8 @@ const LookingGlass = new Lang.Class({
|
||||
this.actor.width = myWidth;
|
||||
this.actor.height = myHeight;
|
||||
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
||||
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
|
||||
this._targetY + Math.floor(myHeight * 0.1));
|
||||
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
|
||||
primary.y + this._targetY + Math.floor(myHeight * 0.1));
|
||||
},
|
||||
|
||||
insertObject: function(obj) {
|
||||
@ -1140,7 +1105,7 @@ const LookingGlass = new Lang.Class({
|
||||
// Handle key events which are relevant for all tabs of the LookingGlass
|
||||
_globalKeyPressEvent : function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
let modifierState = Shell.get_event_state(event);
|
||||
let modifierState = event.get_state();
|
||||
if (symbol == Clutter.Escape) {
|
||||
if (this._objInspector.actor.visible) {
|
||||
this._objInspector.close();
|
||||
@ -1164,7 +1129,7 @@ const LookingGlass = new Lang.Class({
|
||||
if (this._open)
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this._entry))
|
||||
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
|
||||
return;
|
||||
|
||||
this._notebook.selectIndex(0);
|
||||
@ -1191,11 +1156,7 @@ const LookingGlass = new Lang.Class({
|
||||
this._open = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
if (this._borderPaintTarget != null) {
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget.disconnect(this._borderDestroyId);
|
||||
this._borderPaintTarget = null;
|
||||
}
|
||||
this.setBorderPaintTarget(null);
|
||||
|
||||
Main.popModal(this._entry);
|
||||
|
||||
|
@ -12,10 +12,11 @@ const Signals = imports.signals;
|
||||
const Main = imports.ui.main;
|
||||
const MagnifierDBus = imports.ui.magnifierDBus;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const PointerWatcher = imports.ui.pointerWatcher;
|
||||
|
||||
const MOUSE_POLL_FREQUENCY = 50;
|
||||
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
||||
const NO_CHANGE = 0.0;
|
||||
|
||||
// Settings
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
@ -24,6 +25,14 @@ const SHOW_KEY = 'screen-magnifier-enabled';
|
||||
const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
|
||||
const SCREEN_POSITION_KEY = 'screen-position';
|
||||
const MAG_FACTOR_KEY = 'mag-factor';
|
||||
const INVERT_LIGHTNESS_KEY = 'invert-lightness';
|
||||
const COLOR_SATURATION_KEY = 'color-saturation';
|
||||
const BRIGHT_RED_KEY = 'brightness-red';
|
||||
const BRIGHT_GREEN_KEY = 'brightness-green';
|
||||
const BRIGHT_BLUE_KEY = 'brightness-blue';
|
||||
const CONTRAST_RED_KEY = 'contrast-red';
|
||||
const CONTRAST_GREEN_KEY = 'contrast-green';
|
||||
const CONTRAST_BLUE_KEY = 'contrast-blue';
|
||||
const LENS_MODE_KEY = 'lens-mode';
|
||||
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
||||
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
||||
@ -47,7 +56,7 @@ const Magnifier = new Lang.Class({
|
||||
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
||||
this._mouseSprite = new Clutter.Texture();
|
||||
xfixesCursor.update_texture_image(this._mouseSprite);
|
||||
this._cursorRoot = new Clutter.Group();
|
||||
this._cursorRoot = new Clutter.Actor();
|
||||
this._cursorRoot.add_actor(this._mouseSprite);
|
||||
|
||||
// Create the first ZoomRegion and initialize it according to the
|
||||
@ -127,11 +136,8 @@ const Magnifier = new Lang.Class({
|
||||
* Turn on mouse tracking, if not already doing so.
|
||||
*/
|
||||
startTrackingMouse: function() {
|
||||
if (!this._mouseTrackingId)
|
||||
this._mouseTrackingId = Mainloop.timeout_add(
|
||||
MOUSE_POLL_FREQUENCY,
|
||||
Lang.bind(this, this.scrollToMousePos)
|
||||
);
|
||||
if (!this._pointerWatch)
|
||||
this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(MOUSE_POLL_FREQUENCY, Lang.bind(this, this.scrollToMousePos));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -139,10 +145,10 @@ const Magnifier = new Lang.Class({
|
||||
* Turn off mouse tracking, if not already doing so.
|
||||
*/
|
||||
stopTrackingMouse: function() {
|
||||
if (this._mouseTrackingId)
|
||||
Mainloop.source_remove(this._mouseTrackingId);
|
||||
if (this._pointerWatch)
|
||||
this._pointerWatch.remove();
|
||||
|
||||
this._mouseTrackingId = null;
|
||||
this._pointerWatch = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -294,8 +300,7 @@ const Magnifier = new Lang.Class({
|
||||
*/
|
||||
setCrosshairsColor: function(color) {
|
||||
if (this._crossHairs) {
|
||||
let clutterColor = new Clutter.Color();
|
||||
clutterColor.from_string(color);
|
||||
let [res, clutterColor] = Clutter.Color.from_string(color);
|
||||
this._crossHairs.setColor(clutterColor);
|
||||
}
|
||||
},
|
||||
@ -443,6 +448,25 @@ const Magnifier = new Lang.Class({
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(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);
|
||||
@ -465,6 +489,25 @@ const Magnifier = new Lang.Class({
|
||||
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
||||
Lang.bind(this, this._updateMouseTrackingMode));
|
||||
|
||||
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
|
||||
Lang.bind(this, this._updateInvertLightness));
|
||||
this._settings.connect('changed::' + COLOR_SATURATION_KEY,
|
||||
Lang.bind(this, this._updateColorSaturation));
|
||||
|
||||
this._settings.connect('changed::' + BRIGHT_RED_KEY,
|
||||
Lang.bind(this, this._updateBrightness));
|
||||
this._settings.connect('changed::' + BRIGHT_GREEN_KEY,
|
||||
Lang.bind(this, this._updateBrightness));
|
||||
this._settings.connect('changed::' + BRIGHT_BLUE_KEY,
|
||||
Lang.bind(this, this._updateBrightness));
|
||||
|
||||
this._settings.connect('changed::' + CONTRAST_RED_KEY,
|
||||
Lang.bind(this, this._updateContrast));
|
||||
this._settings.connect('changed::' + CONTRAST_GREEN_KEY,
|
||||
Lang.bind(this, this._updateContrast));
|
||||
this._settings.connect('changed::' + CONTRAST_BLUE_KEY,
|
||||
Lang.bind(this, this._updateContrast));
|
||||
|
||||
this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
|
||||
Lang.bind(this, function() {
|
||||
this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
|
||||
@ -540,7 +583,47 @@ const Magnifier = new Lang.Class({
|
||||
this._settings.get_enum(MOUSE_TRACKING_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateInvertLightness: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
this._zoomRegions[0].setInvertLightness(
|
||||
this._settings.get_boolean(INVERT_LIGHTNESS_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateColorSaturation: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
this._zoomRegions[0].setColorSaturation(
|
||||
this._settings.get_double(COLOR_SATURATION_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateBrightness: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
let brightness = {};
|
||||
brightness.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
brightness.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
brightness.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
this._zoomRegions[0].setBrightness(brightness);
|
||||
}
|
||||
},
|
||||
|
||||
_updateContrast: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
let contrast = {};
|
||||
contrast.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
contrast.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
this._zoomRegions[0].setContrast(contrast);
|
||||
}
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(Magnifier.prototype);
|
||||
|
||||
@ -554,8 +637,13 @@ const ZoomRegion = new Lang.Class({
|
||||
this._clampScrollingAtEdges = false;
|
||||
this._lensMode = false;
|
||||
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
||||
this._invertLightness = false;
|
||||
this._colorSaturation = 1.0;
|
||||
this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
|
||||
this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
|
||||
|
||||
this._magView = null;
|
||||
this._background = null;
|
||||
this._uiGroupClone = null;
|
||||
this._mouseSourceActor = mouseSourceActor;
|
||||
this._mouseActor = null;
|
||||
@ -565,12 +653,15 @@ const ZoomRegion = new Lang.Class({
|
||||
this._viewPortX = 0;
|
||||
this._viewPortY = 0;
|
||||
this._viewPortWidth = global.screen_width;
|
||||
this._viewPortWidth = global.screen_height;
|
||||
this._viewPortHeight = global.screen_height;
|
||||
this._xCenter = this._viewPortWidth / 2;
|
||||
this._yCenter = this._viewPortHeight / 2;
|
||||
this._xMagFactor = 1;
|
||||
this._yMagFactor = 1;
|
||||
this._followingCursor = false;
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -875,6 +966,107 @@ const ZoomRegion = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* setInvertLightness:
|
||||
* Set whether to invert the lightness of the magnified view.
|
||||
* @flag Boolean to either invert brightness (true), or not (false).
|
||||
*/
|
||||
setInvertLightness: function(flag) {
|
||||
this._invertLightness = flag;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setInvertLightness(this._invertLightness);
|
||||
},
|
||||
|
||||
/**
|
||||
* getInvertLightness:
|
||||
* Retrieve whether the lightness is inverted.
|
||||
* @return Boolean indicating inversion (true), or not (false).
|
||||
*/
|
||||
getInvertLightness: function() {
|
||||
return this._invertLightness;
|
||||
},
|
||||
|
||||
/**
|
||||
* setColorSaturation:
|
||||
* Set the color saturation of the magnified view.
|
||||
* @sauration A value from 0.0 to 1.0 that defines the color
|
||||
* saturation, with 0.0 defining no color (grayscale),
|
||||
* and 1.0 defining full color.
|
||||
*/
|
||||
setColorSaturation: function(saturation) {
|
||||
this._colorSaturation = saturation;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setColorSaturation(this._colorSaturation);
|
||||
},
|
||||
|
||||
/**
|
||||
* getColorSaturation:
|
||||
* Retrieve the color saturation of the magnified view.
|
||||
*/
|
||||
getColorSaturation: function() {
|
||||
return this._colorSaturation;
|
||||
},
|
||||
|
||||
/**
|
||||
* setBrightness:
|
||||
* Alter the brightness of the magnified view.
|
||||
* @brightness Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||
*/
|
||||
setBrightness: function(brightness) {
|
||||
this._brightness.r = brightness.r;
|
||||
this._brightness.g = brightness.g;
|
||||
this._brightness.b = brightness.b;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setBrightness(this._brightness);
|
||||
},
|
||||
|
||||
/**
|
||||
* getBrightness:
|
||||
* Retrive the current brightness of the Zoom Region.
|
||||
* @return Object containing the brightness change for the red, green,
|
||||
* and blue channels.
|
||||
*/
|
||||
getBrightness: function() {
|
||||
let brightness = {};
|
||||
brightness.r = this._brightness.r;
|
||||
brightness.g = this._brightness.g;
|
||||
brightness.b = this._brightness.b;
|
||||
return brightness;
|
||||
},
|
||||
|
||||
/**
|
||||
* setContrast:
|
||||
* Alter the contrast of the magnified view.
|
||||
* @contrast Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
setContrast: function(contrast) {
|
||||
this._contrast.r = contrast.r;
|
||||
this._contrast.g = contrast.g;
|
||||
this._contrast.b = contrast.b;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setContrast(this._contrast);
|
||||
},
|
||||
|
||||
/**
|
||||
* getContrast:
|
||||
* Retreive the contrast of the magnified view.
|
||||
* @return Object containing the contrast for the red, green,
|
||||
* and blue channels.
|
||||
*/
|
||||
getContrast: function() {
|
||||
let contrast = {};
|
||||
contrast.r = this._contrast.r;
|
||||
contrast.g = this._contrast.g;
|
||||
contrast.b = this._contrast.b;
|
||||
return contrast;
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_createActors: function() {
|
||||
@ -885,21 +1077,21 @@ const ZoomRegion = new Lang.Class({
|
||||
// hide the magnified region from CLUTTER_PICK_ALL
|
||||
Shell.util_set_hidden_from_pick (this._magView, true);
|
||||
|
||||
// Append a Clutter.Group to clip the contents of the magnified view.
|
||||
let mainGroup = new Clutter.Group({ clip_to_allocation: true });
|
||||
// Add a group to clip the contents of the magnified view.
|
||||
let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
|
||||
this._magView.set_child(mainGroup);
|
||||
|
||||
// Add a background for when the magnified uiGroup is scrolled
|
||||
// out of view (don't want to see desktop showing through).
|
||||
let background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
|
||||
mainGroup.add_actor(background);
|
||||
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
mainGroup.add_actor(this._background);
|
||||
|
||||
// Clone the group that contains all of UI on the screen. This is the
|
||||
// chrome, the windows, etc.
|
||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
||||
mainGroup.add_actor(this._uiGroupClone);
|
||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
||||
background.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||
// it.
|
||||
@ -913,6 +1105,13 @@ const ZoomRegion = new Lang.Class({
|
||||
this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
|
||||
else
|
||||
this._crossHairsActor = null;
|
||||
|
||||
// Contrast and brightness effects.
|
||||
this._magShaderEffects = new MagShaderEffects(this._uiGroupClone);
|
||||
this._magShaderEffects.setColorSaturation(this._colorSaturation);
|
||||
this._magShaderEffects.setInvertLightness(this._invertLightness);
|
||||
this._magShaderEffects.setBrightness(this._brightness);
|
||||
this._magShaderEffects.setContrast(this._contrast);
|
||||
},
|
||||
|
||||
_destroyActors: function() {
|
||||
@ -921,8 +1120,11 @@ const ZoomRegion = new Lang.Class({
|
||||
if (this._crossHairs)
|
||||
this._crossHairs.removeFromParent(this._crossHairsActor);
|
||||
|
||||
this._magShaderEffects.destroyEffects();
|
||||
this._magShaderEffects = null;
|
||||
this._magView.destroy();
|
||||
this._magView = null;
|
||||
this._background = null;
|
||||
this._uiGroupClone = null;
|
||||
this._mouseActor = null;
|
||||
this._crossHairsActor = null;
|
||||
@ -1123,7 +1325,7 @@ const ZoomRegion = new Lang.Class({
|
||||
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
|
||||
|
||||
let [x, y] = this._screenToViewPort(0, 0);
|
||||
this._uiGroupClone.set_position(x, y);
|
||||
this._uiGroupClone.set_position(Math.round(x), Math.round(y));
|
||||
|
||||
this._updateMousePosition();
|
||||
},
|
||||
@ -1145,6 +1347,21 @@ const ZoomRegion = new Lang.Class({
|
||||
this._crossHairsActor.set_position(xMagMouse - groupWidth / 2,
|
||||
yMagMouse - groupHeight / 2);
|
||||
}
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
if (!this.isActive())
|
||||
return;
|
||||
|
||||
this._background.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
||||
this._setViewPort({ x: this._viewPortX,
|
||||
y: this._viewPortY,
|
||||
width: this._viewPortWidth,
|
||||
height: this._viewPortHeight });
|
||||
else
|
||||
this.setScreenPosition(this._screenPosition);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1159,15 +1376,15 @@ const Crosshairs = new Lang.Class({
|
||||
let groupWidth = global.screen_width * 3;
|
||||
let groupHeight = global.screen_height * 3;
|
||||
|
||||
this._actor = new Clutter.Group({
|
||||
this._actor = new Clutter.Actor({
|
||||
clip_to_allocation: false,
|
||||
width: groupWidth,
|
||||
height: groupHeight
|
||||
});
|
||||
this._horizLeftHair = new Clutter.Rectangle();
|
||||
this._horizRightHair = new Clutter.Rectangle();
|
||||
this._vertTopHair = new Clutter.Rectangle();
|
||||
this._vertBottomHair = new Clutter.Rectangle();
|
||||
this._horizLeftHair = new Clutter.Actor();
|
||||
this._horizRightHair = new Clutter.Actor();
|
||||
this._vertTopHair = new Clutter.Actor();
|
||||
this._vertBottomHair = new Clutter.Actor();
|
||||
this._actor.add_actor(this._horizLeftHair);
|
||||
this._actor.add_actor(this._horizRightHair);
|
||||
this._actor.add_actor(this._vertTopHair);
|
||||
@ -1175,6 +1392,14 @@ const Crosshairs = new Lang.Class({
|
||||
this._clipSize = [0, 0];
|
||||
this._clones = [];
|
||||
this.reCenter();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
this._actor.set_size(global.screen_width * 3, global.screen_height * 3);
|
||||
this.reCenter();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1199,10 +1424,7 @@ const Crosshairs = new Lang.Class({
|
||||
crosshairsActor = new Clutter.Clone({ source: this._actor });
|
||||
this._clones.push(crosshairsActor);
|
||||
}
|
||||
if (this._actor.visible)
|
||||
crosshairsActor.show();
|
||||
else
|
||||
crosshairsActor.hide();
|
||||
crosshairsActor.visible = this._actor.visible;
|
||||
|
||||
container.add_actor(crosshairsActor);
|
||||
container.raise_child(magnifiedMouse, crosshairsActor);
|
||||
@ -1233,10 +1455,10 @@ const Crosshairs = new Lang.Class({
|
||||
* @clutterColor: The color as a Clutter.Color.
|
||||
*/
|
||||
setColor: function(clutterColor) {
|
||||
this._horizLeftHair.set_color(clutterColor);
|
||||
this._horizRightHair.set_color(clutterColor);
|
||||
this._vertTopHair.set_color(clutterColor);
|
||||
this._vertBottomHair.set_color(clutterColor);
|
||||
this._horizLeftHair.background_color = clutterColor;
|
||||
this._horizRightHair.background_color = clutterColor;
|
||||
this._vertTopHair.background_color = clutterColor;
|
||||
this._vertBottomHair.background_color = clutterColor;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1245,9 +1467,7 @@ const Crosshairs = new Lang.Class({
|
||||
* @color: The color as a Clutter.Color.
|
||||
*/
|
||||
getColor: function() {
|
||||
let clutterColor = new Clutter.Color();
|
||||
this._horizLeftHair.get_color(clutterColor);
|
||||
return clutterColor;
|
||||
return this._horizLeftHair.get_color();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1407,3 +1627,144 @@ const Crosshairs = new Lang.Class({
|
||||
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
|
||||
}
|
||||
});
|
||||
|
||||
const MagShaderEffects = new Lang.Class({
|
||||
Name: 'MagShaderEffects',
|
||||
|
||||
_init: function(uiGroupClone) {
|
||||
this._inverse = new Shell.InvertLightnessEffect();
|
||||
this._brightnessContrast = new Clutter.BrightnessContrastEffect();
|
||||
this._colorDesaturation = new Clutter.DesaturateEffect();
|
||||
this._inverse.set_enabled(false);
|
||||
this._brightnessContrast.set_enabled(false);
|
||||
|
||||
this._magView = uiGroupClone;
|
||||
this._magView.add_effect(this._inverse);
|
||||
this._magView.add_effect(this._brightnessContrast);
|
||||
this._magView.add_effect(this._colorDesaturation);
|
||||
},
|
||||
|
||||
/**
|
||||
* destroyEffects:
|
||||
* Remove contrast and brightness effects from the magnified view, and
|
||||
* lose the reference to the actor they were applied to. Don't use this
|
||||
* object after calling this.
|
||||
*/
|
||||
destroyEffects: function() {
|
||||
this._magView.clear_effects();
|
||||
this._colorDesaturation = null;
|
||||
this._brightnessContrast = null;
|
||||
this._inverse = null;
|
||||
this._magView = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* setInvertLightness:
|
||||
* Enable/disable invert lightness effect.
|
||||
* @invertFlag: Enabled flag.
|
||||
*/
|
||||
setInvertLightness: function(invertFlag) {
|
||||
this._inverse.set_enabled(invertFlag);
|
||||
},
|
||||
|
||||
/**
|
||||
* getInvertLightness:
|
||||
* Report whether the inversion effect is enabled.
|
||||
* @return: Boolean.
|
||||
*/
|
||||
getInvertLightness: function() {
|
||||
return this._inverse.get_enabled();
|
||||
},
|
||||
|
||||
setColorSaturation: function(factor) {
|
||||
this._colorDesaturation.set_factor(1.0 - factor);
|
||||
},
|
||||
|
||||
getColorSaturation: function() {
|
||||
return 1.0 - this._colorDesaturation.get_factor();
|
||||
},
|
||||
|
||||
/**
|
||||
* setBrightness:
|
||||
* Set the brightness of the magnified view.
|
||||
* @brightness: Object containing the brightness for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness,
|
||||
* respectively.
|
||||
*/
|
||||
setBrightness: function(brightness) {
|
||||
let bRed = brightness.r;
|
||||
let bGreen = brightness.g;
|
||||
let bBlue = brightness.b;
|
||||
this._brightnessContrast.set_brightness_full(bRed, bGreen, bBlue);
|
||||
|
||||
// Enable the effect if the brightness OR contrast change are such that
|
||||
// it modifies the brightness and/or contrast.
|
||||
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||
this._brightnessContrast.set_enabled(
|
||||
(bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
|
||||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* getBrightness:
|
||||
* Retrieve current brightness of the magnified view.
|
||||
* @return: Object containing the brightness for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||
*/
|
||||
getBrightness: function() {
|
||||
let result = {};
|
||||
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||
result.r = bRed;
|
||||
result.g = bGreen;
|
||||
result.b = bBlue;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the contrast of the magnified view.
|
||||
* @contrast: Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
setContrast: function(contrast) {
|
||||
let cRed = contrast.r;
|
||||
let cGreen = contrast.g;
|
||||
let cBlue = contrast.b;
|
||||
|
||||
this._brightnessContrast.set_contrast_full(cRed, cGreen, cBlue);
|
||||
|
||||
// Enable the effect if the contrast OR brightness change are such that
|
||||
// it modifies the brightness and/or contrast.
|
||||
// should be able to use Clutter.color_equal(), but that complains of
|
||||
// a null first argument.
|
||||
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||
this._brightnessContrast.set_enabled(
|
||||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE ||
|
||||
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve current contrast of the magnified view.
|
||||
* @return: Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
getContrast: function() {
|
||||
let resutl = {};
|
||||
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||
result.r = cRed;
|
||||
result.g = cGreen;
|
||||
result.b = cBlue;
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
393
js/ui/main.js
@ -10,153 +10,108 @@ const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AutomountManager = imports.ui.automountManager;
|
||||
const AutorunManager = imports.ui.autorunManager;
|
||||
const Components = imports.ui.components;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const Keyboard = imports.ui.keyboard;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const Params = imports.misc.params;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const Layout = imports.ui.layout;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const NetworkAgent = imports.ui.networkAgent;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
const ScreenShield = imports.ui.screenShield;
|
||||
const Scripting = imports.ui.scripting;
|
||||
const SessionMode = imports.ui.sessionMode;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const TelepathyClient = imports.ui.telepathyClient;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const UnlockDialog = imports.ui.unlockDialog;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
|
||||
let automountManager = null;
|
||||
let autorunManager = null;
|
||||
const KeybindingMode = {
|
||||
NONE: 0, // block all keybindings
|
||||
NORMAL: 1 << 0, // window mode
|
||||
OVERVIEW: 1 << 1,
|
||||
LOCK_SCREEN: 1 << 2,
|
||||
UNLOCK_SCREEN: 1 << 3,
|
||||
LOGIN_SCREEN: 1 << 4,
|
||||
MESSAGE_TRAY: 1 << 5,
|
||||
SYSTEM_MODAL: 1 << 6,
|
||||
LOOKING_GLASS: 1 << 7,
|
||||
ALL: ~0,
|
||||
};
|
||||
|
||||
let componentManager = null;
|
||||
let panel = null;
|
||||
let hotCorners = [];
|
||||
let placesManager = null;
|
||||
let overview = null;
|
||||
let runDialog = null;
|
||||
let lookingGlass = null;
|
||||
let wm = null;
|
||||
let messageTray = null;
|
||||
let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let telepathyClient = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let recorder = null;
|
||||
let sessionMode = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
let modalCount = 0;
|
||||
let keybindingMode = KeybindingMode.NORMAL;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
let xdndHandler = null;
|
||||
let statusIconDispatcher = null;
|
||||
let keyboard = null;
|
||||
let layoutManager = null;
|
||||
let networkAgent = null;
|
||||
let _errorLogStack = [];
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _gdmCssStylesheet = null;
|
||||
let _overridesSettings = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
function _createUserSession() {
|
||||
// Load the calendar server. Note that we are careful about
|
||||
// not loading any events until the user presses the clock
|
||||
global.launch_calendar_server();
|
||||
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
automountManager = new AutomountManager.AutomountManager();
|
||||
autorunManager = new AutorunManager.AutorunManager();
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
}
|
||||
|
||||
function _createGDMSession() {
|
||||
// We do this this here instead of at the top to prevent GDM
|
||||
// related code from getting loaded in normal user sessions
|
||||
const LoginDialog = imports.gdm.loginDialog;
|
||||
|
||||
let loginDialog = new LoginDialog.LoginDialog();
|
||||
loginDialog.connect('loaded', function() {
|
||||
loginDialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
function _initRecorder() {
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
|
||||
global.screen.connect('toggle-recording', function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
if (recorder.is_recording()) {
|
||||
recorder.pause();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
|
||||
let pipeline = recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _initUserSession() {
|
||||
_initRecorder();
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
||||
getRunDialog().open();
|
||||
});
|
||||
|
||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
function _sessionUpdated() {
|
||||
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
||||
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW);
|
||||
|
||||
wm.setCustomKeybindingHandler('panel-run-dialog',
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
if (sessionMode.isGreeter)
|
||||
screenShield.showDialog();
|
||||
}
|
||||
|
||||
function start() {
|
||||
// Monkey patch utility functions into the global proxy;
|
||||
// This is easier and faster than indirecting down into global
|
||||
// if we want to call back up into JS.
|
||||
global.logError = _logError;
|
||||
global.log = _logDebug;
|
||||
// These are here so we don't break compatibility.
|
||||
global.logError = window.log;
|
||||
global.log = window.log;
|
||||
|
||||
// Chain up async errors reported from C
|
||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
sessionMode = new SessionMode.SessionMode();
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
@ -166,9 +121,11 @@ function start() {
|
||||
// and recalculate application associations, so to avoid
|
||||
// races for now we initialize it here. It's better to
|
||||
// be predictable anyways.
|
||||
Shell.WindowTracker.get_default();
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
Shell.AppUsage.get_default();
|
||||
|
||||
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
|
||||
|
||||
// The stage is always covered so Clutter doesn't need to clear it; however
|
||||
// the color is used as the default contents for the Mutter root background
|
||||
// actor so set it anyways.
|
||||
@ -176,7 +133,6 @@ function start() {
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
|
||||
loadTheme();
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
@ -187,7 +143,16 @@ function start() {
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
});
|
||||
St.set_ui_root(global.stage, uiGroup);
|
||||
uiGroup.connect('get-preferred-width',
|
||||
function(actor, forHeight, alloc) {
|
||||
let width = global.stage.width;
|
||||
[alloc.min_size, alloc.natural_size] = [width, width];
|
||||
});
|
||||
uiGroup.connect('get-preferred-height',
|
||||
function(actor, forWidth, alloc) {
|
||||
let height = global.stage.height;
|
||||
[alloc.min_size, alloc.natural_size] = [height, height];
|
||||
});
|
||||
global.window_group.reparent(uiGroup);
|
||||
global.overlay_group.reparent(uiGroup);
|
||||
global.stage.add_actor(uiGroup);
|
||||
@ -195,44 +160,36 @@ function start() {
|
||||
layoutManager = new Layout.LayoutManager();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
// This overview object is just a stub for non-user sessions
|
||||
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
||||
panel = new Panel.Panel();
|
||||
overview = new Overview.Overview();
|
||||
wm = new WindowManager.WindowManager();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
if (UnlockDialog.isSupported())
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
else
|
||||
screenShield = new ScreenShield.ScreenShieldFallback();
|
||||
panel = new Panel.Panel();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
_createUserSession();
|
||||
else if (global.session_type == Shell.SessionType.GDM)
|
||||
_createGDMSession();
|
||||
|
||||
panel.startStatusArea();
|
||||
componentManager = new Components.ComponentManager();
|
||||
|
||||
layoutManager.init();
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
_initUserSession();
|
||||
statusIconDispatcher.start(messageTray.actor);
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
sessionMode.connect('updated', _sessionUpdated);
|
||||
_sessionUpdated();
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
|
||||
// Attempt to become a PolicyKit authentication agent
|
||||
PolkitAuthenticationAgent.init()
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
|
||||
_log('info', 'loaded at ' + _startDate);
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
|
||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||
@ -242,6 +199,9 @@ function start() {
|
||||
Scripting.runPerfScript(module, perfOutput);
|
||||
}
|
||||
|
||||
_overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
|
||||
_overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
|
||||
|
||||
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
||||
|
||||
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
||||
@ -249,6 +209,9 @@ function start() {
|
||||
global.screen.connect('restacked', _windowsRestacked);
|
||||
|
||||
_nWorkspacesChanged();
|
||||
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.init();
|
||||
}
|
||||
|
||||
let _workspaces = [];
|
||||
@ -266,17 +229,30 @@ function _checkWorkspaces() {
|
||||
let i;
|
||||
let emptyWorkspaces = [];
|
||||
|
||||
if (!Meta.prefs_get_dynamic_workspaces()) {
|
||||
_checkWorkspacesId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < _workspaces.length; i++) {
|
||||
let lastRemoved = _workspaces[i]._lastRemovedWindow;
|
||||
if (lastRemoved &&
|
||||
if ((lastRemoved &&
|
||||
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
||||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG))
|
||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
||||
_workspaces[i]._keepAliveId)
|
||||
emptyWorkspaces[i] = false;
|
||||
else
|
||||
emptyWorkspaces[i] = true;
|
||||
}
|
||||
|
||||
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
||||
for (i = 0; i < sequences.length; i++) {
|
||||
let index = sequences[i].get_workspace();
|
||||
if (index >= 0 && index <= global.screen.n_workspaces)
|
||||
emptyWorkspaces[index] = false;
|
||||
}
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
for (i = 0; i < windows.length; i++) {
|
||||
let win = windows[i];
|
||||
@ -324,6 +300,17 @@ function _checkWorkspaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function keepWorkspaceAlive(workspace, duration) {
|
||||
if (workspace._keepAliveId)
|
||||
Mainloop.source_remove(workspace._keepAliveId);
|
||||
|
||||
workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
|
||||
workspace._keepAliveId = 0;
|
||||
_queueCheckWorkspaces();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function _windowRemoved(workspace, window) {
|
||||
workspace._lastRemovedWindow = window;
|
||||
_queueCheckWorkspaces();
|
||||
@ -448,9 +435,6 @@ function loadTheme() {
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||
|
||||
if (global.session_type == Shell.SessionType.GDM)
|
||||
theme.load_stylesheet(_gdmCssStylesheet);
|
||||
|
||||
if (previousTheme) {
|
||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||
|
||||
@ -491,59 +475,6 @@ function notifyError(msg, details) {
|
||||
notify(msg, details);
|
||||
}
|
||||
|
||||
/**
|
||||
* _log:
|
||||
* @category: string message type ('info', 'error')
|
||||
* @msg: A message string
|
||||
* ...: Any further arguments are converted into JSON notation,
|
||||
* and appended to the log message, separated by spaces.
|
||||
*
|
||||
* Log a message into the LookingGlass error
|
||||
* stream. This is primarily intended for use by the
|
||||
* extension system as well as debugging.
|
||||
*/
|
||||
function _log(category, msg) {
|
||||
let text = msg;
|
||||
if (arguments.length > 2) {
|
||||
text += ': ';
|
||||
for (let i = 2; i < arguments.length; i++) {
|
||||
text += JSON.stringify(arguments[i]);
|
||||
if (i < arguments.length - 1)
|
||||
text += ' ';
|
||||
}
|
||||
}
|
||||
_errorLogStack.push({timestamp: new Date().getTime(),
|
||||
category: category,
|
||||
message: text });
|
||||
}
|
||||
|
||||
function _logError(msg) {
|
||||
return _log('error', msg);
|
||||
}
|
||||
|
||||
function _logDebug(msg) {
|
||||
return _log('debug', msg);
|
||||
}
|
||||
|
||||
// Used by the error display in lookingGlass.js
|
||||
function _getAndClearErrorStack() {
|
||||
let errors = _errorLogStack;
|
||||
_errorLogStack = [];
|
||||
return errors;
|
||||
}
|
||||
|
||||
function logStackTrace(msg) {
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
// e.stack must have at least two lines, with the first being
|
||||
// logStackTrace() (which we strip off), and the second being
|
||||
// our caller.
|
||||
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
|
||||
log(msg ? (msg + '\n' + trace) : trace);
|
||||
}
|
||||
}
|
||||
|
||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||
return win.get_workspace() == workspaceIndex ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
@ -555,73 +486,6 @@ function getWindowActorsForWorkspace(workspaceIndex) {
|
||||
});
|
||||
}
|
||||
|
||||
// This function encapsulates hacks to make certain global keybindings
|
||||
// work even when we are in one of our modes where global keybindings
|
||||
// are disabled with a global grab. (When there is a global grab, then
|
||||
// all key events will be delivered to the stage, so ::captured-event
|
||||
// on the stage can be used for global keybindings.)
|
||||
function _globalKeyPressHandler(actor, event) {
|
||||
if (modalCount == 0)
|
||||
return false;
|
||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
||||
return false;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
let keyCode = event.get_key_code();
|
||||
let modifierState = Shell.get_event_state(event);
|
||||
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
||||
|
||||
// Other bindings are only available to the user session when the overview is up and
|
||||
// no modal dialog is present.
|
||||
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
|
||||
return false;
|
||||
|
||||
// This isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
overview.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
|
||||
modifierState);
|
||||
return true;
|
||||
}
|
||||
|
||||
// None of the other bindings are relevant outside of the user's session
|
||||
if (global.session_type != Shell.SessionType.USER)
|
||||
return false;
|
||||
|
||||
switch (action) {
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing; we also disable them in the main view.
|
||||
//
|
||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
// wm.actionMoveWorkspaceLeft();
|
||||
// return true;
|
||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
// wm.actionMoveWorkspaceRight();
|
||||
// return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
||||
wm.actionMoveWorkspaceUp();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
||||
wm.actionMoveWorkspaceDown();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
||||
case Meta.KeyBindingAction.COMMAND_2:
|
||||
getRunDialog().open();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
overview.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _findModal(actor) {
|
||||
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
||||
if (modalActorFocusStack[i].actor == actor)
|
||||
@ -630,10 +494,14 @@ function _findModal(actor) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function isInModalStack(actor) {
|
||||
return _findModal(actor) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushModal:
|
||||
* @actor: #ClutterActor which will be given keyboard focus
|
||||
* @timestamp: optional timestamp
|
||||
* @params: optional parameters
|
||||
*
|
||||
* Ensure we are in a mode where all keyboard and mouse input goes to
|
||||
* the stage, and focus @actor. Multiple calls to this function act in
|
||||
@ -644,24 +512,31 @@ function _findModal(actor) {
|
||||
* modal stack returns to this actor, reset the focus to the actor
|
||||
* which was focused at the time pushModal() was invoked.
|
||||
*
|
||||
* @timestamp is optionally used to associate the call with a specific user
|
||||
* initiated event. If not provided then the value of
|
||||
* @params may be used to provide the following parameters:
|
||||
* - timestamp: used to associate the call with a specific user initiated
|
||||
* event. If not provided then the value of
|
||||
* global.get_current_time() is assumed.
|
||||
*
|
||||
* @options: optional Meta.ModalOptions flags to indicate that the
|
||||
* pointer is alrady grabbed
|
||||
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
||||
* already grabbed
|
||||
*
|
||||
* - keybindingMode: used to set the current Main.KeybindingMode to filter
|
||||
* global keybindings; the default of NONE will filter
|
||||
* out all keybindings
|
||||
*
|
||||
* Returns: true iff we successfully acquired a grab or already had one
|
||||
*/
|
||||
function pushModal(actor, timestamp, options) {
|
||||
if (timestamp == undefined)
|
||||
timestamp = global.get_current_time();
|
||||
function pushModal(actor, params) {
|
||||
params = Params.parse(params, { timestamp: global.get_current_time(),
|
||||
options: 0,
|
||||
keybindingMode: KeybindingMode.NONE });
|
||||
|
||||
if (modalCount == 0) {
|
||||
if (!global.begin_modal(timestamp, options ? options : 0)) {
|
||||
if (!global.begin_modal(params.timestamp, params.options)) {
|
||||
log('pushModal: invocation of begin_modal failed');
|
||||
return false;
|
||||
}
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
}
|
||||
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
||||
@ -670,7 +545,7 @@ function pushModal(actor, timestamp, options) {
|
||||
let actorDestroyId = actor.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack.splice(index, 1);
|
||||
popModal(actor);
|
||||
});
|
||||
let curFocus = global.stage.get_key_focus();
|
||||
let curFocusDestroyId;
|
||||
@ -684,8 +559,10 @@ function pushModal(actor, timestamp, options) {
|
||||
modalActorFocusStack.push({ actor: actor,
|
||||
focus: curFocus,
|
||||
destroyId: actorDestroyId,
|
||||
focusDestroyId: curFocusDestroyId });
|
||||
focusDestroyId: curFocusDestroyId,
|
||||
keybindingMode: keybindingMode });
|
||||
|
||||
keybindingMode = params.keybindingMode;
|
||||
global.stage.set_key_focus(actor);
|
||||
return true;
|
||||
}
|
||||
@ -712,6 +589,7 @@ function popModal(actor, timestamp) {
|
||||
global.stage.set_key_focus(null);
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
keybindingMode = KeybindingMode.NORMAL;
|
||||
|
||||
throw new Error('incorrect pop');
|
||||
}
|
||||
@ -724,6 +602,7 @@ function popModal(actor, timestamp) {
|
||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||
if (record.focus)
|
||||
record.focus.disconnect(record.focusDestroyId);
|
||||
keybindingMode = record.keybindingMode;
|
||||
global.stage.set_key_focus(record.focus);
|
||||
} else {
|
||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||
@ -733,6 +612,7 @@ function popModal(actor, timestamp) {
|
||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
||||
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
||||
}
|
||||
}
|
||||
modalActorFocusStack.splice(focusIndex, 1);
|
||||
@ -742,6 +622,8 @@ function popModal(actor, timestamp) {
|
||||
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
keybindingMode = KeybindingMode.NORMAL;
|
||||
}
|
||||
|
||||
function createLookingGlass() {
|
||||
@ -751,11 +633,11 @@ function createLookingGlass() {
|
||||
return lookingGlass;
|
||||
}
|
||||
|
||||
function getRunDialog() {
|
||||
function openRunDialog() {
|
||||
if (runDialog == null) {
|
||||
runDialog = new RunDialog.RunDialog();
|
||||
}
|
||||
return runDialog;
|
||||
runDialog.open();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -884,7 +766,8 @@ function initializeDeferredWork(actor, callback, props) {
|
||||
function queueDeferredWork(workId) {
|
||||
let data = _deferredWorkData[workId];
|
||||
if (!data) {
|
||||
global.logError('invalid work id ', workId);
|
||||
let message = 'Invalid work id %d'.format(workId);
|
||||
logError(new Error(message), message);
|
||||
return;
|
||||
}
|
||||
if (_deferredWorkQueue.indexOf(workId) < 0)
|
||||
|