Compare commits
1222 Commits
Author | SHA1 | Date | |
---|---|---|---|
7d598b7725 | |||
9f890982b2 | |||
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 |
8
.gitignore
vendored
@ -16,6 +16,7 @@ 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
|
||||
@ -37,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
|
||||
@ -44,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
|
||||
@ -62,6 +66,8 @@ 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
|
||||
@ -80,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
|
||||
|
505
NEWS
@ -1,3 +1,508 @@
|
||||
3.7.3.1
|
||||
=======
|
||||
* Revert 490206b to not depend on NMGTK-0.9.7, which hasn't been released yet
|
||||
|
||||
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]
|
||||
|
@ -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 4
|
||||
#define PLUGIN_API_VERSION 5
|
||||
|
||||
typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
@ -153,8 +153,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
||||
/* global initialization routine, called once when plugin
|
||||
is loaded */
|
||||
|
||||
g_type_init ();
|
||||
|
||||
g_debug ("plugin loaded");
|
||||
|
||||
memcpy (&funcs, pfuncs, sizeof (funcs));
|
||||
@ -225,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)
|
||||
@ -373,39 +371,14 @@ plugin_object_deallocate (NPObject *npobj)
|
||||
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 onrestart_id;
|
||||
static NPIdentifier get_errors_id;
|
||||
static NPIdentifier launch_extension_prefs_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 ||
|
||||
name == launch_extension_prefs_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;
|
||||
|
||||
@ -469,8 +442,73 @@ jsonify_variant (GVariant *variant,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_list_extensions (PluginObject *obj,
|
||||
NPVariant *result)
|
||||
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;
|
||||
GVariant *res;
|
||||
@ -494,21 +532,20 @@ plugin_list_extensions (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_enable_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
gboolean enabled)
|
||||
plugin_enable_extension (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gboolean ret;
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
gchar *uuid;
|
||||
gboolean enabled;
|
||||
gsize length;
|
||||
gchar **uuids;
|
||||
const gchar **new_uuids;
|
||||
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("ub", argc, argv, &uuid, &enabled))
|
||||
return FALSE;
|
||||
|
||||
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
|
||||
length = g_strv_length (uuids);
|
||||
@ -517,7 +554,7 @@ plugin_enable_extension (PluginObject *obj,
|
||||
{
|
||||
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
|
||||
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
|
||||
new_uuids[length] = uuid_str;
|
||||
new_uuids[length] = uuid;
|
||||
new_uuids[length + 1] = NULL;
|
||||
}
|
||||
else
|
||||
@ -526,7 +563,7 @@ plugin_enable_extension (PluginObject *obj,
|
||||
new_uuids = g_new (const gchar *, length);
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
if (g_str_equal (uuids[i], uuid_str))
|
||||
if (g_str_equal (uuids[i], uuid))
|
||||
continue;
|
||||
|
||||
new_uuids[j] = uuids[i];
|
||||
@ -542,63 +579,112 @@ plugin_enable_extension (PluginObject *obj,
|
||||
|
||||
g_strfreev (uuids);
|
||||
g_free (new_uuids);
|
||||
g_free (uuid_str);
|
||||
g_free (uuid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_install_extension (PluginObject *obj,
|
||||
NPString uuid)
|
||||
{
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
typedef struct _AsyncClosure AsyncClosure;
|
||||
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
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)
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
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,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid;
|
||||
NPObject *callback, *errback;
|
||||
AsyncClosure *async_closure;
|
||||
|
||||
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
|
||||
return FALSE;
|
||||
|
||||
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 ("(s)", uuid_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 (uuid);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_uninstall_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_uninstall_extension (PluginObject *obj,
|
||||
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);
|
||||
return FALSE;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -611,30 +697,27 @@ plugin_uninstall_extension (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_get_info (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_get_info (PluginObject *obj,
|
||||
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);
|
||||
return FALSE;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -647,31 +730,26 @@ plugin_get_info (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_get_errors (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_get_errors (PluginObject *obj,
|
||||
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);
|
||||
return FALSE;
|
||||
}
|
||||
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);
|
||||
@ -683,29 +761,25 @@ plugin_get_errors (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_launch_extension_prefs (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_launch_extension_prefs (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
"LaunchExtensionPrefs",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
|
||||
g_free (uuid_str);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -759,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)
|
||||
{
|
||||
@ -774,59 +878,13 @@ plugin_object_invoke (NPObject *npobj,
|
||||
|
||||
VOID_TO_NPVARIANT (*result);
|
||||
|
||||
if (!plugin_object_has_method (npobj, name))
|
||||
return FALSE;
|
||||
#define METHOD(x) \
|
||||
if (name == x##_id) \
|
||||
return plugin_##x (obj, argc, argv, result);
|
||||
METHODS
|
||||
#undef METHOD
|
||||
|
||||
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;
|
||||
|
||||
return plugin_install_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]));
|
||||
}
|
||||
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);
|
||||
}
|
||||
else if (name == launch_extension_prefs_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_launch_extension_prefs (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
result);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
64
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.5.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.3.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -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,15 +46,15 @@ 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"
|
||||
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)
|
||||
@ -60,13 +62,12 @@ fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.9.16
|
||||
CLUTTER_MIN_VERSION=1.11.11
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.5.3
|
||||
FOLKS_MIN_VERSION=0.5.2
|
||||
MUTTER_MIN_VERSION=3.7.3
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.31.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
|
||||
@ -75,30 +76,15 @@ 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.5.1
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
|
||||
AC_ARG_WITH(folks,
|
||||
AS_HELP_STRING([--with-folks],
|
||||
[Enable folks support]),
|
||||
[with_folks=$withval], [with_folks=yes])
|
||||
|
||||
if test x${with_folks} = xyes; then
|
||||
FOLKS_REQUIREMENT="folks >= $FOLKS_MIN_VERSION"
|
||||
AC_DEFINE([HAVE_FOLKS], [1], [folks support])
|
||||
AC_SUBST([HAVE_FOLKS],[1])
|
||||
else
|
||||
FOLKS_REQUIREMENT=
|
||||
AC_SUBST([HAVE_FOLKS],[0])
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_WITH_FOLKS, test x${with_folks} = xyes)
|
||||
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
|
||||
atk-bridge-2.0
|
||||
$FOLKS_REQUIREMENT
|
||||
libmutter >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
@ -114,8 +100,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util gnome-keyring-1
|
||||
gcr-3 >= $GCR_MIN_VERSION
|
||||
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
|
||||
gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
@ -123,6 +108,9 @@ 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)
|
||||
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
@ -134,10 +122,10 @@ CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11)
|
||||
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 >= 3.5.1)
|
||||
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],
|
||||
@ -153,7 +141,7 @@ 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)
|
||||
|
||||
@ -222,6 +210,18 @@ 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,
|
||||
@ -266,7 +266,9 @@ AC_CONFIG_FILES([
|
||||
docs/reference/st/Makefile
|
||||
docs/reference/st/st-docs.sgml
|
||||
js/Makefile
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/Makefile
|
||||
src/gvc/Makefile
|
||||
browser-plugin/Makefile
|
||||
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>
|
||||
|
@ -8,17 +8,12 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
# Placeholder until we add intltool
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
|
||||
searchprovidersdir = $(pkgdatadir)/open-search-providers
|
||||
dist_searchproviders_DATA = \
|
||||
open-search-providers/google.xml \
|
||||
open-search-providers/wikipedia.xml
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = org.gnome.ShellSearchProvider.xml
|
||||
introspection_DATA = \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
themedir = $(pkgdatadir)/theme
|
||||
dist_theme_DATA = \
|
||||
@ -36,22 +31,31 @@ 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/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
|
||||
|
||||
@ -73,24 +77,20 @@ 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) \
|
||||
$(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 \
|
||||
org.gnome.shell.gschema.valid \
|
||||
|
@ -1,7 +0,0 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Google</ShortName>
|
||||
<Description>Google Search</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
|
||||
</OpenSearchDescription>
|
@ -1,44 +0,0 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Wikipedia</ShortName>
|
||||
<Description>Wikipedia, the free encyclopedia</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
|
||||
<!-- The criterion for being below is being listed with more than 100,000
|
||||
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
|
||||
<Language>ar</Language>
|
||||
<Language>bg</Language>
|
||||
<Language>ca</Language>
|
||||
<Language>cs</Language>
|
||||
<Language>da</Language>
|
||||
<Language>de</Language>
|
||||
<Language>en</Language>
|
||||
<Language>eo</Language>
|
||||
<Language>es</Language>
|
||||
<Language>fa</Language>
|
||||
<Language>fi</Language>
|
||||
<Language>fr</Language>
|
||||
<Language>he</Language>
|
||||
<Language>hu</Language>
|
||||
<Language>id</Language>
|
||||
<Language>it</Language>
|
||||
<Language>ja</Language>
|
||||
<Language>ko</Language>
|
||||
<Language>lt</Language>
|
||||
<Language>nl</Language>
|
||||
<Language>no</Language>
|
||||
<Language>pl</Language>
|
||||
<Language>pt</Language>
|
||||
<Language>ro</Language>
|
||||
<Language>ru</Language>
|
||||
<Language>sk</Language>
|
||||
<Language>sl</Language>
|
||||
<Language>sr</Language>
|
||||
<Language>sv</Language>
|
||||
<Language>tr</Language>
|
||||
<Language>uk</Language>
|
||||
<Language>vi</Language>
|
||||
<Language>vo</Language>
|
||||
<Language>war</Language>
|
||||
<Language>zh</Language>
|
||||
</OpenSearchDescription>
|
@ -2,146 +2,72 @@
|
||||
'-//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">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
The interface used for integrating into GNOME Shell's search
|
||||
interface.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
|
||||
<!--
|
||||
GetInitialResultSet:
|
||||
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
|
||||
|
||||
Called when the user first begins a search.
|
||||
-->
|
||||
<method name="GetInitialResultSet">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Called when the user first begins a search.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
<arg type="as" direction="in">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
Array of search terms, which the provider should treat as
|
||||
logical AND.
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<arg type="as" direction="out">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
An array of result identifier strings representing items which
|
||||
match the given search terms. Identifiers must be unique within
|
||||
the provider's domain, but other than that may be chosen freely
|
||||
by the provider.
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<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">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Called when a search is performed which is a "subsearch" of
|
||||
the previous search, e.g. the method may return less results, but
|
||||
not more or different results.
|
||||
|
||||
This allows search providers to only search through the previous
|
||||
result set, rather than possibly performing a full re-query.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
<arg type="as" direction="in">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
Array of item identifiers
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<arg type="as" direction="in">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
Array of updated search terms, which the provider should treat as
|
||||
logical AND.
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<arg type="as" direction="out">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
An array of result identifier strings representing items which
|
||||
match the given search terms. Identifiers must be unique within
|
||||
the provider's domain, but other than that may be chosen freely
|
||||
by the provider.
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<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">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Return an array of meta data used to display each given result
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
<arg type="as" direction="in">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
An array of result identifiers as returned by
|
||||
GetInitialResultSet() or GetSubsearchResultSet()
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<arg type="a{sv}" direction="out">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
A dictionary describing the given search result, containing
|
||||
'id', 'name' (both strings) and either 'icon' (a serialized
|
||||
GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
|
||||
height, rowstride, has-alpha, bits per sample, channels, data)
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<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">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Called when the users chooses a given result. The result should
|
||||
be displayed in the application associated with the corresponding
|
||||
provider.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
<arg type="s" direction="in">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
A result identifier as returned by GetInitialResultSet() or
|
||||
GetSubsearchResultSet()
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<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>
|
||||
@ -61,6 +57,29 @@ value here is from the TpConnectionPresenceType enumeration.</_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="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
@ -87,6 +106,28 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
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>
|
||||
@ -129,7 +170,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
|
||||
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
</_description>
|
||||
@ -145,44 +186,80 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</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>
|
||||
<_summary>Workspaces are managed dynamically</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="workspaces-only-on-primary" type="b">
|
||||
<default>true</default>
|
||||
<summary>Workspaces only on primary monitor</summary>
|
||||
<_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,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">
|
||||
<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"
|
||||
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" />
|
||||
<rect
|
||||
y="558.85046"
|
||||
x="468.96878"
|
||||
height="3.1425927"
|
||||
width="28.149134"
|
||||
id="rect2996"
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
</g>
|
||||
transform="translate(-469.08263,-532.99307)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect2996"
|
||||
width="31"
|
||||
height="3"
|
||||
x="468.08264"
|
||||
y="558.99304" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.1 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,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10"
|
||||
height="4"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="scroll-hhandle.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="rect3592"
|
||||
width="2"
|
||||
height="4"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect3592"
|
||||
id="use2825"
|
||||
transform="translate(8,0)"
|
||||
width="10"
|
||||
height="4" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use2825"
|
||||
id="use2827"
|
||||
transform="translate(-4,0)"
|
||||
width="10"
|
||||
height="4" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="4"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="scroll-hhandle.svg">
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="rect3592"
|
||||
width="2"
|
||||
height="4"
|
||||
x="0"
|
||||
y="-4"
|
||||
rx="0"
|
||||
ry="0"
|
||||
transform="matrix(0,1,-1,0,0,0)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect3592"
|
||||
id="use3705"
|
||||
transform="translate(0,4)"
|
||||
width="4"
|
||||
height="10" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use3705"
|
||||
id="use3707"
|
||||
transform="translate(0,4)"
|
||||
width="4"
|
||||
height="10" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
120
data/theme/summary-counter.svg
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
id="Foreground"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 23.272727 23.272727"
|
||||
enable-background="new 0 0 16 16"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="summary-counter.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata2399"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2397"><linearGradient
|
||||
id="linearGradient3173"><stop
|
||||
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3175" /><stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3177" /></linearGradient><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 8 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="16 : 8 : 1"
|
||||
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||
id="perspective2401" /><filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter16494-4"
|
||||
x="-0.20989846"
|
||||
width="1.4197969"
|
||||
y="-0.20903821"
|
||||
height="1.4180764"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.3282637"
|
||||
id="feGaussianBlur16496-8" /></filter><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient16498-6"
|
||||
id="radialGradient16504-1"
|
||||
cx="7.6582627"
|
||||
cy="5.8191104"
|
||||
fx="7.6582627"
|
||||
fy="5.8191104"
|
||||
r="8.6928644"
|
||||
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient16498-6"><stop
|
||||
style="stop-color:#9FD0FF;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop16500-8" /><stop
|
||||
style="stop-color:#3465A4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
|
||||
inkscape:window-height="709"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="15.386407"
|
||||
inkscape:cy="13.739577"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1179"
|
||||
inkscape:current-layer="g16402-8"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-maximized="1"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11246"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
|
||||
style="display:inline"
|
||||
id="g16402-8"
|
||||
transform="translate(4.7533483,2.8238929)"><g
|
||||
id="g3175-4"
|
||||
transform="translate(-0.89995416,0.94028614)"><path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
|
||||
xlink:href="#path2394-32"
|
||||
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
|
||||
id="path16480-5"
|
||||
inkscape:href="#path2394-32"
|
||||
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
|
||||
transform="translate(0,1.028519)" /><path
|
||||
clip-rule="evenodd"
|
||||
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
|
||||
id="path2394-32"
|
||||
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:nodetypes="csssc"
|
||||
inkscape:connector-curvature="0" /><g
|
||||
id="g3172-6" /></g></g></svg>
|
After Width: | Height: | Size: 5.4 KiB |
BIN
data/theme/ws-switch-arrow-down.png
Normal file
After Width: | Height: | Size: 850 B |
@ -1,376 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-down.svg">
|
||||
<metadata
|
||||
id="metadata3353">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="718"
|
||||
inkscape:window-height="480"
|
||||
id="namedview3351"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6979167"
|
||||
inkscape:cx="48"
|
||||
inkscape:cy="48"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg25070" />
|
||||
<defs
|
||||
id="defs25072">
|
||||
<linearGradient
|
||||
x1="-86.552246"
|
||||
y1="185.439"
|
||||
x2="-83.37072"
|
||||
y2="197.31261"
|
||||
id="linearGradient24957"
|
||||
xlink:href="#linearGradient4034-0-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(6,0)" />
|
||||
<linearGradient
|
||||
id="linearGradient4034-0-4">
|
||||
<stop
|
||||
id="stop4036-5-7"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4038-9-6"
|
||||
style="stop-color:#babdb6;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter24765">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix24767" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix24769" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="-74.520325"
|
||||
y1="169.06032"
|
||||
x2="-74.520325"
|
||||
y2="205.94189"
|
||||
id="linearGradient24955"
|
||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-5,0)" />
|
||||
<linearGradient
|
||||
id="linearGradient4632-1-3-9-3-2">
|
||||
<stop
|
||||
id="stop4634-1-8-3-9-0"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4636-1-9-9-8-8"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0.0274937" />
|
||||
<stop
|
||||
id="stop4638-8-3-9-6-6"
|
||||
style="stop-color:#f2f2f2;stop-opacity:1"
|
||||
offset="0.274937" />
|
||||
<stop
|
||||
id="stop4640-8-5-7-8-9"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.38707438" />
|
||||
<stop
|
||||
id="stop4642-5-41-9-6-9"
|
||||
style="stop-color:#d9dad8;stop-opacity:1"
|
||||
offset="0.66528589" />
|
||||
<stop
|
||||
id="stop4644-5-2-7-9-2"
|
||||
style="stop-color:#dfe0dd;stop-opacity:1"
|
||||
offset="0.76745707" />
|
||||
<stop
|
||||
id="stop4646-3-2-3-7-3"
|
||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
r="2.3554697"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
id="radialGradient24959"
|
||||
xlink:href="#linearGradient4869-4-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-1">
|
||||
<stop
|
||||
id="stop4871-6-2"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4879-7-4"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.31807542" />
|
||||
<stop
|
||||
id="stop4877-6-1"
|
||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
||||
offset="0.74691135" />
|
||||
<stop
|
||||
id="stop4873-1-0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25011">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25013" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25015" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
r="2.3554697"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
id="radialGradient24961"
|
||||
xlink:href="#linearGradient4869-4-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-0">
|
||||
<stop
|
||||
id="stop4871-6-8"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4879-7-5"
|
||||
style="stop-color:#eeeeec;stop-opacity:1"
|
||||
offset="0.31807542" />
|
||||
<stop
|
||||
id="stop4877-6-5"
|
||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
||||
offset="0.74691135" />
|
||||
<stop
|
||||
id="stop4873-1-4"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25023">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25025" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25027" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898"
|
||||
id="linearGradient24963"
|
||||
xlink:href="#linearGradient4941"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4941">
|
||||
<stop
|
||||
id="stop4943"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4945"
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25033">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25035" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25037" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898"
|
||||
id="linearGradient24965"
|
||||
xlink:href="#linearGradient4941-7"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4941-7">
|
||||
<stop
|
||||
id="stop4943-2"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4945-5"
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25043">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25045" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25047" />
|
||||
</filter>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25049">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25051" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25053" />
|
||||
</filter>
|
||||
<filter
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter25055">
|
||||
<feColorMatrix
|
||||
result="fbSourceGraphic"
|
||||
values="1"
|
||||
type="saturate"
|
||||
id="feColorMatrix25057" />
|
||||
<feColorMatrix
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
||||
in="fbSourceGraphic"
|
||||
id="feColorMatrix25059" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
|
||||
id="g4030-1-8"
|
||||
style="stroke:#000000;stroke-opacity:1;display:inline">
|
||||
<path
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
|
||||
id="path3165-7-3-1"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
|
||||
id="path3165-7-3-1-9"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 13 KiB |
BIN
data/theme/ws-switch-arrow-up.png
Normal file
After Width: | Height: | Size: 841 B |
@ -1,447 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-up.svg">
|
||||
<defs
|
||||
id="defs25072">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective25078" />
|
||||
<inkscape:perspective
|
||||
id="perspective24985"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4034-0-4"
|
||||
id="linearGradient24957"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(6)"
|
||||
x1="-86.552246"
|
||||
y1="185.439"
|
||||
x2="-83.37072"
|
||||
y2="197.31261" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4034-0-4">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4036-5-7" />
|
||||
<stop
|
||||
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4038-9-6" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter24765"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24767"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24769"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
||||
id="linearGradient24955"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-5)"
|
||||
x1="-74.520325"
|
||||
y1="169.06032"
|
||||
x2="-74.520325"
|
||||
y2="205.94189" />
|
||||
<linearGradient
|
||||
id="linearGradient4632-1-3-9-3-2">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4634-1-8-3-9-0" />
|
||||
<stop
|
||||
id="stop4636-1-9-9-8-8"
|
||||
offset="0.0274937"
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4638-8-3-9-6-6"
|
||||
offset="0.274937"
|
||||
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4640-8-5-7-8-9"
|
||||
offset="0.38707438"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4642-5-41-9-6-9"
|
||||
offset="0.66528589"
|
||||
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4644-5-2-7-9-2"
|
||||
offset="0.76745707"
|
||||
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4646-3-2-3-7-3" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-1"
|
||||
id="radialGradient24959"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-1">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-2" />
|
||||
<stop
|
||||
id="stop4879-7-4"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-1"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-0" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25011"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25013"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25015"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-0"
|
||||
id="radialGradient24961"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-0">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-8" />
|
||||
<stop
|
||||
id="stop4879-7-5"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-5"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-4" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25023"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25025"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25027"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941"
|
||||
id="linearGradient24963"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25033"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25035"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25037"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941-7"
|
||||
id="linearGradient24965"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941-7">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943-2" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945-5" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25043"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25045"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25047"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25049"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25051"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25053"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25055"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25057"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25059"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="-12.356322"
|
||||
inkscape:cy="57.536221"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1200"
|
||||
inkscape:window-height="840"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata25075">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0, 48)">
|
||||
<g
|
||||
id="g3181"
|
||||
transform="matrix(0,1,-1,0,48.0003,-48)">
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1;display:inline"
|
||||
transform="matrix(2,0,0,2,193.25,-374.967)"
|
||||
id="g4030-1-8">
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
sodipodi:nodetypes="ccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccscccsc"
|
||||
id="path3165-7-3-1"
|
||||
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path3165-7-3-1-9"
|
||||
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 16 KiB |
@ -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"/>
|
||||
|
@ -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>
|
||||
|
@ -1,3 +1,4 @@
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = misc/config.js.in
|
||||
CLEANFILES = misc/config.js
|
||||
@ -6,7 +7,6 @@ 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_FOLKS@|$(HAVE_FOLKS)|g" \
|
||||
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
|
||||
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
|
||||
-e "s|[@]datadir@|$(datadir)|g" \
|
||||
@ -18,11 +18,11 @@ jsdir = $(pkgdatadir)/js
|
||||
|
||||
nobase_dist_js_DATA = \
|
||||
gdm/batch.js \
|
||||
gdm/consoleKit.js \
|
||||
gdm/fingerprint.js \
|
||||
gdm/loginDialog.js \
|
||||
gdm/powerMenu.js \
|
||||
gdm/systemd.js \
|
||||
gdm/realmd.js \
|
||||
gdm/util.js \
|
||||
extensionPrefs/main.js \
|
||||
misc/config.js \
|
||||
misc/extensionUtils.js \
|
||||
@ -30,42 +30,39 @@ nobase_dist_js_DATA = \
|
||||
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/checkBox.js \
|
||||
ui/contactDisplay.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
ui/dnd.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/keyringPrompt.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 \
|
||||
@ -73,24 +70,25 @@ nobase_dist_js_DATA = \
|
||||
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 \
|
||||
@ -100,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)
|
||||
|
@ -13,8 +13,7 @@ const _ = Gettext.gettext;
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
@ -162,7 +161,7 @@ const Application = new Lang.Class({
|
||||
vbox.add(toolbar);
|
||||
let toolitem;
|
||||
|
||||
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
|
||||
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
|
||||
use_markup: true });
|
||||
toolitem = new Gtk.ToolItem({ child: label });
|
||||
toolbar.add(toolitem);
|
||||
@ -175,7 +174,7 @@ const Application = new Lang.Class({
|
||||
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), null);
|
||||
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 });
|
||||
@ -210,7 +209,7 @@ const Application = new Lang.Class({
|
||||
_extensionFound: function(signals, extension) {
|
||||
let iter = this._model.append();
|
||||
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||
this._extensionIters[uuid] = iter;
|
||||
this._extensionIters[extension.uuid] = iter;
|
||||
},
|
||||
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
|
||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
|
||||
<method name='CanRestart'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='CanStop'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='Restart' />
|
||||
<method name='Stop' />
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||
|
||||
function ConsoleKitManager() {
|
||||
return new ConsoleKitProxy(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
};
|
@ -19,8 +19,7 @@ function FprintManager() {
|
||||
g_interface_info: FprintManagerInfo,
|
||||
g_name: 'net.reactivated.Fprint',
|
||||
g_object_path: '/net/reactivated/Fprint/Manager',
|
||||
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;
|
||||
|
@ -18,12 +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 Systemd = imports.gdm.systemd;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -32,20 +32,17 @@ const PowerMenuButton = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('system-shutdown', null);
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
/* Translators: accessible name of the power menu in the login screen */
|
||||
this.parent('system-shutdown-symbolic', _("Power"));
|
||||
|
||||
if (Systemd.haveSystemd())
|
||||
this._systemdLoginManager = new Systemd.SystemdLoginManager();
|
||||
else
|
||||
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
|
||||
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,
|
||||
@ -53,75 +50,41 @@ const PowerMenuButton = new Lang.Class({
|
||||
if (open) {
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
}
|
||||
}));
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||
this.actor.visible = shouldBeVisible;
|
||||
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
|
||||
if (Systemd.haveSystemd()) {
|
||||
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveShutdown = result != 'no';
|
||||
else
|
||||
this._haveShutdown = false;
|
||||
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
} else {
|
||||
this._consoleKitManager.CanStopRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveShutdown = result;
|
||||
else
|
||||
this._haveShutdown = false;
|
||||
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
}
|
||||
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
|
||||
this._haveShutdown = result;
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveRestart: function() {
|
||||
|
||||
if (Systemd.haveSystemd()) {
|
||||
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveRestart = result != 'no';
|
||||
else
|
||||
this._haveRestart = false;
|
||||
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
} else {
|
||||
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveRestart = result;
|
||||
else
|
||||
this._haveRestart = false;
|
||||
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
}
|
||||
this._loginManager.canReboot(Lang.bind(this, function(result) {
|
||||
this._haveRestart = result;
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
@ -144,27 +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)
|
||||
return;
|
||||
|
||||
if (Systemd.haveSystemd())
|
||||
this._systemdLoginManager.RebootRemote(true);
|
||||
else
|
||||
this._consoleKitManager.RestartRemote();
|
||||
this._loginManager.reboot();
|
||||
},
|
||||
|
||||
_onActivatePowerOff: function() {
|
||||
if (!this._haveShutdown)
|
||||
return;
|
||||
|
||||
if (Systemd.haveSystemd())
|
||||
this._systemdLoginManager.PowerOffRemote(true);
|
||||
else
|
||||
this._consoleKitManager.StopRemote();
|
||||
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)
|
@ -1,31 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
|
||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||
<method name='PowerOff'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='Reboot'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='CanPowerOff'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanReboot'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
||||
|
||||
function SystemdLoginManager() {
|
||||
return new SystemdLoginManagerProxy(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1');
|
||||
};
|
||||
|
||||
function haveSystemd() {
|
||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
||||
}
|
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);
|
@ -6,8 +6,6 @@ const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
/* 1 if folks is available, 0 otherwise */
|
||||
const HAVE_FOLKS = @HAVE_FOLKS@;
|
||||
/* gettext package */
|
||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
||||
/* locale dir */
|
||||
|
@ -11,6 +11,7 @@ const Gio = imports.gi.Gio;
|
||||
const ShellJS = imports.gi.ShellJS;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
|
||||
const ExtensionType = {
|
||||
SYSTEM: 1,
|
||||
@ -120,11 +121,6 @@ function createExtensionObject(uuid, dir, type) {
|
||||
}
|
||||
}
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta.url) {
|
||||
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
}
|
||||
@ -155,45 +151,35 @@ function installImporter(extension) {
|
||||
const ExtensionFinder = new Lang.Class({
|
||||
Name: 'ExtensionFinder',
|
||||
|
||||
_scanExtensionsInDirectory: function(dir, type) {
|
||||
let fileEnum;
|
||||
let file, info;
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
logError(e, 'Could not enumerate extensions directory');
|
||||
_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;
|
||||
}
|
||||
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let uuid = info.get_name();
|
||||
let extensionDir = dir.get_child(uuid);
|
||||
|
||||
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()));
|
||||
continue;
|
||||
}
|
||||
let extension = createExtensionObject(uuid, extensionDir, type);
|
||||
this.emit('extension-found', extension);
|
||||
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;
|
||||
}
|
||||
fileEnum.close(null);
|
||||
this.emit('extension-found', extension);
|
||||
},
|
||||
|
||||
scanExtensions: function() {
|
||||
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
|
||||
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
|
||||
|
||||
let systemDataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
|
||||
let dir = Gio.file_new_for_path(dirPath);
|
||||
if (dir.query_exists(null))
|
||||
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
deleteGFile(dir);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
@ -46,8 +46,70 @@ 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);
|
||||
}
|
||||
|
||||
function findProviderForMCCMNC(table, needle) {
|
||||
let needlemcc = needle.substring(0, 3);
|
||||
let needlemnc = needle.substring(3, needle.length);
|
||||
|
||||
let name2, name3;
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
|
||||
// Search through MCC/MNC list
|
||||
let list = provider.get_gsm_mcc_mnc();
|
||||
for (let j = 0; j < list.length; j++) {
|
||||
let mccmnc = list[j];
|
||||
|
||||
// Match both 2-digit and 3-digit MNC; prefer a
|
||||
// 3-digit match if found, otherwise a 2-digit one.
|
||||
if (mccmnc.mcc != needlemcc)
|
||||
continue; // MCC was wrong
|
||||
|
||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
||||
name3 = provider.name;
|
||||
|
||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
||||
name2 = provider.name;
|
||||
|
||||
if (name2 && name3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name3 || name2 || null;
|
||||
}
|
||||
|
||||
function findProviderForSid(table, sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
// Search through each country
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let cdma_sid = provider.get_cdma_sid();
|
||||
|
||||
// Search through CDMA SID list
|
||||
for (let j = 0; j < cdma_sid.length; j++) {
|
||||
if (cdma_sid[j] == sid)
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const ModemGsm = new Lang.Class({
|
||||
@ -91,64 +153,29 @@ const ModemGsm = new Lang.Class({
|
||||
},
|
||||
|
||||
_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;
|
||||
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.length == 0 && opCode)
|
||||
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;
|
||||
|
||||
return this._findProviderForMCCMNC(needle);
|
||||
},
|
||||
|
||||
_findProviderForMCCMNC: function(needle) {
|
||||
let table = _getProvidersTable();
|
||||
let needlemcc = needle.substring(0, 3);
|
||||
let needlemnc = needle.substring(3, needle.length);
|
||||
|
||||
let name2, name3;
|
||||
for (let iter in table) {
|
||||
let providers = table[iter];
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
|
||||
// Search through MCC/MNC list
|
||||
let list = provider.get_gsm_mcc_mnc();
|
||||
for (let j = 0; j < list.length; j++) {
|
||||
let mccmnc = list[j];
|
||||
|
||||
// Match both 2-digit and 3-digit MNC; prefer a
|
||||
// 3-digit match if found, otherwise a 2-digit one.
|
||||
if (mccmnc.mcc != needlemcc)
|
||||
continue; // MCC was wrong
|
||||
|
||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
||||
name3 = provider.name;
|
||||
|
||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
||||
name2 = provider.name;
|
||||
|
||||
if (name2 && name3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name3 || name2 || null;
|
||||
return findProviderForMCCMNC(table, needle);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemGsm.prototype);
|
||||
@ -189,39 +216,14 @@ const ModemCdma = new Lang.Class({
|
||||
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;
|
||||
}
|
@ -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;
|
||||
|
||||
@ -87,7 +84,7 @@ function trySpawn(argv)
|
||||
try {
|
||||
[success, pid] = GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null, null);
|
||||
null);
|
||||
} catch (err) {
|
||||
/* Rewrite the error in case of ENOENT */
|
||||
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
|
||||
@ -153,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.
|
||||
|
@ -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;
|
||||
@ -18,7 +19,6 @@ 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;
|
||||
@ -48,18 +48,16 @@ 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 action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this.actor.add_action(action);
|
||||
},
|
||||
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
|
||||
// Reset scroll on mapping
|
||||
adjustment.value = 0;
|
||||
}));
|
||||
_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() {
|
||||
@ -216,6 +214,16 @@ const ViewByCategories = new Lang.Class({
|
||||
},
|
||||
|
||||
_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,
|
||||
@ -225,18 +233,16 @@ const ViewByCategories = new Lang.Class({
|
||||
this._selectCategory(index);
|
||||
}));
|
||||
|
||||
var apps;
|
||||
if (dir == null) {
|
||||
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() {
|
||||
@ -262,8 +268,9 @@ const ViewByCategories = new Lang.Class({
|
||||
var dir = iter.get_directory();
|
||||
if (dir.get_is_nodisplay())
|
||||
continue;
|
||||
this._addCategory(dir.get_name(), i, dir);
|
||||
i++;
|
||||
|
||||
if (this._addCategory(dir.get_name(), i, dir))
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,11 +311,10 @@ 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';
|
||||
},
|
||||
|
||||
getResultMetas: function(apps, callback) {
|
||||
@ -333,18 +339,15 @@ const AppSearchProvider = new Lang.Class({
|
||||
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 ? 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) {
|
||||
@ -364,13 +367,10 @@ 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');
|
||||
},
|
||||
|
||||
getResultMetas: function(prefs, callback) {
|
||||
@ -379,9 +379,7 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
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);
|
||||
@ -395,21 +393,13 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
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());
|
||||
}
|
||||
});
|
||||
|
||||
@ -479,7 +469,6 @@ const AppWellIcon = new Lang.Class({
|
||||
Lang.bind(this,
|
||||
this._onStateChanged));
|
||||
this._onStateChanged();
|
||||
this.isMenuUp = false;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
@ -561,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;
|
||||
},
|
||||
@ -578,7 +569,7 @@ const AppWellIcon = new Lang.Class({
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
this.actor.sync_hover();
|
||||
this.isMenuUp = false;
|
||||
this.emit('menu-state-changed', false);
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
|
@ -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 () {
|
||||
this._removeFavorite(appId);
|
||||
}));
|
||||
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 () {
|
||||
this._addFavorite(appId, pos);
|
||||
}));
|
||||
{ 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 });
|
||||
@ -65,11 +76,16 @@ const BoxPointer = new Lang.Class({
|
||||
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.opacity = 0;
|
||||
this.actor.show();
|
||||
|
||||
if (animate) {
|
||||
if (animate & PopupAnimation.SLIDE) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.yOffset = -rise;
|
||||
@ -95,7 +111,7 @@ const BoxPointer = new Lang.Class({
|
||||
if (onComplete)
|
||||
onComplete();
|
||||
}),
|
||||
time: POPUP_ANIMATION_TIME });
|
||||
time: animationTime });
|
||||
},
|
||||
|
||||
hide: function(animate, onComplete) {
|
||||
@ -103,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;
|
||||
@ -123,13 +141,15 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
this._muteInput();
|
||||
|
||||
Tweener.addTween(this, { opacity: 0,
|
||||
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)
|
||||
@ -199,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) {
|
||||
@ -214,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();
|
||||
@ -225,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
|
||||
@ -238,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);
|
||||
@ -253,19 +327,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
|
||||
// top-right corner
|
||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
if (!skipTopRight) {
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
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);
|
||||
@ -273,19 +348,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
|
||||
// bottom-right corner
|
||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||
0, Math.PI/2);
|
||||
if (!skipBottomRight) {
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
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);
|
||||
@ -293,19 +369,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
|
||||
// bottom-left corner
|
||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||
Math.PI/2, Math.PI);
|
||||
if (!skipBottomLeft) {
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
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);
|
||||
@ -313,20 +390,26 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
|
||||
// top-left corner
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
if (!skipTopLeft) {
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
}
|
||||
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
cr.fillPreserve();
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
|
||||
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();
|
||||
@ -343,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) {
|
||||
@ -368,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;
|
||||
|
||||
@ -390,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) {
|
||||
@ -446,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.
|
||||
@ -204,12 +194,11 @@ const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIfac
|
||||
|
||||
function CalendarServer() {
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||
g_interface_name: CalendarServerInfo.name,
|
||||
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_interface_name: CalendarServerInfo.name,
|
||||
g_interface_info: CalendarServerInfo,
|
||||
g_name: 'org.gnome.Shell.CalendarServer',
|
||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
||||
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)) {
|
||||
@ -413,8 +407,6 @@ const Calendar = new Lang.Class({
|
||||
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));
|
||||
@ -451,18 +443,6 @@ const Calendar = new Lang.Class({
|
||||
this._firstDayIndex = this.actor.get_n_children();
|
||||
},
|
||||
|
||||
_onStyleChange: function(actor, event) {
|
||||
// width of a digit in pango units
|
||||
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
|
||||
this._setWeekdateHeaderWidth();
|
||||
},
|
||||
|
||||
_setWeekdateHeaderWidth: function() {
|
||||
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
|
||||
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
|
||||
}
|
||||
},
|
||||
|
||||
_onScroll : function(actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
@ -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) {
|
||||
@ -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);
|
||||
}
|
||||
},
|
||||
|
65
js/ui/components/__init__.js
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ComponentManager = new Lang.Class({
|
||||
Name: 'ComponentManager',
|
||||
|
||||
_init: function() {
|
||||
this._allComponents = {};
|
||||
this._enabledComponents = [];
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let newEnabledComponents = Main.sessionMode.components;
|
||||
|
||||
newEnabledComponents.filter(Lang.bind(this, function(name) {
|
||||
return this._enabledComponents.indexOf(name) == -1;
|
||||
})).forEach(Lang.bind(this, function(name) {
|
||||
this._enableComponent(name);
|
||||
}));
|
||||
|
||||
this._enabledComponents.filter(Lang.bind(this, function(name) {
|
||||
return newEnabledComponents.indexOf(name) == -1;
|
||||
})).forEach(Lang.bind(this, function(name) {
|
||||
this._disableComponent(name);
|
||||
}));
|
||||
|
||||
this._enabledComponents = newEnabledComponents;
|
||||
},
|
||||
|
||||
_importComponent: function(name) {
|
||||
let module = imports.ui.components[name];
|
||||
return module.Component;
|
||||
},
|
||||
|
||||
_ensureComponent: function(name) {
|
||||
let component = this._allComponents[name];
|
||||
if (component)
|
||||
return component;
|
||||
|
||||
if (Main.sessionMode.isLocked)
|
||||
return null;
|
||||
|
||||
let constructor = this._importComponent(name);
|
||||
component = new constructor();
|
||||
this._allComponents[name] = component;
|
||||
return component;
|
||||
},
|
||||
|
||||
_enableComponent: function(name) {
|
||||
let component = this._ensureComponent(name);
|
||||
if (component)
|
||||
component.enable();
|
||||
},
|
||||
|
||||
_disableComponent: function(name) {
|
||||
let component = this._allComponents[name];
|
||||
if (component == null)
|
||||
return;
|
||||
component.disable();
|
||||
}
|
||||
});
|
@ -5,11 +5,14 @@ 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';
|
||||
@ -17,106 +20,54 @@ 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._updateSessionActive = function() {
|
||||
if (self.g_name_owner) {
|
||||
self.GetCurrentSessionRemote(function([session]) {
|
||||
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
|
||||
|
||||
self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) {
|
||||
self.sessionActive = isActive;
|
||||
});
|
||||
self._ckSession.IsActiveRemote(function([isActive]) {
|
||||
self.sessionActive = isActive;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
self.sessionActive = true;
|
||||
}
|
||||
};
|
||||
self.connect('notify::g-name-owner',
|
||||
Lang.bind(self, self._updateSessionActive));
|
||||
|
||||
self._updateSessionActive();
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
||||
function haveSystemd() {
|
||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
||||
}
|
||||
|
||||
const AutomountManager = new Lang.Class({
|
||||
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;
|
||||
|
||||
if (!haveSystemd())
|
||||
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() {
|
||||
@ -127,27 +78,14 @@ const AutomountManager = new Lang.Class({
|
||||
allowAutorun: false });
|
||||
}));
|
||||
|
||||
this._mountAllId = 0;
|
||||
return false;
|
||||
},
|
||||
|
||||
isSessionActive: function() {
|
||||
// Return whether the current session is active, using the
|
||||
// right mechanism: either systemd if available or ConsoleKit
|
||||
// as fallback.
|
||||
|
||||
if (haveSystemd())
|
||||
return Shell.session_is_active_for_systemd();
|
||||
|
||||
return this.ckListener.sessionActive;
|
||||
},
|
||||
|
||||
_onDriveConnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this.isSessionActive())
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-added-media');
|
||||
@ -156,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.isSessionActive())
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-removed-media');
|
||||
@ -168,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.isSessionActive())
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
// we force stop/eject in this case, so we don't have to pass a
|
||||
@ -208,17 +143,13 @@ const AutomountManager = new Lang.Class({
|
||||
if (params.checkSession) {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// don't attempt automount
|
||||
if (!this.isSessionActive())
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive) {
|
||||
if (this._volumeQueue.indexOf(volume) == -1)
|
||||
this._volumeQueue.push(volume);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inhibited)
|
||||
return;
|
||||
|
||||
// Volume is already mounted, don't bother.
|
||||
if (volume.get_mount())
|
||||
return;
|
||||
@ -307,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;
|
||||
@ -43,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();
|
||||
@ -82,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) {
|
||||
// guess mount's content types using GIO
|
||||
mount.guess_content_type(false, null,
|
||||
Lang.bind(this,
|
||||
this._onContentTypeGuessed));
|
||||
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) {
|
||||
@ -142,55 +162,69 @@ 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.isSessionActive())
|
||||
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);
|
||||
this._residentSource.removeMount(mount);
|
||||
if (this._residentSource)
|
||||
this._residentSource.removeMount(mount);
|
||||
},
|
||||
|
||||
ejectMount: function(mount) {
|
||||
@ -257,12 +291,18 @@ const AutorunResidentSource = new Lang.Class({
|
||||
Name: 'AutorunResidentSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Removable Devices"), 'media-removable', St.IconType.FULLCOLOR);
|
||||
_init: function(manager) {
|
||||
this.parent(_("Removable Devices"), 'media-removable');
|
||||
this.showInLockScreen = false;
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
this._notification = new AutorunResidentNotification(this);
|
||||
this._manager = manager;
|
||||
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
addMount: function(mount, apps) {
|
||||
@ -312,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
|
||||
@ -320,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,
|
||||
@ -367,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 =
|
||||
@ -382,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;
|
||||
@ -392,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 });
|
||||
},
|
||||
@ -435,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) {
|
||||
@ -488,23 +530,22 @@ const AutorunTransientSource = new Lang.Class({
|
||||
Name: 'AutorunTransientSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(mount, apps) {
|
||||
_init: function(manager, mount, apps) {
|
||||
this._manager = manager;
|
||||
this.mount = mount;
|
||||
this.apps = apps;
|
||||
|
||||
this.parent(mount.get_name());
|
||||
|
||||
this._notification = new AutorunTransientNotification(this);
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
@ -512,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);
|
||||
@ -566,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);
|
||||
|
||||
@ -583,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;
|
@ -65,7 +65,8 @@ const KeyringDialog = new Lang.Class({
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton)
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true
|
||||
}]
|
||||
|
||||
this.setButtons(buttons);
|
||||
@ -83,7 +84,10 @@ const KeyringDialog = new Lang.Class({
|
||||
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 });
|
||||
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});
|
||||
@ -99,7 +103,10 @@ const KeyringDialog = new Lang.Class({
|
||||
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 });
|
||||
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});
|
||||
@ -185,23 +192,36 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onContinueButton: function() {
|
||||
this.prompt.complete()
|
||||
this.prompt.complete();
|
||||
},
|
||||
|
||||
_onCancelButton: function() {
|
||||
this.prompt.cancel()
|
||||
this.prompt.cancel();
|
||||
},
|
||||
});
|
||||
|
||||
function init() {
|
||||
prompter = new Gcr.SystemPrompter();
|
||||
prompter.connect('new-prompt', function(prompter) {
|
||||
let dialog = new KeyringDialog();
|
||||
return dialog.prompt;
|
||||
});
|
||||
const KeyringPrompter = new Lang.Class({
|
||||
Name: 'KeyringPrompter',
|
||||
|
||||
let connection = Gio.DBus.session;
|
||||
prompter.register(connection);
|
||||
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
}
|
||||
_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,23 +1,4 @@
|
||||
// -*- 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;
|
||||
@ -135,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++;
|
||||
|
||||
@ -147,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"),
|
||||
@ -166,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() {
|
||||
@ -428,7 +408,10 @@ const VPNRequestHandler = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
cancel: function(respond) {
|
||||
if (respond)
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
|
||||
if (this._newStylePlugin && this._shellDialog) {
|
||||
this._shellDialog.close(global.get_current_time());
|
||||
this._shellDialog.destroy();
|
||||
@ -604,7 +587,7 @@ 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 = { };
|
||||
@ -614,6 +597,26 @@ const NetworkAgent = new Lang.Class({
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
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);
|
||||
@ -634,7 +637,7 @@ const NetworkAgent = new Lang.Class({
|
||||
this._dialogs[requestId].destroy();
|
||||
delete this._dialogs[requestId];
|
||||
} else if (this._vpnRequests[requestId]) {
|
||||
this._vpnRequests[requestId].cancel();
|
||||
this._vpnRequests[requestId].cancel(false);
|
||||
delete this._vpnRequests[requestId];
|
||||
}
|
||||
},
|
||||
@ -683,7 +686,10 @@ const NetworkAgent = new Lang.Class({
|
||||
try {
|
||||
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
let path = GLib.build_filenamev([Config.LIBEXECDIR, binary]);
|
||||
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 };
|
||||
@ -700,3 +706,4 @@ const NetworkAgent = new Lang.Class({
|
||||
}
|
||||
}
|
||||
});
|
||||
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,8 +13,12 @@ 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',
|
||||
@ -65,7 +49,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
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: 'prompt-dialog-headline',
|
||||
text: _("Authentication Required") });
|
||||
@ -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,17 +121,17 @@ 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: 'prompt-dialog-password-label' }));
|
||||
this._passwordBox.add(this._passwordLabel);
|
||||
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: "",
|
||||
can_focus: true});
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||
this._passwordBox.add(this._passwordEntry,
|
||||
{expand: true });
|
||||
{ expand: true });
|
||||
this.setInitialKeyFocus(this._passwordEntry);
|
||||
this._passwordBox.hide();
|
||||
|
||||
@ -178,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;
|
||||
@ -269,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);
|
||||
@ -312,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();
|
||||
}
|
||||
},
|
||||
|
||||
@ -340,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) {
|
||||
@ -402,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
|
||||
@ -63,10 +63,10 @@ function makeMessageFromTplEvent(event) {
|
||||
};
|
||||
}
|
||||
|
||||
const Client = new Lang.Class({
|
||||
Name: 'Client',
|
||||
const TelepathyClient = new Lang.Class({
|
||||
Name: 'TelepathyClient',
|
||||
|
||||
_init : function() {
|
||||
_init: function() {
|
||||
// channel path -> ChatSource
|
||||
this._chatSources = {};
|
||||
this._chatState = Tp.ChannelChatState.ACTIVE;
|
||||
@ -89,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(
|
||||
@ -99,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(
|
||||
@ -108,21 +112,26 @@ 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._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
|
||||
Lang.bind(this, this._accountValidityChanged));
|
||||
|
||||
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));
|
||||
},
|
||||
|
||||
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,
|
||||
@ -132,7 +141,7 @@ const Client = new Lang.Class({
|
||||
let channel = channels[i];
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
if (Shell.is_channel_invalidated(channel))
|
||||
if (channel.get_invalidated())
|
||||
continue;
|
||||
|
||||
/* Only observe contact text channels */
|
||||
@ -184,7 +193,7 @@ const Client = new Lang.Class({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Shell.is_channel_invalidated(channel))
|
||||
if (channel.get_invalidated())
|
||||
continue;
|
||||
|
||||
// 'notify' will be true when coming from an actual HandleChannels
|
||||
@ -211,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;
|
||||
}
|
||||
|
||||
@ -237,8 +248,9 @@ const Client = new Lang.Class({
|
||||
let channel = channels[0];
|
||||
let chanType = channel.get_channel_type();
|
||||
|
||||
if (Shell.is_channel_invalidated(channel)) {
|
||||
Shell.decline_dispatch_op(context, 'Channel is invalidated');
|
||||
if (channel.get_invalidated()) {
|
||||
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||
message: 'Channel is invalidated' }));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -249,7 +261,8 @@ const Client = new Lang.Class({
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
||||
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
||||
else
|
||||
Shell.decline_dispatch_op(context, 'Unsupported channel type');
|
||||
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||
message: 'Unsupported channel type' }));
|
||||
},
|
||||
|
||||
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
||||
@ -370,26 +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 MessageTray.Source(_("Subscription request"),
|
||||
'gtk-dialog-question',
|
||||
St.IconType.FULLCOLOR);
|
||||
Main.messageTray.add(this._subscriptionSource);
|
||||
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._subscriptionSource = null;
|
||||
}));
|
||||
}
|
||||
|
||||
return this._subscriptionSource;
|
||||
},
|
||||
|
||||
_accountConnectionStatusNotifyCb: function(account) {
|
||||
let connectionError = account.connection_error;
|
||||
|
||||
@ -403,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;
|
||||
@ -413,18 +412,16 @@ const Client = new Lang.Class({
|
||||
source.notify(notif);
|
||||
},
|
||||
|
||||
_ensureAccountSource: function() {
|
||||
if (this._accountSource == null) {
|
||||
this._accountSource = new MessageTray.Source(_("Connection error"),
|
||||
'gtk-dialog-error',
|
||||
St.IconType.FULLCOLOR);
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
@ -471,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();
|
||||
@ -482,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
|
||||
@ -547,7 +582,7 @@ const ChatSource = new Lang.Class({
|
||||
this._pendingMessages.push(message);
|
||||
}
|
||||
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
|
||||
let showTimestamp = false;
|
||||
|
||||
@ -593,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) {
|
||||
@ -602,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);
|
||||
@ -664,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) {
|
||||
@ -703,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);
|
||||
}
|
||||
});
|
||||
|
||||
@ -722,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',
|
||||
@ -731,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 });
|
||||
@ -892,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);
|
||||
@ -922,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 });
|
||||
|
||||
@ -932,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);
|
||||
@ -1037,9 +1057,8 @@ const ApproverSource = new Lang.Class({
|
||||
this.parent();
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_size: this.ICON_SIZE });
|
||||
getIcon: function() {
|
||||
return this._gicon;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1100,7 +1119,7 @@ const AudioVideoNotification = new Lang.Class({
|
||||
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"));
|
||||
|
||||
@ -1190,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 });
|
||||
}
|
||||
|
||||
@ -1318,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();
|
||||
@ -1364,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() {
|
||||
@ -1398,3 +1405,4 @@ const AccountNotification = new Lang.Class({
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
const Component = TelepathyClient;
|
@ -1,196 +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 Atk = imports.gi.Atk;
|
||||
|
||||
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,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
accessible_role: Atk.Role.PUSH_BUTTON });
|
||||
|
||||
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 ||
|
||||
C_("contact", "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 });
|
||||
|
||||
this.actor.label_actor = aliasLabel;
|
||||
|
||||
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;
|
||||
case Folks.PresenceType.OFFLINE:
|
||||
text = _("Offline");
|
||||
iconName = 'user-offline';
|
||||
break;
|
||||
default:
|
||||
text = '';
|
||||
iconName = null;
|
||||
}
|
||||
|
||||
let box = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'contact-details-status' });
|
||||
|
||||
if (iconName) {
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: 16,
|
||||
style_class: 'contact-details-status-icon' });
|
||||
box.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
}
|
||||
|
||||
let label = new St.Label({ text: text });
|
||||
|
||||
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();
|
||||
},
|
||||
|
||||
getResultMetas: function(ids, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
let contact = new Contact(ids[i]);
|
||||
metas.push({ 'id': ids[i],
|
||||
'name': contact.alias,
|
||||
'createIcon': function(size) {
|
||||
return contact.createIcon(size);
|
||||
}
|
||||
});
|
||||
}
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
this.searchSystem.pushResults(this, this._contactSys.initial_search(terms));
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
this.searchSystem.pushResults(this, 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) {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
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);
|
||||
_createSwitcher: function() {
|
||||
this._switcherList = new CtrlAltTabSwitcher(this._items);
|
||||
return true;
|
||||
},
|
||||
|
||||
_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;
|
||||
_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.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
return true;
|
||||
this._select(this._selectedIndex);
|
||||
},
|
||||
|
||||
_next : function() {
|
||||
return mod(this._selection + 1, this._items.length);
|
||||
},
|
||||
|
||||
_previous : function() {
|
||||
return mod(this._selection - 1, this._items.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
|
||||
if (shift && keysym == Clutter.KEY_Tab)
|
||||
keysym = Clutter.ISO_Left_Tab;
|
||||
|
||||
if (keysym == Clutter.KEY_Escape)
|
||||
this.destroy();
|
||||
else if (keysym == Clutter.KEY_Tab)
|
||||
this._select(this._next());
|
||||
else if (keysym == Clutter.KEY_ISO_Left_Tab)
|
||||
_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 } );
|
||||
|
296
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,9 +106,10 @@ const DashItemContainer = new Lang.Class({
|
||||
},
|
||||
|
||||
showLabel: function() {
|
||||
if (this.label == null)
|
||||
if (!this._labelText)
|
||||
return;
|
||||
|
||||
this.label.set_text(this._labelText);
|
||||
this.label.opacity = 0;
|
||||
this.label.show();
|
||||
|
||||
@ -124,16 +140,11 @@ 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,
|
||||
{ opacity: 0,
|
||||
time: DASH_ITEM_LABEL_HIDE_TIME,
|
||||
@ -227,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();
|
||||
|
||||
@ -296,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',
|
||||
|
||||
@ -307,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)
|
||||
@ -327,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));
|
||||
@ -370,54 +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 &&
|
||||
app.get_state() != Shell.AppState.RUNNING &&
|
||||
dragEvent.source.actor &&
|
||||
this.actor.contains (dragEvent.source.actor) &&
|
||||
this._favRemoveTarget == null) {
|
||||
this._favRemoveTarget = new RemoveFavoriteIcon();
|
||||
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
||||
this._box.add(this._favRemoveTarget.actor);
|
||||
this._adjustIconSize();
|
||||
this._favRemoveTarget.animateIn();
|
||||
}
|
||||
|
||||
let favRemoveHovered = false;
|
||||
if (this._favRemoveTarget)
|
||||
favRemoveHovered =
|
||||
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
|
||||
|
||||
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
|
||||
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;
|
||||
},
|
||||
@ -433,37 +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());
|
||||
// Override default AppWellIcon label_actor
|
||||
display.actor.label_actor = item.label;
|
||||
|
||||
appIcon.icon.setIconSize(this.iconSize);
|
||||
this._hookUpLabel(item);
|
||||
|
||||
display.icon.setIconSize(this.iconSize);
|
||||
display.actor.connect('notify::hover',
|
||||
Lang.bind(this, function() {
|
||||
this._onHover(item, display)
|
||||
}));
|
||||
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,
|
||||
@ -474,7 +546,7 @@ const Dash = new Lang.Class({
|
||||
}));
|
||||
if (this._resetHoverTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||||
this._resetHoverTimeoutId = 0;
|
||||
this._resetHoverTimeoutId = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -504,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._box.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 });
|
||||
@ -541,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;
|
||||
@ -572,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();
|
||||
@ -697,8 +764,9 @@ const Dash = new Lang.Class({
|
||||
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.destroy();
|
||||
@ -713,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++)
|
||||
@ -730,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())
|
||||
@ -815,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()) {
|
||||
|
@ -70,16 +70,8 @@ const DateMenuButton = new Lang.Class({
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
if (Main.sessionMode.showCalendarEvents) {
|
||||
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) {
|
||||
@ -97,31 +89,34 @@ 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 (Main.sessionMode.showCalendarEvents) {
|
||||
// Add vertical separator
|
||||
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(this._separator);
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
|
||||
vertical: true });
|
||||
hbox.add(vbox, { expand: true });
|
||||
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({name: 'calendarEventsArea',
|
||||
vertical: true});
|
||||
hbox.add(vbox, { expand: true });
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
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._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) {
|
||||
@ -151,21 +146,72 @@ const DateMenuButton = new Lang.Class({
|
||||
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() {
|
||||
this._clockDisplay.set_text(this._clock.clock);
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
let dateFormat = _("%A %B %e, %Y");
|
||||
let displayDate = new Date();
|
||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||
},
|
||||
|
||||
_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');
|
||||
|
15
js/ui/dnd.js
@ -234,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.
|
||||
@ -263,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;
|
||||
@ -280,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;
|
||||
|
@ -34,6 +34,7 @@ const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
|
||||
let _endSessionDialog = null;
|
||||
|
||||
@ -89,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'
|
||||
};
|
||||
|
||||
@ -104,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'
|
||||
};
|
||||
|
||||
@ -160,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 });
|
||||
@ -279,11 +281,7 @@ 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() {
|
||||
@ -306,41 +304,6 @@ 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();
|
||||
}
|
||||
},
|
||||
|
||||
_updateDescription: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
@ -389,16 +352,16 @@ const EndSessionDialog = new Lang.Class({
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
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 (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();
|
||||
|
@ -7,6 +7,7 @@ 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;
|
||||
@ -18,25 +19,39 @@ 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_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||
const REPOSITORY_URL_UPDATE = REPOSITORY_URL_BASE + '/update-info/';
|
||||
|
||||
let _httpSession;
|
||||
|
||||
function installExtensionFromUUID(uuid) {
|
||||
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) {
|
||||
let info = JSON.parse(message.response_body.data);
|
||||
let dialog = new InstallExtensionDialog(uuid, info);
|
||||
dialog.open(global.get_current_time());
|
||||
});
|
||||
_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 uninstallExtensionFromUUID(uuid) {
|
||||
function uninstallExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
@ -45,29 +60,29 @@ function uninstallExtensionFromUUID(uuid) {
|
||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||
return false;
|
||||
|
||||
if (!ExtensionSystem.unloadExtension(uuid))
|
||||
if (!ExtensionSystem.unloadExtension(extension))
|
||||
return false;
|
||||
|
||||
FileUtils.recursivelyDeleteDir(extension.dir);
|
||||
FileUtils.recursivelyDeleteDir(extension.dir, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
function gotExtensionZipFile(session, message, uuid) {
|
||||
function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
||||
errback('DownloadExtensionError', message.status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
|
||||
try {
|
||||
if (!dir.query_exists(null))
|
||||
dir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
logExtensionError('Could not create extension directory');
|
||||
errback('CreateExtensionDirectoryError', e);
|
||||
return;
|
||||
}
|
||||
|
||||
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
|
||||
let contents = message.response_body.flatten().as_bytes();
|
||||
let contents = message.response_body.flatten().get_as_bytes();
|
||||
stream.output_stream.write_bytes(contents, null);
|
||||
stream.close(null);
|
||||
let [success, pid] = GLib.spawn_async(null,
|
||||
@ -77,21 +92,92 @@ function gotExtensionZipFile(session, message, uuid) {
|
||||
null);
|
||||
|
||||
if (!success) {
|
||||
logExtensionError(uuid, 'extract: could not extract');
|
||||
errback('ExtractExtensionError');
|
||||
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(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1) {
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
}
|
||||
if (status != 0)
|
||||
errback('ExtractExtensionError');
|
||||
else
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
ExtensionSystem.loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -99,18 +185,20 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
Name: 'InstallExtensionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(uuid, info) {
|
||||
_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)
|
||||
action: Lang.bind(this, this._onInstallButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
|
||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
|
||||
@ -128,34 +216,46 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
|
||||
_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: ExtensionSystem.ExtensionState.UNINSTALLED,
|
||||
error: '' };
|
||||
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||
},
|
||||
|
||||
_onInstallButtonPressed: function(button, event) {
|
||||
let state = { uuid: this._uuid,
|
||||
state: ExtensionSystem.ExtensionState.DOWNLOADING,
|
||||
error: '' };
|
||||
|
||||
_signals.emit('extension-state-changed', state);
|
||||
|
||||
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);
|
||||
|
||||
_httpSession.queue_message(message,
|
||||
Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, this._uuid);
|
||||
}));
|
||||
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());
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
@ -38,6 +39,9 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
|
||||
var initted = false;
|
||||
var enabled;
|
||||
|
||||
function disableExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
@ -63,7 +67,7 @@ function disableExtension(uuid) {
|
||||
try {
|
||||
ExtensionUtils.extensions[uuid].stateObj.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,19 +76,14 @@ function disableExtension(uuid) {
|
||||
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||
}
|
||||
|
||||
try {
|
||||
extension.stateObj.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
}
|
||||
extension.stateObj.disable();
|
||||
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
let uuid = order[i];
|
||||
try {
|
||||
ExtensionUtils.extensions[uuid].stateObj.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,42 +106,35 @@ function enableExtension(uuid) {
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
try {
|
||||
extension.stateObj.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
try {
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
extension.stylesheet = stylesheetFile;
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
|
||||
}
|
||||
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, message, state) {
|
||||
function logExtensionError(uuid, error) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
let message = '' + error;
|
||||
|
||||
extension.state = ExtensionState.ERROR;
|
||||
if (!extension.errors)
|
||||
extension.errors = [];
|
||||
|
||||
extension.errors.push(message);
|
||||
|
||||
log('Extension "%s" had error: %s'.format(uuid, message));
|
||||
state = state || ExtensionState.ERROR;
|
||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||
error: message,
|
||||
state: state });
|
||||
state: extension.state });
|
||||
}
|
||||
|
||||
function loadExtension(extension) {
|
||||
@ -150,40 +142,47 @@ function loadExtension(extension) {
|
||||
extension.state = ExtensionState.ERROR;
|
||||
|
||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||
logExtensionError(extension.uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
||||
extension.state = ExtensionState.OUT_OF_DATE;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||
if (enabled) {
|
||||
initExtension(extension.uuid);
|
||||
if (extension.state == ExtensionState.DISABLED)
|
||||
enableExtension(extension.uuid);
|
||||
} else {
|
||||
extension.state = ExtensionState.INITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function unloadExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
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(uuid);
|
||||
disableExtension(extension.uuid);
|
||||
|
||||
extension.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
|
||||
delete ExtensionUtils.extensions[uuid];
|
||||
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;
|
||||
@ -192,57 +191,51 @@ function initExtension(uuid) {
|
||||
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;
|
||||
}
|
||||
if (!extensionJs.query_exists(null))
|
||||
throw new Error('Missing extension.js');
|
||||
|
||||
let extensionModule;
|
||||
let extensionState = null;
|
||||
try {
|
||||
ExtensionUtils.installImporter(extension);
|
||||
extensionModule = extension.imports.extension;
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, '' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
ExtensionUtils.installImporter(extension);
|
||||
extensionModule = extension.imports.extension;
|
||||
|
||||
if (extensionModule.init) {
|
||||
try {
|
||||
extensionState = extensionModule.init(extension);
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
|
||||
return;
|
||||
}
|
||||
extensionState = extensionModule.init(extension);
|
||||
}
|
||||
|
||||
if (!extensionState)
|
||||
extensionState = extensionModule;
|
||||
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);
|
||||
}
|
||||
|
||||
function getEnabledExtensions() {
|
||||
let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
if (!Array.isArray(Main.sessionMode.enabledExtensions))
|
||||
return extensions;
|
||||
|
||||
return Main.sessionMode.enabledExtensions.concat(extensions);
|
||||
}
|
||||
|
||||
function onEnabledExtensionsChanged() {
|
||||
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) {
|
||||
enableExtension(uuid);
|
||||
try {
|
||||
enableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||
@ -250,19 +243,74 @@ function onEnabledExtensionsChanged() {
|
||||
enabledExtensions.filter(function(item) {
|
||||
return newEnabledExtensions.indexOf(item) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
disableExtension(uuid);
|
||||
try {
|
||||
disableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
}
|
||||
|
||||
function loadExtensions() {
|
||||
function _loadExtensions() {
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
enabledExtensions = getEnabledExtensions();
|
||||
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', function(signals, extension) {
|
||||
loadExtension(extension);
|
||||
try {
|
||||
loadExtension(extension);
|
||||
} catch(e) {
|
||||
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();
|
||||
}
|
||||
|
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,7 +151,22 @@ const BaseIcon = new Lang.Class({
|
||||
size = found ? len : ICON_SIZE;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
@ -198,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);
|
||||
|
@ -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 });
|
||||
@ -278,7 +251,7 @@ const Keyboard = new Lang.Class({
|
||||
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();
|
||||
},
|
||||
@ -288,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();
|
||||
@ -339,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;
|
||||
},
|
||||
@ -387,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;
|
||||
|
||||
@ -468,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();
|
||||
},
|
||||
@ -494,26 +488,42 @@ 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;
|
||||
|
||||
this._timestamp = timestamp;
|
||||
this.show();
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
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;
|
||||
|
||||
this._timestamp = timestamp;
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
},
|
||||
|
||||
@ -542,7 +552,8 @@ const KeyboardSource = new Lang.Class({
|
||||
|
||||
_init: function(keyboard) {
|
||||
this._keyboard = keyboard;
|
||||
this.parent(_("Keyboard"), 'input-keyboard', St.IconType.SYMBOLIC);
|
||||
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
|
||||
this.keepTrayOnSummaryClick = true;
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
@ -555,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);
|
||||
}
|
||||
});
|
||||
|
355
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;
|
||||
@ -11,12 +12,88 @@ 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',
|
||||
@ -26,28 +103,37 @@ const LayoutManager = new Lang.Class({
|
||||
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',
|
||||
@ -149,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);
|
||||
@ -168,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);
|
||||
@ -190,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();
|
||||
@ -230,16 +307,83 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
get currentMonitor() {
|
||||
let index = global.screen.get_current_monitor();
|
||||
return Main.layoutManager.monitors[index];
|
||||
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);
|
||||
}
|
||||
|
||||
return i;
|
||||
},
|
||||
|
||||
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,
|
||||
@ -248,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,
|
||||
@ -261,11 +405,16 @@ const LayoutManager = new Lang.Class({
|
||||
onComplete: this._showKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
Tweener.addTween(this.trayBox,
|
||||
{ anchor_y: this.keyboardBox.height,
|
||||
time: KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
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() {
|
||||
@ -275,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;
|
||||
this.trayBox.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;
|
||||
@ -292,11 +441,16 @@ const LayoutManager = new Lang.Class({
|
||||
onComplete: this._hideKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
Tweener.addTween(this.trayBox,
|
||||
{ anchor_y: 0,
|
||||
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
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() {
|
||||
@ -318,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);
|
||||
},
|
||||
@ -333,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);
|
||||
},
|
||||
@ -358,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);
|
||||
|
||||
@ -542,7 +700,7 @@ const HotCorner = new Lang.Class({
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
trackFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
@ -569,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) {
|
||||
@ -672,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;
|
||||
}
|
||||
},
|
||||
|
||||
@ -709,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();
|
||||
},
|
||||
@ -732,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() {
|
||||
@ -798,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;
|
||||
}
|
||||
@ -822,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 &&
|
||||
|
@ -8,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
|
||||
@ -15,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
|
||||
@ -38,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',
|
||||
@ -52,6 +59,7 @@ 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));
|
||||
|
||||
@ -93,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();
|
||||
|
@ -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,16 +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 ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Link = imports.ui.link;
|
||||
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; ' +
|
||||
@ -36,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); ';
|
||||
@ -261,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;
|
||||
@ -272,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;
|
||||
@ -299,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 });
|
||||
@ -311,12 +317,14 @@ 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() {
|
||||
@ -332,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);
|
||||
@ -343,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 {
|
||||
@ -357,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) {
|
||||
@ -412,7 +423,7 @@ const ObjInspector = new Lang.Class({
|
||||
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>' });
|
||||
}
|
||||
@ -433,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 });
|
||||
@ -457,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() {
|
||||
@ -465,34 +472,36 @@ const ObjInspector = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
function addBorderPaintHook(actor) {
|
||||
let signalId = actor.connect_after('paint',
|
||||
function () {
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
const RedBorderEffect = new Lang.Class({
|
||||
Name: 'RedBorderEffect',
|
||||
Extends: Clutter.Effect,
|
||||
|
||||
let geom = actor.get_allocation_geometry();
|
||||
let width = 2;
|
||||
vfunc_paint: function() {
|
||||
let actor = this.get_actor();
|
||||
actor.continue_paint();
|
||||
|
||||
// clockwise order
|
||||
Cogl.rectangle(0, 0, geom.width, width);
|
||||
Cogl.rectangle(geom.width - width, width,
|
||||
geom.width, geom.height);
|
||||
Cogl.rectangle(0, geom.height,
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
});
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
|
||||
actor.queue_redraw();
|
||||
return signalId;
|
||||
}
|
||||
let geom = actor.get_allocation_geometry();
|
||||
let width = 2;
|
||||
|
||||
// clockwise order
|
||||
Cogl.rectangle(0, 0, geom.width, width);
|
||||
Cogl.rectangle(geom.width - width, width,
|
||||
geom.width, geom.height);
|
||||
Cogl.rectangle(0, geom.height,
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
},
|
||||
});
|
||||
|
||||
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));
|
||||
@ -506,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));
|
||||
@ -523,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) {
|
||||
@ -543,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();
|
||||
@ -623,12 +626,7 @@ 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);
|
||||
}
|
||||
});
|
||||
|
||||
@ -662,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 });
|
||||
|
||||
@ -723,13 +721,13 @@ const Extensions = new Lang.Class({
|
||||
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 extension = actor._extension;
|
||||
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
this._lookingGlass.close();
|
||||
},
|
||||
|
||||
_onViewErrors: function (actor) {
|
||||
@ -793,24 +791,33 @@ const Extensions = new Lang.Class({
|
||||
text: this._stateToString(extension.state) });
|
||||
metaBox.add(state);
|
||||
|
||||
let viewsource = new Link.Link({ label: _("View Source") });
|
||||
viewsource.actor._extension = extension;
|
||||
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 (extension.metadata.url) {
|
||||
let webpage = new Link.Link({ label: _("Web Page") });
|
||||
webpage.actor._extension = extension;
|
||||
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
metaBox.add(webpage.actor);
|
||||
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._extension = extension;
|
||||
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;
|
||||
}
|
||||
@ -821,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;
|
||||
|
||||
@ -852,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);
|
||||
@ -894,18 +899,14 @@ 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._memory = new Memory();
|
||||
@ -959,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();
|
||||
@ -1089,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) {
|
||||
@ -1129,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);
|
||||
@ -1156,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,7 +12,7 @@ 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];
|
||||
@ -26,6 +26,7 @@ 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';
|
||||
@ -55,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
|
||||
@ -135,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));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -147,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;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -302,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);
|
||||
}
|
||||
},
|
||||
@ -456,6 +453,10 @@ const Magnifier = new Lang.Class({
|
||||
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);
|
||||
@ -490,6 +491,8 @@ const Magnifier = new Lang.Class({
|
||||
|
||||
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));
|
||||
@ -591,6 +594,15 @@ const Magnifier = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_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) {
|
||||
@ -626,6 +638,7 @@ const ZoomRegion = new Lang.Class({
|
||||
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 };
|
||||
|
||||
@ -973,6 +986,27 @@ const ZoomRegion = new Lang.Class({
|
||||
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.
|
||||
@ -1043,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).
|
||||
this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
|
||||
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
mainGroup.add_actor(this._background);
|
||||
|
||||
// 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);
|
||||
this._background.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||
// it.
|
||||
@ -1074,6 +1108,7 @@ const ZoomRegion = new Lang.Class({
|
||||
|
||||
// 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);
|
||||
@ -1290,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();
|
||||
},
|
||||
@ -1318,7 +1353,6 @@ const ZoomRegion = new Lang.Class({
|
||||
if (!this.isActive())
|
||||
return;
|
||||
|
||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
||||
this._background.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
||||
@ -1342,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);
|
||||
@ -1421,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;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1433,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();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1602,12 +1634,14 @@ const MagShaderEffects = new Lang.Class({
|
||||
_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);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1618,6 +1652,7 @@ const MagShaderEffects = new Lang.Class({
|
||||
*/
|
||||
destroyEffects: function() {
|
||||
this._magView.clear_effects();
|
||||
this._colorDesaturation = null;
|
||||
this._brightnessContrast = null;
|
||||
this._inverse = null;
|
||||
this._magView = null;
|
||||
@ -1641,6 +1676,14 @@ const MagShaderEffects = new Lang.Class({
|
||||
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.
|
||||
|
292
js/ui/main.js
@ -10,12 +10,9 @@ 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 KeyringPrompt = imports.ui.keyringPrompt;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
@ -23,54 +20,62 @@ 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 ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const TelepathyClient = imports.ui.telepathyClient;
|
||||
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 OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||
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 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 _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
@ -78,66 +83,20 @@ 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();
|
||||
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);
|
||||
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
automountManager = new AutomountManager.AutomountManager();
|
||||
autorunManager = new AutorunManager.AutorunManager();
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
|
||||
_initRecorder();
|
||||
}
|
||||
|
||||
function createGDMSession() {
|
||||
// We do this this here instead of at the top to prevent GDM
|
||||
// related code from getting loaded in normal user sessions
|
||||
const LoginDialog = imports.gdm.loginDialog;
|
||||
|
||||
let loginDialog = new LoginDialog.LoginDialog();
|
||||
loginDialog.connect('loaded', function() {
|
||||
loginDialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
function createInitialSetupSession() {
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
}
|
||||
|
||||
function _initRecorder() {
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||
|
||||
global.display.add_keybinding('toggle-recording',
|
||||
bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE, function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
if (recorder.is_recording()) {
|
||||
recorder.close();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||
/* Translators: this is a filename used for screencast recording */
|
||||
// xgettext:no-c-format
|
||||
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
|
||||
let pipeline = recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
});
|
||||
wm.setCustomKeybindingHandler('panel-run-dialog',
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
if (sessionMode.isGreeter)
|
||||
screenShield.showDialog();
|
||||
}
|
||||
|
||||
function start() {
|
||||
@ -202,63 +161,35 @@ function start() {
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
||||
panel = new Panel.Panel();
|
||||
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();
|
||||
|
||||
sessionMode.createSession();
|
||||
|
||||
panel.startStatusArea();
|
||||
componentManager = new Components.ComponentManager();
|
||||
|
||||
layoutManager.init();
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
if (sessionMode.hasWorkspaces)
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
|
||||
if (sessionMode.allowExtensions) {
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
}
|
||||
|
||||
if (sessionMode.hasRunDialog) {
|
||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
||||
getRunDialog().open();
|
||||
});
|
||||
}
|
||||
|
||||
if (sessionMode.hasOverview) {
|
||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
|
||||
global.display.connect('overlay-key',
|
||||
Lang.bind(overview, overview.toggle));
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
// Become a prompter for gnome keyring
|
||||
KeyringPrompt.init();
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
|
||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||
@ -278,6 +209,9 @@ function start() {
|
||||
global.screen.connect('restacked', _windowsRestacked);
|
||||
|
||||
_nWorkspacesChanged();
|
||||
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.init();
|
||||
}
|
||||
|
||||
let _workspaces = [];
|
||||
@ -501,9 +435,6 @@ function loadTheme() {
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||
|
||||
if (sessionMode.extraStylesheet)
|
||||
theme.load_stylesheet(sessionMode.extraStylesheet);
|
||||
|
||||
if (previousTheme) {
|
||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||
|
||||
@ -555,85 +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;
|
||||
|
||||
if (!sessionMode.allowKeybindingsWhenModal) {
|
||||
if (modalCount > (overview.visible ? 1 : 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
let keyCode = event.get_key_code();
|
||||
let ignoredModifiers = global.display.get_ignored_modifier_mask();
|
||||
let modifierState = event.get_state() & ~ignoredModifiers;
|
||||
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
||||
|
||||
// This isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
overview.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
|
||||
modifierState);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing; we also disable them in the main view.
|
||||
//
|
||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
// if (!sessionMode.hasWorkspaces)
|
||||
// return false;
|
||||
//
|
||||
// wm.actionMoveWorkspaceLeft();
|
||||
// return true;
|
||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
// if (!sessionMode.hasWorkspaces)
|
||||
// return false;
|
||||
//
|
||||
// wm.actionMoveWorkspaceRight();
|
||||
// return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
||||
if (!sessionMode.hasWorkspaces)
|
||||
return false;
|
||||
|
||||
wm.actionMoveWorkspaceUp();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
||||
if (!sessionMode.hasWorkspaces)
|
||||
return false;
|
||||
|
||||
wm.actionMoveWorkspaceDown();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
||||
case Meta.KeyBindingAction.COMMAND_2:
|
||||
if (!sessionMode.hasRunDialog)
|
||||
return false;
|
||||
|
||||
getRunDialog().open();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
overview.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _findModal(actor) {
|
||||
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
||||
if (modalActorFocusStack[i].actor == actor)
|
||||
@ -642,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
|
||||
@ -656,21 +512,27 @@ 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
|
||||
* global.get_current_time() is assumed.
|
||||
* @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;
|
||||
}
|
||||
@ -683,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;
|
||||
@ -697,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;
|
||||
}
|
||||
@ -725,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');
|
||||
}
|
||||
@ -737,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];
|
||||
@ -746,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);
|
||||
@ -756,6 +623,7 @@ 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() {
|
||||
@ -765,11 +633,11 @@ function createLookingGlass() {
|
||||
return lookingGlass;
|
||||
}
|
||||
|
||||
function getRunDialog() {
|
||||
function openRunDialog() {
|
||||
if (runDialog == null) {
|
||||
runDialog = new RunDialog.RunDialog();
|
||||
}
|
||||
return runDialog;
|
||||
runDialog.open();
|
||||
}
|
||||
|
||||
/**
|
||||
|
1604
js/ui/messageTray.js
@ -14,12 +14,12 @@ const Atk = imports.gi.Atk;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const Layout = imports.ui.layout;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||
const FADE_IN_BUTTONS_TIME = 0.33;
|
||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||
|
||||
const State = {
|
||||
@ -35,17 +35,22 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null });
|
||||
styleClass: null,
|
||||
parentActor: Main.uiGroup,
|
||||
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
|
||||
shouldFadeIn: true });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this._hasModal = false;
|
||||
this._keybindingMode = params.keybindingMode;
|
||||
this._shellReactive = params.shellReactive;
|
||||
this._shouldFadeIn = params.shouldFadeIn;
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
accessible_role: Atk.Role.DIALOG });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
params.parentActor.add_actor(this._group);
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
@ -53,16 +58,18 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||
|
||||
this._actionKeys = {};
|
||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
this._buttonKeys = {};
|
||||
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||
|
||||
this._backgroundBin = new St.Bin();
|
||||
this._monitorConstraint = new Layout.MonitorConstraint();
|
||||
this._backgroundBin.add_constraint(this._monitorConstraint);
|
||||
this._group.add_actor(this._backgroundBin);
|
||||
|
||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
if (params.styleClass != null) {
|
||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
||||
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||
}
|
||||
|
||||
if (!this._shellReactive) {
|
||||
@ -75,29 +82,29 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||
stack.add_actor(this._eventBlocker);
|
||||
stack.add_actor(this._dialogLayout);
|
||||
stack.add_actor(this.dialogLayout);
|
||||
} else {
|
||||
this._backgroundBin.child = this._dialogLayout;
|
||||
this._backgroundBin.child = this.dialogLayout;
|
||||
}
|
||||
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this._dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
this.dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
visible: false,
|
||||
vertical: false });
|
||||
this._dialogLayout.add(this._buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
this.dialogLayout.add(this.buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
|
||||
global.focus_manager.add_group(this._dialogLayout);
|
||||
this._initialKeyFocus = this._dialogLayout;
|
||||
global.focus_manager.add_group(this.dialogLayout);
|
||||
this._initialKeyFocus = this.dialogLayout;
|
||||
this._initialKeyFocusDestroyId = 0;
|
||||
this._savedKeyFocus = null;
|
||||
},
|
||||
@ -106,24 +113,17 @@ const ModalDialog = new Lang.Class({
|
||||
this._group.destroy();
|
||||
},
|
||||
|
||||
clearButtons: function() {
|
||||
this.buttonLayout.destroy_all_children();
|
||||
this._buttonKeys = {};
|
||||
},
|
||||
|
||||
setButtons: function(buttons) {
|
||||
let hadChildren = this._buttonLayout.get_children() > 0;
|
||||
|
||||
this._buttonLayout.destroy_all_children();
|
||||
this._actionKeys = {};
|
||||
|
||||
this._buttonLayout.visible = (buttons.length > 0);
|
||||
this.clearButtons();
|
||||
this.buttonLayout.visible = (buttons.length > 0);
|
||||
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let buttonInfo = buttons[i];
|
||||
let label = buttonInfo['label'];
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
|
||||
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
|
||||
let x_alignment;
|
||||
if (buttons.length == 1)
|
||||
@ -135,66 +135,89 @@ const ModalDialog = new Lang.Class({
|
||||
else
|
||||
x_alignment = St.Align.MIDDLE;
|
||||
|
||||
if (!this._initialKeyFocusDestroyId)
|
||||
this._initialKeyFocus = buttonInfo.button;
|
||||
this._buttonLayout.add(buttonInfo.button,
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: x_alignment,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
buttonInfo.button.connect('clicked', action);
|
||||
|
||||
if (key)
|
||||
this._actionKeys[key] = action;
|
||||
this.addButton(buttonInfo, { expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: x_alignment,
|
||||
y_align: St.Align.MIDDLE });
|
||||
}
|
||||
|
||||
// Fade in buttons if there weren't any before
|
||||
if (!hadChildren && buttons.length > 0) {
|
||||
this._buttonLayout.opacity = 0;
|
||||
Tweener.addTween(this._buttonLayout,
|
||||
{ opacity: 255,
|
||||
time: FADE_IN_BUTTONS_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.emit('buttons-set');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.emit('buttons-set');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
||||
let symbol = keyPressEvent.get_key_symbol();
|
||||
let action = this._actionKeys[symbol];
|
||||
addButton: function(buttonInfo, layoutInfo) {
|
||||
let label = buttonInfo['label'];
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
let isDefault = buttonInfo['default'];
|
||||
|
||||
if (action)
|
||||
let keys;
|
||||
|
||||
if (key)
|
||||
keys = [key];
|
||||
else if (isDefault)
|
||||
keys = [Clutter.KEY_Return, Clutter.KEY_KP_Enter, Clutter.KEY_ISO_Enter];
|
||||
else
|
||||
keys = [];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
button.connect('clicked', action);
|
||||
|
||||
buttonInfo['button'] = button;
|
||||
|
||||
if (isDefault)
|
||||
button.add_style_pseudo_class('default');
|
||||
|
||||
if (!this._initialKeyFocusDestroyId)
|
||||
this._initialKeyFocus = button;
|
||||
|
||||
for (let i in keys)
|
||||
this._buttonKeys[keys[i]] = buttonInfo;
|
||||
|
||||
this.buttonLayout.add(button, layoutInfo);
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
_onKeyReleaseEvent: function(object, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
let buttonInfo = this._buttonKeys[symbol];
|
||||
|
||||
if (!buttonInfo)
|
||||
return false;
|
||||
|
||||
let button = buttonInfo['button'];
|
||||
let action = buttonInfo['action'];
|
||||
|
||||
if (action && button.reactive) {
|
||||
action();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onGroupDestroy: function() {
|
||||
this.emit('destroy');
|
||||
},
|
||||
|
||||
_fadeOpen: function() {
|
||||
let monitor = Main.layoutManager.currentMonitor;
|
||||
|
||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
||||
_fadeOpen: function(onPrimary) {
|
||||
if (onPrimary)
|
||||
this._monitorConstraint.primary = true;
|
||||
else
|
||||
this._monitorConstraint.index = global.screen.get_current_monitor();
|
||||
|
||||
this.state = State.OPENING;
|
||||
|
||||
this._dialogLayout.opacity = 255;
|
||||
this.dialogLayout.opacity = 255;
|
||||
if (this._lightbox)
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
@ -211,19 +234,19 @@ const ModalDialog = new Lang.Class({
|
||||
this._initialKeyFocus = actor;
|
||||
|
||||
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._initialKeyFocus = this._dialogLayout;
|
||||
this._initialKeyFocus = this.dialogLayout;
|
||||
this._initialKeyFocusDestroyId = 0;
|
||||
}));
|
||||
},
|
||||
|
||||
open: function(timestamp) {
|
||||
open: function(timestamp, onPrimary) {
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
return true;
|
||||
|
||||
if (!this.pushModal(timestamp))
|
||||
if (!this.pushModal({ timestamp: timestamp }))
|
||||
return false;
|
||||
|
||||
this._fadeOpen();
|
||||
this._fadeOpen(onPrimary);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -270,7 +293,8 @@ const ModalDialog = new Lang.Class({
|
||||
pushModal: function (timestamp) {
|
||||
if (this._hasModal)
|
||||
return true;
|
||||
if (!Main.pushModal(this._group, timestamp))
|
||||
if (!Main.pushModal(this._group, { timestamp: timestamp,
|
||||
keybindingMode: this._keybindingMode }))
|
||||
return false;
|
||||
|
||||
this._hasModal = true;
|
||||
@ -304,7 +328,7 @@ const ModalDialog = new Lang.Class({
|
||||
return;
|
||||
|
||||
this.popModal(timestamp);
|
||||
Tweener.addTween(this._dialogLayout,
|
||||
Tweener.addTween(this.dialogLayout,
|
||||
{ opacity: 0,
|
||||
time: FADE_OUT_DIALOG_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
@ -87,6 +88,21 @@ const rewriteRules = {
|
||||
]
|
||||
};
|
||||
|
||||
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'bluetooth-applet': 'bluetooth',
|
||||
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
|
||||
// when moved to control center
|
||||
'gnome-sound-applet': 'volume',
|
||||
'nm-applet': 'network',
|
||||
'gnome-power-manager': 'battery',
|
||||
'keyboard': 'keyboard',
|
||||
'a11y-keyboard': 'a11y',
|
||||
'kbd-scrolllock': 'keyboard',
|
||||
'kbd-numlock': 'keyboard',
|
||||
'kbd-capslock': 'keyboard',
|
||||
'ibus-ui-gtk': 'keyboard'
|
||||
};
|
||||
|
||||
const NotificationDaemon = new Lang.Class({
|
||||
Name: 'NotificationDaemon',
|
||||
|
||||
@ -99,18 +115,19 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._notifications = {};
|
||||
this._busProxy = new Bus();
|
||||
|
||||
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
this._trayManager = new Shell.TrayManager();
|
||||
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
|
||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon, hints, size) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
|
||||
_iconForNotificationData: function(icon, hints) {
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
@ -121,20 +138,18 @@ const NotificationDaemon = new Lang.Class({
|
||||
// a large image.
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return textureCache.load_uri_async(icon, size, size);
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/') {
|
||||
let uri = GLib.filename_to_uri(icon, null);
|
||||
return textureCache.load_uri_async(uri, size, size);
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
} else
|
||||
return new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
} else if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
|
||||
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
} else if (hints['image-path']) {
|
||||
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||
} else {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
@ -146,9 +161,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
stockIcon = 'gtk-dialog-error';
|
||||
break;
|
||||
}
|
||||
return new St.Icon({ icon_name: stockIcon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
}
|
||||
},
|
||||
|
||||
@ -243,6 +256,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||
return false;
|
||||
}));
|
||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||
}
|
||||
@ -341,11 +355,11 @@ const NotificationDaemon = new Lang.Class({
|
||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||
ndata.actions, ndata.hints, ndata.notification];
|
||||
|
||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ icon: iconActor,
|
||||
{ gicon: gicon,
|
||||
bannerMarkup: true });
|
||||
ndata.notification = notification;
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
@ -370,7 +384,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
} else {
|
||||
notification.update(summary, body, { icon: iconActor,
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
@ -421,8 +435,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
||||
source.processNotification(notification, sourceIconActor);
|
||||
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
||||
source.processNotification(notification, sourceGIcon);
|
||||
},
|
||||
|
||||
CloseNotification: function(id) {
|
||||
@ -483,6 +497,10 @@ const NotificationDaemon = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
|
||||
return;
|
||||
|
||||
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
|
||||
},
|
||||
|
||||
@ -534,11 +552,11 @@ const Source = new Lang.Class({
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
processNotification: function(notification, icon) {
|
||||
if (!this.app)
|
||||
this._setApp();
|
||||
if (!this.app && icon)
|
||||
this._setSummaryIcon(icon);
|
||||
processNotification: function(notification, gicon) {
|
||||
if (gicon)
|
||||
this._gicon = gicon;
|
||||
if (!this.trayIcon)
|
||||
this.iconUpdated();
|
||||
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
if (notification.resident && this.app && tracker.focus_app == this.app)
|
||||
@ -563,18 +581,12 @@ const Source = new Lang.Class({
|
||||
this.notifications.length > 0)
|
||||
return false;
|
||||
|
||||
if (Main.overview.visible) {
|
||||
// We can't just connect to Main.overview's 'hidden' signal,
|
||||
// because it's emitted *before* it calls popModal()...
|
||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
|
||||
function () {
|
||||
global.disconnect(id);
|
||||
this.trayIcon.click(event);
|
||||
}));
|
||||
Main.overview.hide();
|
||||
} else {
|
||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
|
||||
global.disconnect(id);
|
||||
this.trayIcon.click(event);
|
||||
}
|
||||
}));
|
||||
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -606,10 +618,20 @@ const Source = new Lang.Class({
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this.trayIcon) {
|
||||
this.useNotificationIcon = false;
|
||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
||||
this.iconUpdated();
|
||||
}
|
||||
},
|
||||
|
||||
setTitle: function(title) {
|
||||
// Do nothing if .app is set, we don't want to override the
|
||||
// app name with whatever is provided through libnotify (usually
|
||||
// garbage)
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.parent(title);
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
this.destroyNonResidentNotifications();
|
||||
this.openApp();
|
||||
@ -640,8 +662,18 @@ const Source = new Lang.Class({
|
||||
this.parent();
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
// We set the summary icon ourselves.
|
||||
return null;
|
||||
createIcon: function(size) {
|
||||
if (this.trayIcon) {
|
||||
return new Clutter.Clone({ width: size,
|
||||
height: size,
|
||||
source: this.trayIcon });
|
||||
} else if (this.app) {
|
||||
return this.app.create_icon_texture(size);
|
||||
} else if (this._gicon) {
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_size: size });
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Config = imports.misc.config;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -11,41 +10,38 @@ const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const ContactDisplay = Config.HAVE_FOLKS ? imports.ui.contactDisplay : null;
|
||||
const Dash = imports.ui.dash;
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Panel = imports.ui.panel;
|
||||
const Params = imports.misc.params;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const RemoteSearch = imports.ui.remoteSearch;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
const Wanda = imports.ui.wanda;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||
|
||||
// Time for initial animation going into Overview mode
|
||||
const ANIMATION_TIME = 0.25;
|
||||
|
||||
// We split the screen vertically between the dash and the view selector.
|
||||
const DASH_SPLIT_FRACTION = 0.1;
|
||||
|
||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
||||
|
||||
const SwipeScrollDirection = {
|
||||
NONE: 0,
|
||||
HORIZONTAL: 1,
|
||||
VERTICAL: 2
|
||||
};
|
||||
|
||||
const SwipeScrollResult = {
|
||||
CANCEL: 0,
|
||||
SWIPE: 1,
|
||||
CLICK: 2
|
||||
};
|
||||
const GLSL_DIM_EFFECT_DECLARATIONS = '';
|
||||
const GLSL_DIM_EFFECT_CODE = '\
|
||||
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
|
||||
float elipse_radius = 0.5; \
|
||||
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
|
||||
the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
|
||||
float y = 250.0 / 255.0; \
|
||||
float x = 180.0 / 255.0; \
|
||||
/* interpolate darkening value, based on distance from screen center */ \
|
||||
float val = min(length(dist), elipse_radius); \
|
||||
float a = mix(x, y, val / elipse_radius); \
|
||||
/* dim_factor varies from [1.0 -> 0.5] when overview is showing \
|
||||
We use it to smooth value, then we clamp it to valid color interval */ \
|
||||
a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \
|
||||
/* We\'re blending between: color and black color (obviously omitted in the equation) */ \
|
||||
cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
|
||||
cogl_color_out.a = 1.0;';
|
||||
|
||||
const ShellInfo = new Lang.Class({
|
||||
Name: 'ShellInfo',
|
||||
@ -64,7 +60,14 @@ const ShellInfo = new Lang.Class({
|
||||
this._source.destroy();
|
||||
},
|
||||
|
||||
setMessage: function(text, undoCallback, undoLabel) {
|
||||
setMessage: function(text, options) {
|
||||
options = Params.parse(options, { undoCallback: null,
|
||||
forFeedback: false
|
||||
});
|
||||
|
||||
let undoCallback = options.undoCallback;
|
||||
let forFeedback = options.forFeedback;
|
||||
|
||||
if (this._source == null) {
|
||||
this._source = new MessageTray.SystemNotificationSource();
|
||||
this._source.connect('destroy', Lang.bind(this,
|
||||
@ -77,19 +80,17 @@ const ShellInfo = new Lang.Class({
|
||||
let notification = null;
|
||||
if (this._source.notifications.length == 0) {
|
||||
notification = new MessageTray.Notification(this._source, text, null);
|
||||
notification.setTransient(true);
|
||||
notification.setForFeedback(forFeedback);
|
||||
} else {
|
||||
notification = this._source.notifications[0];
|
||||
notification.update(text, null, { clear: true });
|
||||
}
|
||||
|
||||
notification.setTransient(true);
|
||||
|
||||
this._undoCallback = undoCallback;
|
||||
if (undoCallback) {
|
||||
notification.addButton('system-undo',
|
||||
undoLabel ? undoLabel : _("Undo"));
|
||||
notification.connect('action-invoked',
|
||||
Lang.bind(this, this._onUndoClicked));
|
||||
notification.addButton('system-undo', _("Undo"));
|
||||
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
|
||||
}
|
||||
|
||||
this._source.notify(notification);
|
||||
@ -99,16 +100,22 @@ const ShellInfo = new Lang.Class({
|
||||
const Overview = new Lang.Class({
|
||||
Name: 'Overview',
|
||||
|
||||
_init : function() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
_init: function() {
|
||||
this._overviewCreated = false;
|
||||
this._initCalled = false;
|
||||
|
||||
// We only have an overview in user sessions, so
|
||||
// create a dummy overview in other cases
|
||||
if (this.isDummy) {
|
||||
this.animationInProgress = false;
|
||||
this.visible = false;
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_createOverview: function() {
|
||||
if (this._overviewCreated)
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._overviewCreated = true;
|
||||
|
||||
// The main BackgroundActor is inside global.window_group which is
|
||||
// hidden when displaying the overview, so we create a new
|
||||
@ -116,37 +123,29 @@ const Overview = new Lang.Class({
|
||||
// scenes which allows us to show the background with different
|
||||
// rendering options without duplicating the texture data.
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT,
|
||||
GLSL_DIM_EFFECT_DECLARATIONS,
|
||||
GLSL_DIM_EFFECT_CODE,
|
||||
false);
|
||||
this._background.hide();
|
||||
global.overlay_group.add_actor(this._background);
|
||||
|
||||
this._desktopFade = new St.Bin();
|
||||
global.overlay_group.add_actor(this._desktopFade);
|
||||
|
||||
this._spacing = 0;
|
||||
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
this._group = new St.Widget({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true });
|
||||
this._group._delegate = this;
|
||||
this._group.connect('style-changed',
|
||||
Lang.bind(this, function() {
|
||||
let node = this._group.get_theme_node();
|
||||
let spacing = node.get_length('spacing');
|
||||
if (spacing != this._spacing) {
|
||||
this._spacing = spacing;
|
||||
this._relayout();
|
||||
}
|
||||
}));
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true,
|
||||
vertical: true });
|
||||
this._overview._delegate = this;
|
||||
|
||||
this._group = new St.BoxLayout({ name: 'overview-group' });
|
||||
|
||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||
this._scrollAdjustment = null;
|
||||
this._capturedEventId = 0;
|
||||
this._buttonPressId = 0;
|
||||
|
||||
this._workspacesDisplay = null;
|
||||
|
||||
this.visible = false; // animating to overview, in overview, animating out
|
||||
this._shown = false; // show() and not hide()
|
||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
||||
@ -159,12 +158,11 @@ const Overview = new Lang.Class({
|
||||
// Dash elements, or mouseover handlers in the workspaces.
|
||||
this._coverPane = new Clutter.Rectangle({ opacity: 0,
|
||||
reactive: true });
|
||||
this._group.add_actor(this._coverPane);
|
||||
this._overview.add_actor(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||
|
||||
|
||||
this._group.hide();
|
||||
global.overlay_group.add_actor(this._group);
|
||||
this._overview.hide();
|
||||
global.overlay_group.add_actor(this._overview);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
@ -176,11 +174,21 @@ const Overview = new Lang.Class({
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
||||
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._windowSwitchTimestamp = 0;
|
||||
this._lastActiveWorkspaceIndex = -1;
|
||||
this._lastHoveredWindow = null;
|
||||
this._needsFakePointerEvent = false;
|
||||
|
||||
if (this._initCalled)
|
||||
this.init();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
this._createOverview();
|
||||
},
|
||||
|
||||
// The members we construct that are implemented in JS might
|
||||
@ -188,37 +196,35 @@ const Overview = new Lang.Class({
|
||||
// signal handlers and so forth. So we create them after
|
||||
// construction in this init() method.
|
||||
init: function() {
|
||||
this._initCalled = true;
|
||||
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._shellInfo = new ShellInfo();
|
||||
|
||||
this._viewSelector = new ViewSelector.ViewSelector();
|
||||
this._group.add_actor(this._viewSelector.actor);
|
||||
// Add a clone of the panel to the overview so spacing and such is
|
||||
// automatic
|
||||
this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel.actor }),
|
||||
reactive: false,
|
||||
opacity: 0 });
|
||||
this._overview.add_actor(this._panelGhost);
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||
|
||||
let appView = new AppDisplay.AllAppDisplay();
|
||||
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
||||
|
||||
// Default search providers
|
||||
// Wanda comes obviously first
|
||||
this.addSearchProvider(new Wanda.WandaSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
if (ContactDisplay != null)
|
||||
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
|
||||
|
||||
// Load remote search providers provided by applications
|
||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
|
||||
this._searchEntry = new St.Entry({ name: 'searchEntry',
|
||||
/* Translators: this is the text displayed
|
||||
in the search entry when no search is
|
||||
active; it should not exceed ~30
|
||||
characters. */
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true,
|
||||
can_focus: true });
|
||||
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
||||
x_align: St.Align.MIDDLE });
|
||||
this._overview.add_actor(this._searchEntryBin);
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this._dash = new Dash.Dash();
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this._dash.actor.add_constraint(this._viewSelector.constrainY);
|
||||
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
this._dash.connect('icon-size-changed',
|
||||
Lang.bind(this, function() {
|
||||
@ -227,7 +233,24 @@ const Overview = new Lang.Class({
|
||||
|
||||
// Translators: this is the name of the dock/favorites area on
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
|
||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._group.add(this._viewSelector.actor, { x_fill: true,
|
||||
expand: true });
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._group, { y_fill: true,
|
||||
expand: true });
|
||||
|
||||
// Then account for message tray
|
||||
this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
|
||||
reactive: false,
|
||||
opacity: 0,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._overview.add_actor(this._messageTrayGhost);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
@ -241,11 +264,16 @@ const Overview = new Lang.Class({
|
||||
this._viewSelector.removeSearchProvider(provider);
|
||||
},
|
||||
|
||||
setMessage: function(text, undoCallback, undoLabel) {
|
||||
//
|
||||
// options:
|
||||
// - undoCallback (function): the callback to be called if undo support is needed
|
||||
// - forFeedback (boolean): whether the message is for direct feedback of a user action
|
||||
//
|
||||
setMessage: function(text, options) {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._shellInfo.setMessage(text, undoCallback, undoLabel);
|
||||
this._shellInfo.setMessage(text, options);
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
@ -316,159 +344,11 @@ const Overview = new Lang.Class({
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
setScrollAdjustment: function(adjustment, direction) {
|
||||
addAction: function(action) {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._scrollAdjustment = adjustment;
|
||||
if (this._scrollAdjustment == null)
|
||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||
else
|
||||
this._scrollDirection = direction;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (this._scrollDirection == SwipeScrollDirection.NONE
|
||||
|| event.get_button() != 1)
|
||||
return;
|
||||
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
this._dragStartX = this._dragX = stageX;
|
||||
this._dragStartY = this._dragY = stageY;
|
||||
this._dragStartValue = this._scrollAdjustment.value;
|
||||
this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
|
||||
this._capturedEventId = global.stage.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
this.emit('swipe-scroll-begin');
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let stageX, stageY;
|
||||
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
|
||||
|
||||
switch(event.type()) {
|
||||
case Clutter.EventType.BUTTON_RELEASE:
|
||||
[stageX, stageY] = event.get_coords();
|
||||
|
||||
// default to snapping back to the original value
|
||||
let newValue = this._dragStartValue;
|
||||
|
||||
let minValue = this._scrollAdjustment.lower;
|
||||
let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
|
||||
|
||||
let direction;
|
||||
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
|
||||
direction = stageX > this._dragStartX ? -1 : 1;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
direction *= -1;
|
||||
} else {
|
||||
direction = stageY > this._dragStartY ? -1 : 1;
|
||||
}
|
||||
|
||||
// We default to scroll a full page size; both the first
|
||||
// and the last page may be smaller though, so we need to
|
||||
// adjust difference in those cases.
|
||||
let difference = direction * this._scrollAdjustment.page_size;
|
||||
if (this._dragStartValue + difference > maxValue)
|
||||
difference = maxValue - this._dragStartValue;
|
||||
else if (this._dragStartValue + difference < minValue)
|
||||
difference = minValue - this._dragStartValue;
|
||||
|
||||
// If the user has moved more than half the scroll
|
||||
// difference, we want to "settle" to the new value
|
||||
// even if the user stops dragging rather "throws" by
|
||||
// releasing during the drag.
|
||||
let distance = this._dragStartValue - this._scrollAdjustment.value;
|
||||
let noStop = Math.abs(distance / difference) > 0.5;
|
||||
|
||||
// We detect if the user is stopped by comparing the
|
||||
// timestamp of the button release with the timestamp of
|
||||
// the last motion. Experimentally, a difference of 0 or 1
|
||||
// millisecond indicates that the mouse is in motion, a
|
||||
// larger difference indicates that the mouse is stopped.
|
||||
if ((this._lastMotionTime > 0 &&
|
||||
this._lastMotionTime > event.get_time() - 2) ||
|
||||
noStop) {
|
||||
if (this._dragStartValue + difference >= minValue &&
|
||||
this._dragStartValue + difference <= maxValue)
|
||||
newValue += difference;
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
// See if the user has moved the mouse enough to trigger
|
||||
// a drag
|
||||
if (Math.abs(stageX - this._dragStartX) < threshold &&
|
||||
Math.abs(stageY - this._dragStartY) < threshold) {
|
||||
// no motion? It's a click!
|
||||
result = SwipeScrollResult.CLICK;
|
||||
this.emit('swipe-scroll-end', result);
|
||||
} else {
|
||||
if (newValue == this._dragStartValue)
|
||||
result = SwipeScrollResult.CANCEL;
|
||||
else
|
||||
result = SwipeScrollResult.SWIPE;
|
||||
|
||||
// The event capture handler is disconnected
|
||||
// while scrolling to the final position, so
|
||||
// to avoid undesired prelights we raise
|
||||
// the cover pane.
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
|
||||
Tweener.addTween(this._scrollAdjustment,
|
||||
{ value: newValue,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
this._coverPane.hide();
|
||||
this.emit('swipe-scroll-end',
|
||||
result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
|
||||
return result != SwipeScrollResult.CLICK;
|
||||
|
||||
case Clutter.EventType.MOTION:
|
||||
[stageX, stageY] = event.get_coords();
|
||||
let dx = this._dragX - stageX;
|
||||
let dy = this._dragY - stageY;
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
this._dragX = stageX;
|
||||
this._dragY = stageY;
|
||||
this._lastMotionTime = event.get_time();
|
||||
|
||||
// See if the user has moved the mouse enough to trigger
|
||||
// a drag
|
||||
if (Math.abs(stageX - this._dragStartX) < threshold &&
|
||||
Math.abs(stageY - this._dragStartY) < threshold)
|
||||
return true;
|
||||
|
||||
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
|
||||
else
|
||||
this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
|
||||
} else {
|
||||
this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
// Block enter/leave events to avoid prelights
|
||||
// during swipe-scroll
|
||||
case Clutter.EventType.ENTER:
|
||||
case Clutter.EventType.LEAVE:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
this._overview.add_action(action);
|
||||
},
|
||||
|
||||
_getDesktopClone: function() {
|
||||
@ -478,7 +358,9 @@ const Overview = new Lang.Class({
|
||||
if (windows.length == 0)
|
||||
return null;
|
||||
|
||||
let clone = new Clutter.Clone({ source: windows[0].get_texture() });
|
||||
let window = windows[0];
|
||||
let clone = new Clutter.Clone({ source: window.get_texture(),
|
||||
x: window.x, y: window.y });
|
||||
clone.source.connect('destroy', Lang.bind(this, function() {
|
||||
clone.destroy();
|
||||
}));
|
||||
@ -492,35 +374,27 @@ const Overview = new Lang.Class({
|
||||
this.hide();
|
||||
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
let contentY = Main.panel.actor.height;
|
||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
||||
|
||||
this._group.set_position(primary.x, primary.y);
|
||||
this._group.set_size(primary.width, primary.height);
|
||||
this._overview.set_position(primary.x, primary.y);
|
||||
this._overview.set_size(primary.width, primary.height);
|
||||
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
},
|
||||
|
||||
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
|
||||
let viewWidth = primary.width - dashWidth - this._spacing;
|
||||
let viewHeight = contentHeight - 2 * this._spacing;
|
||||
let viewY = contentY + this._spacing;
|
||||
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_window_actors();
|
||||
let stackIndices = {};
|
||||
|
||||
// Set the dash's x position - y is handled by a constraint
|
||||
let dashX;
|
||||
if (rtl) {
|
||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
dashX = primary.width;
|
||||
} else {
|
||||
dashX = 0;
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
this._dash.actor.set_x(dashX);
|
||||
|
||||
this._viewSelector.actor.set_position(viewX, viewY);
|
||||
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||
this.emit('windows-restacked', stackIndices);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
@ -557,15 +431,33 @@ const Overview = new Lang.Class({
|
||||
return;
|
||||
if (this._shown)
|
||||
return;
|
||||
// Do this manually instead of using _syncInputMode, to handle failure
|
||||
if (!Main.pushModal(this._group))
|
||||
return;
|
||||
this._modal = true;
|
||||
this._animateVisible();
|
||||
this._shown = true;
|
||||
this._syncInputMode();
|
||||
if (!this._modal)
|
||||
return;
|
||||
this._animateVisible();
|
||||
},
|
||||
|
||||
this._buttonPressId = this._group.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
fadeInDesktop: function() {
|
||||
this._desktopFade.opacity = 0;
|
||||
this._desktopFade.show();
|
||||
Tweener.addTween(this._desktopFade,
|
||||
{ opacity: 255,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
fadeOutDesktop: function() {
|
||||
if (!this._desktopFade.child)
|
||||
this._desktopFade.child = this._getDesktopClone();
|
||||
|
||||
this._desktopFade.opacity = 255;
|
||||
this._desktopFade.show();
|
||||
Tweener.addTween(this._desktopFade,
|
||||
{ opacity: 0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
_animateVisible: function() {
|
||||
@ -586,26 +478,12 @@ const Overview = new Lang.Class({
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
global.window_group.hide();
|
||||
this._group.show();
|
||||
this._overview.show();
|
||||
this._background.show();
|
||||
this._viewSelector.show();
|
||||
|
||||
this._workspacesDisplay.show();
|
||||
|
||||
if (!this._desktopFade.child)
|
||||
this._desktopFade.child = this._getDesktopClone();
|
||||
|
||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
||||
this._desktopFade.opacity = 255;
|
||||
this._desktopFade.show();
|
||||
Tweener.addTween(this._desktopFade,
|
||||
{ opacity: 0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
|
||||
this._group.opacity = 0;
|
||||
Tweener.addTween(this._group,
|
||||
this._overview.opacity = 0;
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -614,7 +492,7 @@ const Overview = new Lang.Class({
|
||||
});
|
||||
|
||||
Tweener.addTween(this._background,
|
||||
{ dim_factor: 0.4,
|
||||
{ dim_factor: 0.8,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -657,10 +535,6 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._shown = false;
|
||||
this._syncInputMode();
|
||||
|
||||
if (this._buttonPressId > 0)
|
||||
this._group.disconnect(this._buttonPressId);
|
||||
this._buttonPressId = 0;
|
||||
},
|
||||
|
||||
// hideTemporarily:
|
||||
@ -684,7 +558,7 @@ const Overview = new Lang.Class({
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (this._shown)
|
||||
if (this.visible)
|
||||
this.hide();
|
||||
else
|
||||
this.show();
|
||||
@ -701,20 +575,21 @@ const Overview = new Lang.Class({
|
||||
|
||||
if (this._shown) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._group))
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
|
||||
this._modal = true;
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
} else if (this._shownTemporarily) {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
} else {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
||||
@ -729,19 +604,10 @@ const Overview = new Lang.Class({
|
||||
this.animationInProgress = true;
|
||||
this._hideInProgress = true;
|
||||
|
||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
||||
this._desktopFade.opacity = 0;
|
||||
this._desktopFade.show();
|
||||
Tweener.addTween(this._desktopFade,
|
||||
{ opacity: 255,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
||||
this._workspacesDisplay.zoomFromOverview();
|
||||
this._viewSelector.zoomFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -780,11 +646,10 @@ const Overview = new Lang.Class({
|
||||
|
||||
global.window_group.show();
|
||||
|
||||
this._workspacesDisplay.hide();
|
||||
|
||||
this._viewSelector.hide();
|
||||
this._desktopFade.hide();
|
||||
this._background.hide();
|
||||
this._group.hide();
|
||||
this._overview.hide();
|
||||
|
||||
this.visible = false;
|
||||
this.animationInProgress = false;
|
||||
|
418
js/ui/panel.js
@ -14,13 +14,14 @@ const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const DND = imports.ui.dnd;
|
||||
const Layout = imports.ui.layout;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const DateMenu = imports.ui.dateMenu;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
@ -74,33 +75,41 @@ function _unpremultiply(color) {
|
||||
blue: blue, alpha: color.alpha });
|
||||
};
|
||||
|
||||
const Animation = new Lang.Class({
|
||||
Name: 'Animation',
|
||||
|
||||
const AnimatedIcon = new Lang.Class({
|
||||
Name: 'AnimatedIcon',
|
||||
|
||||
_init: function(name, size) {
|
||||
this.actor = new St.Bin({ visible: false });
|
||||
_init: function(filename, width, height, speed) {
|
||||
this.actor = new St.Bin();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibleNotify));
|
||||
this._speed = speed;
|
||||
|
||||
this._isLoaded = false;
|
||||
this._isPlaying = false;
|
||||
this._timeoutId = 0;
|
||||
this._frame = 0;
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image (global.datadir + '/theme/' + name, size, size);
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||
Lang.bind(this, this._animationsLoaded));
|
||||
this.actor.set_child(this._animations);
|
||||
},
|
||||
|
||||
_disconnectTimeout: function() {
|
||||
play: function() {
|
||||
if (this._isLoaded && this._timeoutId == 0) {
|
||||
if (this._frame == 0)
|
||||
this._showFrame(0);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||
}
|
||||
|
||||
this._isPlaying = true;
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (this._timeoutId > 0) {
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_onVisibleNotify: function() {
|
||||
if (this.actor.visible)
|
||||
this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update));
|
||||
else
|
||||
this._disconnectTimeout();
|
||||
this._isPlaying = false;
|
||||
},
|
||||
|
||||
_showFrame: function(frame) {
|
||||
@ -120,8 +129,24 @@ const AnimatedIcon = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_animationsLoaded: function() {
|
||||
this._isLoaded = true;
|
||||
|
||||
if (this._isPlaying)
|
||||
this.play();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._disconnectTimeout();
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
|
||||
const AnimatedIcon = new Lang.Class({
|
||||
Name: 'AnimatedIcon',
|
||||
Extends: Animation,
|
||||
|
||||
_init: function(name, size) {
|
||||
this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||
}
|
||||
});
|
||||
|
||||
@ -221,14 +246,14 @@ const AppMenuButton = new Lang.Class({
|
||||
Name: 'AppMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function(menuManager) {
|
||||
_init: function(panel) {
|
||||
this.parent(0.0, null, true);
|
||||
|
||||
this.actor.accessible_role = Atk.Role.MENU;
|
||||
|
||||
this._startingApps = [];
|
||||
|
||||
this._menuManager = menuManager;
|
||||
this._menuManager = panel.menuManager;
|
||||
this._targetApp = null;
|
||||
this._appMenuNotifyId = 0;
|
||||
this._actionGroupNotifyId = 0;
|
||||
@ -246,6 +271,10 @@ const AppMenuButton = new Lang.Class({
|
||||
this._container.connect('get-preferred-height', Lang.bind(this, this._getContentPreferredHeight));
|
||||
this._container.connect('allocate', Lang.bind(this, this._contentAllocate));
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
textureCache.connect('icon-theme-changed',
|
||||
Lang.bind(this, this._onIconThemeChanged));
|
||||
|
||||
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
||||
this._iconBox.connect('style-changed',
|
||||
Lang.bind(this, this._onIconBoxStyleChanged));
|
||||
@ -272,6 +301,7 @@ const AppMenuButton = new Lang.Class({
|
||||
this._spinner = new AnimatedIcon('process-working.svg',
|
||||
PANEL_ICON_SIZE);
|
||||
this._container.add_actor(this._spinner.actor);
|
||||
this._spinner.actor.hide();
|
||||
this._spinner.actor.lower_bottom();
|
||||
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
@ -285,7 +315,7 @@ const AppMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this._visible)
|
||||
if (this._visible || Main.screenShield.locked)
|
||||
return;
|
||||
|
||||
this._visible = true;
|
||||
@ -331,6 +361,15 @@ const AppMenuButton = new Lang.Class({
|
||||
this._updateIconBoxClip();
|
||||
},
|
||||
|
||||
_onIconThemeChanged: function() {
|
||||
if (this._iconBox.child == null)
|
||||
return;
|
||||
|
||||
this._iconBox.child.destroy();
|
||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
this._iconBox.set_child(icon);
|
||||
},
|
||||
|
||||
_updateIconBoxClip: function() {
|
||||
let allocation = this._iconBox.allocation;
|
||||
if (this._iconBottomClip > 0)
|
||||
@ -346,12 +385,14 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._stop = true;
|
||||
this.actor.reactive = true;
|
||||
Tweener.addTween(this._spinner.actor,
|
||||
{ opacity: 0,
|
||||
time: SPINNER_ANIMATION_TIME,
|
||||
transition: "easeOutQuad",
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
this._spinner.stop();
|
||||
this._spinner.actor.opacity = 255;
|
||||
this._spinner.actor.hide();
|
||||
}
|
||||
@ -360,6 +401,8 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
startAnimation: function() {
|
||||
this._stop = false;
|
||||
this.actor.reactive = false;
|
||||
this._spinner.play();
|
||||
this._spinner.actor.show();
|
||||
},
|
||||
|
||||
@ -556,11 +599,12 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||
} else {
|
||||
if (this.menu && !(this.menu instanceof PopupMenu.RemoteMenu))
|
||||
if (this.menu.isDummyQuitMenu)
|
||||
return;
|
||||
|
||||
// fallback to older menu
|
||||
menu = new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.TOP, 0);
|
||||
menu.isDummyQuitMenu = true;
|
||||
menu.addAction(_("Quit"), Lang.bind(this, function() {
|
||||
this._targetApp.request_quit();
|
||||
}));
|
||||
@ -573,15 +617,12 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
Signals.addSignalMethods(AppMenuButton.prototype);
|
||||
|
||||
// Activities button. Because everything else in the top bar is a
|
||||
// PanelMenu.Button, it simplifies some things to make this be one too.
|
||||
// We just hack it up to not actually have a menu attached to it.
|
||||
const ActivitiesButton = new Lang.Class({
|
||||
Name: 'ActivitiesButton',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0);
|
||||
this.parent(0.0, null, true);
|
||||
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
||||
|
||||
let container = new Shell.GenericContainer();
|
||||
@ -598,13 +639,8 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
this.actor.label_actor = this._label;
|
||||
|
||||
this._hotCorner = new Layout.HotCorner();
|
||||
container.add_actor(this._hotCorner.actor);
|
||||
|
||||
// Hack up our menu...
|
||||
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
|
||||
this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
|
||||
this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
|
||||
this.hotCorner = new Layout.HotCorner();
|
||||
container.add_actor(this.hotCorner.actor);
|
||||
|
||||
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
@ -612,16 +648,21 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
this.actor.add_style_pseudo_class('overview');
|
||||
this._escapeMenuGrab();
|
||||
this.actor.add_accessible_state (Atk.StateType.CHECKED);
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
this.actor.remove_style_pseudo_class('overview');
|
||||
this._escapeMenuGrab();
|
||||
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
|
||||
}));
|
||||
|
||||
this._xdndTimeOut = 0;
|
||||
|
||||
// Since the hot corner uses stage coordinates, Clutter won't
|
||||
// queue relayouts for us when the panel moves. Queue a relayout
|
||||
// when that happens.
|
||||
Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
|
||||
container.queue_relayout();
|
||||
}));
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
|
||||
@ -649,10 +690,10 @@ const ActivitiesButton = new Lang.Class({
|
||||
}
|
||||
|
||||
hotBox.x1 = Math.round(x);
|
||||
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
|
||||
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
|
||||
hotBox.y1 = Math.round(y);
|
||||
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
|
||||
this._hotCorner.actor.allocate(hotBox, flags);
|
||||
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
|
||||
this.hotCorner.actor.allocate(hotBox, flags);
|
||||
},
|
||||
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
@ -667,46 +708,21 @@ const ActivitiesButton = new Lang.Class({
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_escapeMenuGrab: function() {
|
||||
if (this.menu.isOpen)
|
||||
this.menu.close();
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
if (!this._hotCorner.shouldToggleOverviewOnClick())
|
||||
if (!this.hotCorner.shouldToggleOverviewOnClick())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onMenuOpenRequest: function() {
|
||||
this.menu.isOpen = true;
|
||||
this.menu.emit('open-state-changed', true);
|
||||
},
|
||||
|
||||
_onMenuCloseRequest: function() {
|
||||
this.menu.isOpen = false;
|
||||
this.menu.emit('open-state-changed', false);
|
||||
},
|
||||
|
||||
_onMenuToggleRequest: function() {
|
||||
this.menu.isOpen = !this.menu.isOpen;
|
||||
this.menu.emit('open-state-changed', this.menu.isOpen);
|
||||
},
|
||||
|
||||
_onButtonRelease: function() {
|
||||
if (this.menu.isOpen) {
|
||||
this.menu.close();
|
||||
Main.overview.toggle();
|
||||
}
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onKeyRelease: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
||||
if (this.menu.isOpen)
|
||||
this.menu.close();
|
||||
Main.overview.toggle();
|
||||
}
|
||||
},
|
||||
@ -751,10 +767,11 @@ const PanelCorner = new Lang.Class({
|
||||
return null;
|
||||
|
||||
// Start at the back and work backward
|
||||
let index = children.length - 1;
|
||||
while (!children[index].visible && index >= 0)
|
||||
index--;
|
||||
|
||||
let index;
|
||||
for (index = children.length - 1; index >= 0; index--) {
|
||||
if (children[index].visible)
|
||||
break;
|
||||
}
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
@ -775,10 +792,11 @@ const PanelCorner = new Lang.Class({
|
||||
return null;
|
||||
|
||||
// Start at the front and work forward
|
||||
let index = 0;
|
||||
while (!children[index].visible && index < children.length)
|
||||
index++;
|
||||
|
||||
let index;
|
||||
for (index = 0; index < children.length; index++) {
|
||||
if (children[index].visible)
|
||||
break;
|
||||
}
|
||||
if (index == children.length)
|
||||
return null;
|
||||
|
||||
@ -845,10 +863,13 @@ const PanelCorner = new Lang.Class({
|
||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||
let borderColor = node.get_color('-panel-corner-border-color');
|
||||
|
||||
let noOverlap = borderColor.alpha == 0;
|
||||
let offsetY = noOverlap ? borderWidth : 0;
|
||||
|
||||
let cr = this.actor.get_context();
|
||||
cr.setOperator(Cairo.Operator.SOURCE);
|
||||
|
||||
cr.moveTo(0, 0);
|
||||
cr.moveTo(0, offsetY);
|
||||
if (this._side == St.Side.LEFT)
|
||||
cr.arc(cornerRadius,
|
||||
borderWidth + cornerRadius,
|
||||
@ -857,7 +878,7 @@ const PanelCorner = new Lang.Class({
|
||||
cr.arc(0,
|
||||
borderWidth + cornerRadius,
|
||||
cornerRadius, 3 * Math.PI / 2, 2 * Math.PI);
|
||||
cr.lineTo(cornerRadius, 0);
|
||||
cr.lineTo(cornerRadius, offsetY);
|
||||
cr.closePath();
|
||||
|
||||
let savedPath = cr.copyPath();
|
||||
@ -867,6 +888,9 @@ const PanelCorner = new Lang.Class({
|
||||
Clutter.cairo_set_source_color(cr, over);
|
||||
cr.fill();
|
||||
|
||||
if (noOverlap)
|
||||
return;
|
||||
|
||||
let offset = borderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
|
||||
@ -888,6 +912,30 @@ const PanelCorner = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||
'activities': ActivitiesButton,
|
||||
'appMenu': AppMenuButton,
|
||||
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||
'logo': imports.gdm.loginDialog.LogoMenuButton,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||
};
|
||||
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
|
||||
imports.ui.status.bluetooth.Indicator;
|
||||
|
||||
try {
|
||||
PANEL_ITEM_IMPLEMENTATIONS['network'] =
|
||||
imports.ui.status.network.NMApplet;
|
||||
} catch(e) {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
const Panel = new Lang.Class({
|
||||
Name: 'Panel',
|
||||
@ -897,16 +945,11 @@ const Panel = new Lang.Class({
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._statusArea = {};
|
||||
this._sessionStyle = null;
|
||||
|
||||
Main.overview.connect('shown', Lang.bind(this, function () {
|
||||
this.actor.add_style_class_name('in-overview');
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
this.actor.remove_style_class_name('in-overview');
|
||||
}));
|
||||
this.statusArea = {};
|
||||
|
||||
this._menus = new PopupMenu.PopupMenuManager(this);
|
||||
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
||||
|
||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||
this.actor.add_actor(this._leftBox);
|
||||
@ -933,34 +976,12 @@ const Panel = new Lang.Class({
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
|
||||
/* Button on the left side of the panel. */
|
||||
if (Main.sessionMode.hasOverview) {
|
||||
this._activitiesButton = new ActivitiesButton();
|
||||
this._activities = this._activitiesButton.actor;
|
||||
this._leftBox.add(this._activities);
|
||||
|
||||
// The activities button has a pretend menu, so as to integrate
|
||||
// more cleanly with the rest of the panel
|
||||
this._menus.addMenu(this._activitiesButton.menu);
|
||||
}
|
||||
|
||||
if (Main.sessionMode.hasAppMenu) {
|
||||
this._appMenu = new AppMenuButton(this._menus);
|
||||
this._leftBox.add(this._appMenu.actor);
|
||||
}
|
||||
|
||||
/* center */
|
||||
this._dateMenu = new DateMenu.DateMenuButton();
|
||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
||||
this._menus.addMenu(this._dateMenu.menu);
|
||||
|
||||
/* right */
|
||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
|
||||
this._updatePanel();
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
@ -1020,16 +1041,19 @@ const Panel = new Lang.Class({
|
||||
}
|
||||
this._rightBox.allocate(childBox, flags);
|
||||
|
||||
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
let cornerMinWidth, cornerMinHeight;
|
||||
let cornerWidth, cornerHeight;
|
||||
|
||||
[cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
[cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = cornerWidth;
|
||||
childBox.y1 = allocHeight;
|
||||
childBox.y2 = allocHeight + cornerHeight;
|
||||
this._leftCorner.actor.allocate(childBox, flags);
|
||||
|
||||
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
[cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
[cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
|
||||
childBox.x1 = allocWidth - cornerWidth;
|
||||
childBox.x2 = allocWidth;
|
||||
childBox.y1 = allocHeight;
|
||||
@ -1077,87 +1101,145 @@ const Panel = new Lang.Class({
|
||||
},
|
||||
|
||||
openAppMenu: function() {
|
||||
let menu = this._appMenu.menu;
|
||||
if (!this._appMenu.actor.reactive || menu.isOpen)
|
||||
let indicator = this.statusArea.appMenu;
|
||||
if (!indicator) // appMenu not supported by current session mode
|
||||
return;
|
||||
|
||||
let menu = indicator.menu;
|
||||
if (!indicator.actor.reactive || menu.isOpen)
|
||||
return;
|
||||
|
||||
menu.open();
|
||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
startStatusArea: function() {
|
||||
for (let i = 0; i < Main.sessionMode.statusArea.order.length; i++) {
|
||||
let role = Main.sessionMode.statusArea.order[i];
|
||||
let constructor = Main.sessionMode.statusArea.implementation[role];
|
||||
set boxOpacity(value) {
|
||||
let isReactive = value > 0;
|
||||
|
||||
this._leftBox.opacity = value;
|
||||
this._leftBox.reactive = isReactive;
|
||||
this._centerBox.opacity = value;
|
||||
this._centerBox.reactive = isReactive;
|
||||
this._rightBox.opacity = value;
|
||||
this._rightBox.reactive = isReactive;
|
||||
},
|
||||
|
||||
get boxOpacity() {
|
||||
return this._leftBox.opacity;
|
||||
},
|
||||
|
||||
_updatePanel: function() {
|
||||
let panel = Main.sessionMode.panel;
|
||||
this._hideIndicators();
|
||||
this._updateBox(panel.left, this._leftBox);
|
||||
this._updateBox(panel.center, this._centerBox);
|
||||
this._updateBox(panel.right, this._rightBox);
|
||||
|
||||
if (this._sessionStyle)
|
||||
this._removeStyleClassName(this._sessionStyle);
|
||||
|
||||
this._sessionStyle = Main.sessionMode.panelStyle;
|
||||
if (this._sessionStyle)
|
||||
this._addStyleClassName(this._sessionStyle);
|
||||
},
|
||||
|
||||
_initBox: function(elements, box) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let role = elements[i];
|
||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
// panel icon is not supported (can happen for
|
||||
// bluetooth or network)
|
||||
continue;
|
||||
}
|
||||
|
||||
let indicator = new constructor();
|
||||
this.addToStatusArea(role, indicator, i);
|
||||
}
|
||||
},
|
||||
|
||||
_insertStatusItem: function(actor, position) {
|
||||
let children = this._rightBox.get_children();
|
||||
let i;
|
||||
for (i = children.length - 1; i >= 0; i--) {
|
||||
let rolePosition = children[i]._rolePosition;
|
||||
if (position > rolePosition) {
|
||||
this._rightBox.insert_child_at_index(actor, i + 1);
|
||||
break;
|
||||
_hideIndicators: function() {
|
||||
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
|
||||
let indicator = this.statusArea[role];
|
||||
if (!indicator)
|
||||
continue;
|
||||
if (indicator.menu)
|
||||
indicator.menu.close();
|
||||
indicator.container.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_ensureIndicator: function(role) {
|
||||
let indicator = this.statusArea[role];
|
||||
if (!indicator) {
|
||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
return null;
|
||||
}
|
||||
indicator = new constructor(this);
|
||||
this.statusArea[role] = indicator;
|
||||
}
|
||||
if (i == -1) {
|
||||
// If we didn't find a position, we must be first
|
||||
this._rightBox.insert_child_at_index(actor, 0);
|
||||
}
|
||||
actor._rolePosition = position;
|
||||
return indicator;
|
||||
},
|
||||
|
||||
addToStatusArea: function(role, indicator, position) {
|
||||
if (this._statusArea[role])
|
||||
_updateBox: function(elements, box) {
|
||||
let nChildren = box.get_n_children();
|
||||
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let role = elements[i];
|
||||
let indicator = this._ensureIndicator(role);
|
||||
if (indicator == null)
|
||||
continue;
|
||||
|
||||
this._addToPanelBox(role, indicator, i + nChildren, box);
|
||||
}
|
||||
},
|
||||
|
||||
_addToPanelBox: function(role, indicator, position, box) {
|
||||
let container = indicator.container;
|
||||
container.show();
|
||||
|
||||
let parent = container.get_parent();
|
||||
if (parent)
|
||||
parent.remove_actor(container);
|
||||
|
||||
box.insert_child_at_index(container, position);
|
||||
if (indicator.menu)
|
||||
this.menuManager.addMenu(indicator.menu);
|
||||
this.statusArea[role] = indicator;
|
||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||
delete this.statusArea[role];
|
||||
emitter.disconnect(destroyId);
|
||||
container.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
addToStatusArea: function(role, indicator, position, box) {
|
||||
if (this.statusArea[role])
|
||||
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
|
||||
|
||||
if (!(indicator instanceof PanelMenu.Button))
|
||||
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
||||
|
||||
if (!position)
|
||||
position = 0;
|
||||
this._insertStatusItem(indicator.actor, position);
|
||||
this._menus.addMenu(indicator.menu);
|
||||
|
||||
this._statusArea[role] = indicator;
|
||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||
this._statusArea[role] = null;
|
||||
emitter.disconnect(destroyId);
|
||||
}));
|
||||
|
||||
position = position || 0;
|
||||
let boxes = {
|
||||
left: this._leftBox,
|
||||
center: this._centerBox,
|
||||
right: this._rightBox
|
||||
};
|
||||
let boxContainer = boxes[box] || this._rightBox;
|
||||
this.statusArea[role] = indicator;
|
||||
this._addToPanelBox(role, indicator, position, boxContainer);
|
||||
return indicator;
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon, role) {
|
||||
if (Main.sessionMode.statusArea.implementation[role]) {
|
||||
// This icon is legacy, and replaced by a Shell version
|
||||
// Hide it
|
||||
return;
|
||||
}
|
||||
|
||||
if (Main.sessionMode.statusArea.order.indexOf(role) == -1)
|
||||
return;
|
||||
|
||||
icon.height = PANEL_ICON_SIZE;
|
||||
let buttonBox = new PanelMenu.ButtonBox();
|
||||
let box = buttonBox.actor;
|
||||
box.add_actor(icon);
|
||||
|
||||
this._insertStatusItem(box, Main.sessionMode.statusArea.order.indexOf(role));
|
||||
_addStyleClassName: function(className) {
|
||||
this.actor.add_style_class_name(className);
|
||||
this._rightCorner.actor.add_style_class_name(className);
|
||||
this._leftCorner.actor.add_style_class_name(className);
|
||||
},
|
||||
|
||||
_onTrayIconRemoved: function(o, icon) {
|
||||
let box = icon.get_parent();
|
||||
if (box && box._delegate instanceof PanelMenu.ButtonBox)
|
||||
box.destroy();
|
||||
},
|
||||
_removeStyleClassName: function(className) {
|
||||
this.actor.remove_style_class_name(className);
|
||||
this._rightCorner.actor.remove_style_class_name(className);
|
||||
this._leftCorner.actor.remove_style_class_name(className);
|
||||
}
|
||||
});
|
||||
|
@ -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 Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -20,6 +21,10 @@ const ButtonBox = new Lang.Class({
|
||||
this.actor = new Shell.GenericContainer(params);
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.container = new St.Bin({ y_fill: true,
|
||||
x_fill: true,
|
||||
child: this.actor });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
@ -107,13 +112,19 @@ const Button = new Lang.Class({
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||
|
||||
if (dontCreateMenu)
|
||||
this.menu = null;
|
||||
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
||||
else
|
||||
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
||||
|
||||
this.setName(nameText);
|
||||
},
|
||||
|
||||
setSensitive: function(sensitive) {
|
||||
this.actor.reactive = sensitive;
|
||||
this.actor.can_focus = sensitive;
|
||||
this.actor.track_hover = sensitive;
|
||||
},
|
||||
|
||||
setName: function(text) {
|
||||
if (text != null) {
|
||||
// This is the easiest way to provide a accessible name to
|
||||
@ -175,8 +186,7 @@ const Button = new Lang.Class({
|
||||
_onMenuKeyPress: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||
let focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
let group = focusManager.get_group(this.actor);
|
||||
let group = global.focus_manager.get_group(this.actor);
|
||||
if (group) {
|
||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||
group.navigate_focus(this.actor, direction, false);
|
||||
@ -204,7 +214,8 @@ const Button = new Lang.Class({
|
||||
destroy: function() {
|
||||
this.actor._delegate = null;
|
||||
|
||||
this.menu.destroy();
|
||||
if (this.menu)
|
||||
this.menu.destroy();
|
||||
this.actor.destroy();
|
||||
|
||||
this.emit('destroy');
|
||||
@ -224,19 +235,36 @@ const SystemStatusButton = new Lang.Class({
|
||||
|
||||
_init: function(iconName, nameText) {
|
||||
this.parent(0.0, nameText);
|
||||
|
||||
this._iconActor = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'system-status-icon' });
|
||||
this.actor.add_actor(this._iconActor);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||
this.actor.add_actor(this._box);
|
||||
|
||||
if (iconName)
|
||||
this.setIcon(iconName);
|
||||
},
|
||||
|
||||
addIcon: function(gicon) {
|
||||
let icon = new St.Icon({ gicon: gicon,
|
||||
style_class: 'system-status-icon' });
|
||||
this._box.add_actor(icon);
|
||||
|
||||
return icon;
|
||||
},
|
||||
|
||||
setIcon: function(iconName) {
|
||||
this._iconActor.icon_name = iconName;
|
||||
// Need to first add a NULL GIcon and then set icon_name, to ensure
|
||||
// compatibility with -symbolic fallbacks
|
||||
|
||||
if (!this.mainIcon)
|
||||
this.mainIcon = this.addIcon(null);
|
||||
this.mainIcon.icon_name = iconName;
|
||||
},
|
||||
|
||||
setGIcon: function(gicon) {
|
||||
this._iconActor.gicon = gicon;
|
||||
if (this.mainIcon)
|
||||
this.mainIcon.gicon = gicon;
|
||||
else
|
||||
this.mainIcon = this.addIcon(gicon);
|
||||
}
|
||||
});
|
||||
|
@ -1,430 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
/**
|
||||
* Represents a place object, which is most normally a bookmark entry,
|
||||
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
|
||||
*
|
||||
* @name: String title
|
||||
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
|
||||
* @launch: A JavaScript callback to launch the entry
|
||||
*/
|
||||
const PlaceInfo = new Lang.Class({
|
||||
Name: 'PlaceInfo',
|
||||
|
||||
_init: function(id, name, iconFactory, launch) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this._lowerName = name.toLowerCase();
|
||||
this.iconFactory = iconFactory;
|
||||
this.launch = launch;
|
||||
},
|
||||
|
||||
matchTerms: function(terms) {
|
||||
let mtype = Search.MatchType.NONE;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
let idx = this._lowerName.indexOf(term);
|
||||
if (idx == 0) {
|
||||
mtype = Search.MatchType.PREFIX;
|
||||
} else if (idx > 0) {
|
||||
if (mtype == Search.MatchType.NONE)
|
||||
mtype = Search.MatchType.SUBSTRING;
|
||||
} else {
|
||||
return Search.MatchType.NONE;
|
||||
}
|
||||
}
|
||||
return mtype;
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
||||
function _makeLaunchContext(params)
|
||||
{
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let launchContext = global.create_app_launch_context();
|
||||
if (params.workspace != -1)
|
||||
launchContext.set_desktop(params.workspace);
|
||||
if (params.timestamp != 0)
|
||||
launchContext.set_timestamp(params.timestamp);
|
||||
|
||||
return launchContext;
|
||||
}
|
||||
|
||||
const PlaceDeviceInfo = new Lang.Class({
|
||||
Name: 'PlaceDeviceInfo',
|
||||
Extends: PlaceInfo,
|
||||
|
||||
_init: function(mount) {
|
||||
this._mount = mount;
|
||||
this.name = mount.get_name();
|
||||
this._lowerName = this.name.toLowerCase();
|
||||
this.id = 'mount:' + mount.get_root().get_uri();
|
||||
},
|
||||
|
||||
iconFactory: function(size) {
|
||||
let icon = this._mount.get_icon();
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
|
||||
launch: function(params) {
|
||||
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
||||
_makeLaunchContext(params));
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
return this._mount.can_unmount();
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
if (!this.isRemovable())
|
||||
return;
|
||||
|
||||
if (this._mount.can_eject())
|
||||
this._mount.eject(0, null, Lang.bind(this, this._removeFinish));
|
||||
else
|
||||
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish));
|
||||
},
|
||||
|
||||
_removeFinish: function(o, res, data) {
|
||||
try {
|
||||
if (this._mount.can_eject())
|
||||
this._mount.eject_finish(res);
|
||||
else
|
||||
this._mount.unmount_finish(res);
|
||||
} catch (e) {
|
||||
let message = _("Failed to unmount '%s'").format(o.get_name());
|
||||
Main.overview.setMessage(message,
|
||||
Lang.bind(this, this.remove),
|
||||
_("Retry"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PlacesManager = new Lang.Class({
|
||||
Name: 'PlacesManager',
|
||||
|
||||
_init: function() {
|
||||
this._defaultPlaces = [];
|
||||
this._mounts = [];
|
||||
this._bookmarks = [];
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
||||
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
||||
this._home = new PlaceInfo('special:home', homeLabel,
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
|
||||
});
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
let desktopFile = Gio.file_new_for_path (desktopPath);
|
||||
let desktopUri = desktopFile.get_uri();
|
||||
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
||||
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
||||
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
|
||||
});
|
||||
|
||||
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
||||
function (size) {
|
||||
// do NOT use St.Icon here, it crashes the shell
|
||||
// see wanda.js for details
|
||||
return St.TextureCache.get_default().load_icon_name(null,
|
||||
'applications-internet',
|
||||
St.IconType.FULLCOLOR,
|
||||
size);
|
||||
},
|
||||
function (params) {
|
||||
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
|
||||
// launch it with the workspace from params. It's probably pretty rare
|
||||
// and odd to drag this place onto a workspace in any case
|
||||
|
||||
Util.spawn(['nautilus-connect-server']);
|
||||
});
|
||||
|
||||
this._defaultPlaces.push(this._home);
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
this._defaultPlaces.push(this._connect);
|
||||
|
||||
/*
|
||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||
*/
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
||||
this._updateDevices();
|
||||
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']);
|
||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._monitor.connect('changed', Lang.bind(this, function () {
|
||||
if (this._bookmarkTimeoutId > 0)
|
||||
return;
|
||||
/* Defensive event compression */
|
||||
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._reloadBookmarks();
|
||||
return false;
|
||||
}));
|
||||
}));
|
||||
|
||||
this._reloadBookmarks();
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
this._mounts = [];
|
||||
|
||||
/* first go through all connected drives */
|
||||
let drives = this._volumeMonitor.get_connected_drives();
|
||||
for (let i = 0; i < drives.length; i++) {
|
||||
let volumes = drives[i].get_volumes();
|
||||
for(let j = 0; j < volumes.length; j++) {
|
||||
let mount = volumes[j].get_mount();
|
||||
if(mount != null) {
|
||||
this._addMount(mount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add all volumes that is not associated with a drive */
|
||||
let volumes = this._volumeMonitor.get_volumes();
|
||||
for(let i = 0; i < volumes.length; i++) {
|
||||
if(volumes[i].get_drive() != null)
|
||||
continue;
|
||||
|
||||
let mount = volumes[i].get_mount();
|
||||
if(mount != null) {
|
||||
this._addMount(mount);
|
||||
}
|
||||
}
|
||||
|
||||
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
for(let i = 0; i < mounts.length; i++) {
|
||||
if(mounts[i].is_shadowed())
|
||||
continue;
|
||||
|
||||
if(mounts[i].get_volume())
|
||||
continue;
|
||||
|
||||
this._addMount(mounts[i]);
|
||||
}
|
||||
|
||||
/* We emit two signals, one for a generic 'all places' update
|
||||
* and the other for one specific to mounts. We do this because
|
||||
* clients like PlaceDisplay may only care about places in general
|
||||
* being updated while clients like DashPlaceDisplay care which
|
||||
* specific type of place got updated.
|
||||
*/
|
||||
this.emit('mounts-updated');
|
||||
this.emit('places-updated');
|
||||
|
||||
},
|
||||
|
||||
_reloadBookmarks: function() {
|
||||
|
||||
this._bookmarks = [];
|
||||
|
||||
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
||||
return;
|
||||
|
||||
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
|
||||
|
||||
let bookmarks = bookmarksContent.split('\n');
|
||||
|
||||
let bookmarksToLabel = {};
|
||||
let bookmarksOrder = [];
|
||||
for (let i = 0; i < bookmarks.length; i++) {
|
||||
let bookmarkLine = bookmarks[i];
|
||||
let components = bookmarkLine.split(' ');
|
||||
let bookmark = components[0];
|
||||
if (bookmark in bookmarksToLabel)
|
||||
continue;
|
||||
let label = null;
|
||||
if (components.length > 1)
|
||||
label = components.slice(1).join(' ');
|
||||
bookmarksToLabel[bookmark] = label;
|
||||
bookmarksOrder.push(bookmark);
|
||||
}
|
||||
|
||||
for (let i = 0; i < bookmarksOrder.length; i++) {
|
||||
let bookmark = bookmarksOrder[i];
|
||||
let label = bookmarksToLabel[bookmark];
|
||||
let file = Gio.file_new_for_uri(bookmark);
|
||||
if (!file.query_exists(null))
|
||||
continue;
|
||||
if (label == null)
|
||||
label = Shell.util_get_label_for_uri(bookmark);
|
||||
if (label == null)
|
||||
continue;
|
||||
let icon = Shell.util_get_icon_for_uri(bookmark);
|
||||
|
||||
let item = new PlaceInfo('bookmark:' + bookmark, label,
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
|
||||
});
|
||||
this._bookmarks.push(item);
|
||||
}
|
||||
|
||||
/* See comment in _updateDevices for explanation why there are two signals. */
|
||||
this.emit('bookmarks-updated');
|
||||
this.emit('places-updated');
|
||||
},
|
||||
|
||||
_addMount: function(mount) {
|
||||
let devItem = new PlaceDeviceInfo(mount);
|
||||
this._mounts.push(devItem);
|
||||
},
|
||||
|
||||
getAllPlaces: function () {
|
||||
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
|
||||
},
|
||||
|
||||
getDefaultPlaces: function () {
|
||||
return this._defaultPlaces;
|
||||
},
|
||||
|
||||
getBookmarks: function () {
|
||||
return this._bookmarks;
|
||||
},
|
||||
|
||||
getMounts: function () {
|
||||
return this._mounts;
|
||||
},
|
||||
|
||||
_lookupIndexById: function(sourceArray, id) {
|
||||
for (let i = 0; i < sourceArray.length; i++) {
|
||||
let place = sourceArray[i];
|
||||
if (place.id == id)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
lookupPlaceById: function(id) {
|
||||
let colonIdx = id.indexOf(':');
|
||||
let type = id.substring(0, colonIdx);
|
||||
let sourceArray = null;
|
||||
if (type == 'special')
|
||||
sourceArray = this._defaultPlaces;
|
||||
else if (type == 'mount')
|
||||
sourceArray = this._mounts;
|
||||
else if (type == 'bookmark')
|
||||
sourceArray = this._bookmarks;
|
||||
return sourceArray[this._lookupIndexById(sourceArray, id)];
|
||||
},
|
||||
|
||||
_removeById: function(sourceArray, id) {
|
||||
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PlacesManager.prototype);
|
||||
|
||||
const PlaceSearchProvider = new Lang.Class({
|
||||
Name: 'PlaceSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("PLACES & DEVICES"));
|
||||
this.placesManager = new PlacesManager();
|
||||
},
|
||||
|
||||
getResultMetas: function(resultIds, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < resultIds.length; i++) {
|
||||
let placeInfo = this.placesManager.lookupPlaceById(resultIds[i]);
|
||||
if (!placeInfo)
|
||||
metas.push(null);
|
||||
else
|
||||
metas.push({ 'id': resultIds[i],
|
||||
'name': placeInfo.name,
|
||||
'createIcon': function(size) {
|
||||
return placeInfo.iconFactory(size);
|
||||
}
|
||||
});
|
||||
}
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
let placeInfo = this.placesManager.lookupPlaceById(id);
|
||||
placeInfo.launch(params);
|
||||
},
|
||||
|
||||
_compareResultMeta: function (idA, idB) {
|
||||
let infoA = this.placesManager.lookupPlaceById(idA);
|
||||
let infoB = this.placesManager.lookupPlaceById(idB);
|
||||
return infoA.name.localeCompare(infoB.name);
|
||||
},
|
||||
|
||||
_searchPlaces: function(places, terms) {
|
||||
let prefixResults = [];
|
||||
let substringResults = [];
|
||||
|
||||
terms = terms.map(String.toLowerCase);
|
||||
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
let place = places[i];
|
||||
let mtype = place.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.PREFIX)
|
||||
prefixResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringResults.push(place.id);
|
||||
}
|
||||
prefixResults.sort(Lang.bind(this, this._compareResultMeta));
|
||||
substringResults.sort(Lang.bind(this, this._compareResultMeta));
|
||||
|
||||
this.searchSystem.pushResults(this, prefixResults.concat(substringResults));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
let places = this.placesManager.getAllPlaces();
|
||||
this._searchPlaces(places, terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
let places = previousResults.map(Lang.bind(this, function(id) {
|
||||
return this.placesManager.lookupPlaceById(id);
|
||||
}));
|
||||
this._searchPlaces(places, terms);
|
||||
}
|
||||
});
|
130
js/ui/pointerWatcher.js
Normal file
@ -0,0 +1,130 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
// We stop polling if the user is idle for more than this amount of time
|
||||
const IDLE_TIME = 1000;
|
||||
|
||||
// This file implements a reasonably efficient system for tracking the position
|
||||
// of the mouse pointer. We simply query the pointer from the X server in a loop,
|
||||
// but we turn off the polling when the user is idle.
|
||||
|
||||
let _pointerWatcher = null;
|
||||
function getPointerWatcher() {
|
||||
if (_pointerWatcher == null)
|
||||
_pointerWatcher = new PointerWatcher();
|
||||
|
||||
return _pointerWatcher;
|
||||
}
|
||||
|
||||
const PointerWatch = new Lang.Class({
|
||||
Name: 'PointerWatch',
|
||||
|
||||
_init: function(watcher, interval, callback) {
|
||||
this.watcher = watcher;
|
||||
this.interval = interval;
|
||||
this.callback = callback;
|
||||
},
|
||||
|
||||
// remove:
|
||||
// remove this watch. This function may safely be called
|
||||
// while the callback is executing.
|
||||
remove: function() {
|
||||
this.watcher._removeWatch(this);
|
||||
}
|
||||
});
|
||||
|
||||
const PointerWatcher = new Lang.Class({
|
||||
Name: 'PointerWatcher',
|
||||
|
||||
_init: function() {
|
||||
let idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
|
||||
this._watches = [];
|
||||
this.pointerX = null;
|
||||
this.pointerY = null;
|
||||
},
|
||||
|
||||
// addWatch:
|
||||
// @interval: hint as to the time resolution needed. When the user is
|
||||
// not idle, the position of the pointer will be queried at least
|
||||
// once every this many milliseconds.
|
||||
// @callback: function to call when the pointer position changes - takes
|
||||
// two arguments, X and Y.
|
||||
//
|
||||
// Set up a watch on the position of the mouse pointer. Returns a
|
||||
// PointerWatch object which has a remove() method to remove the watch.
|
||||
addWatch: function(interval, callback) {
|
||||
// Avoid unreliably calling the watch for the current position
|
||||
this._updatePointer();
|
||||
|
||||
let watch = new PointerWatch(this, interval, callback);
|
||||
this._watches.push(watch);
|
||||
this._updateTimeout();
|
||||
return watch;
|
||||
},
|
||||
|
||||
_removeWatch: function(watch) {
|
||||
for (let i = 0; i < this._watches.length; i++) {
|
||||
if (this._watches[i] == watch) {
|
||||
this._watches.splice(i, 1);
|
||||
this._updateTimeout();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onIdleMonitorBecameActive: function(monitor) {
|
||||
this._idle = false;
|
||||
this._updatePointer();
|
||||
this._updateTimeout();
|
||||
},
|
||||
|
||||
_onIdleMonitorBecameIdle: function(monitor) {
|
||||
this._idle = true;
|
||||
this._updateTimeout();
|
||||
},
|
||||
|
||||
_updateTimeout: function() {
|
||||
if (this._timeoutId) {
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
}
|
||||
|
||||
if (this._idle || this._watches.length == 0)
|
||||
return;
|
||||
|
||||
let minInterval = this._watches[0].interval;
|
||||
for (let i = 1; i < this._watches.length; i++)
|
||||
minInterval = Math.min(this._watches[i].interval, minInterval);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(minInterval,
|
||||
Lang.bind(this, this._onTimeout));
|
||||
},
|
||||
|
||||
_onTimeout: function() {
|
||||
this._updatePointer();
|
||||
return true;
|
||||
},
|
||||
|
||||
_updatePointer: function() {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
if (this.pointerX == x && this.pointerY == y)
|
||||
return;
|
||||
|
||||
this.pointerX = x;
|
||||
this.pointerY = y;
|
||||
|
||||
for (let i = 0; i < this._watches.length;) {
|
||||
let watch = this._watches[i];
|
||||
watch.callback(x, y);
|
||||
if (watch == this._watches[i]) // guard against self-removal
|
||||
i++;
|
||||
}
|
||||
}
|
||||
});
|
@ -1,6 +1,5 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
@ -12,8 +11,10 @@ const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GrabHelper = imports.ui.grabHelper;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Separator = imports.ui.separator;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||
@ -37,12 +38,13 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
activate: true,
|
||||
hover: true,
|
||||
sensitive: true,
|
||||
style_class: null
|
||||
style_class: null,
|
||||
can_focus: true
|
||||
});
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
|
||||
reactive: params.reactive,
|
||||
track_hover: params.reactive,
|
||||
can_focus: params.reactive,
|
||||
can_focus: params.can_focus,
|
||||
accessible_role: Atk.Role.MENU_ITEM});
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
@ -60,6 +62,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
this.setSensitive(this.sensitive);
|
||||
|
||||
if (!this._activatable)
|
||||
this.actor.add_style_class_name('popup-inactive-menu-item');
|
||||
|
||||
if (params.style_class)
|
||||
this.actor.add_style_class_name(params.style_class);
|
||||
|
||||
@ -69,10 +74,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
}
|
||||
if (params.reactive && params.hover)
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
|
||||
if (params.reactive) {
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
|
||||
}
|
||||
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
|
||||
},
|
||||
|
||||
_onStyleChanged: function (actor) {
|
||||
@ -136,10 +140,6 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
this.actor.reactive = sensitive;
|
||||
this.actor.can_focus = sensitive;
|
||||
|
||||
if (sensitive)
|
||||
this.actor.remove_style_pseudo_class('insensitive');
|
||||
else
|
||||
this.actor.add_style_pseudo_class('insensitive');
|
||||
this.emit('sensitive-changed', sensitive);
|
||||
},
|
||||
|
||||
@ -184,12 +184,14 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' });
|
||||
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
|
||||
this.actor.add_actor(this._dot);
|
||||
this.actor.add_accessible_state (Atk.StateType.CHECKED);
|
||||
} else {
|
||||
if (!this._dot)
|
||||
return;
|
||||
|
||||
this._dot.destroy();
|
||||
this._dot = null;
|
||||
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
|
||||
}
|
||||
},
|
||||
|
||||
@ -400,31 +402,11 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
||||
Extends: PopupBaseMenuItem,
|
||||
|
||||
_init: function () {
|
||||
this.parent({ reactive: false });
|
||||
this.parent({ reactive: false,
|
||||
can_focus: false});
|
||||
|
||||
this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
|
||||
this.addActor(this._drawingArea, { span: -1, expand: true });
|
||||
this._drawingArea.connect('repaint', Lang.bind(this, this._onRepaint));
|
||||
},
|
||||
|
||||
_onRepaint: function(area) {
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let margin = themeNode.get_length('-margin-horizontal');
|
||||
let gradientHeight = themeNode.get_length('-gradient-height');
|
||||
let startColor = themeNode.get_color('-gradient-start');
|
||||
let endColor = themeNode.get_color('-gradient-end');
|
||||
|
||||
let gradientWidth = (width - margin * 2);
|
||||
let gradientOffset = (height - gradientHeight) / 2;
|
||||
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
|
||||
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
cr.setSource(pattern);
|
||||
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
|
||||
cr.fill();
|
||||
this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
|
||||
this.addActor(this._separator.actor, { span: -1, expand: true });
|
||||
}
|
||||
});
|
||||
|
||||
@ -719,7 +701,8 @@ const Switch = new Lang.Class({
|
||||
|
||||
_init: function(state) {
|
||||
this.actor = new St.Bin({ style_class: 'toggle-switch',
|
||||
accessible_role: Atk.Role.CHECK_BOX});
|
||||
accessible_role: Atk.Role.CHECK_BOX,
|
||||
can_focus: true });
|
||||
// Translators: this MUST be either "toggle-switch-us"
|
||||
// (for toggle switches containing the English words
|
||||
// "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
@ -763,7 +746,7 @@ const PopupSwitchMenuItem = new Lang.Class({
|
||||
{ expand: true, span: -1, align: St.Align.END });
|
||||
|
||||
this._statusLabel = new St.Label({ text: '',
|
||||
style_class: 'popup-inactive-menu-item'
|
||||
style_class: 'popup-status-menu-item'
|
||||
});
|
||||
this._statusBin.child = this._switch.actor;
|
||||
},
|
||||
@ -773,12 +756,10 @@ const PopupSwitchMenuItem = new Lang.Class({
|
||||
this._statusLabel.text = text;
|
||||
this._statusBin.child = this._statusLabel;
|
||||
this.actor.reactive = false;
|
||||
this.actor.can_focus = false;
|
||||
this.actor.accessible_role = Atk.Role.MENU_ITEM;
|
||||
} else {
|
||||
this._statusBin.child = this._switch.actor;
|
||||
this.actor.reactive = true;
|
||||
this.actor.can_focus = true;
|
||||
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
|
||||
}
|
||||
this.checkAccessibleState();
|
||||
@ -869,12 +850,15 @@ const PopupMenuBase = new Lang.Class({
|
||||
// for the menu which causes its prelight state to freeze
|
||||
this.blockSourceEvents = false;
|
||||
|
||||
// Can be set while a menu is up to let all events through without special
|
||||
// menu handling useful for scrollbars in menus, and probably not otherwise.
|
||||
this.passEvents = false;
|
||||
|
||||
this._activeMenuItem = null;
|
||||
this._childMenus = [];
|
||||
this._settingsActions = { };
|
||||
|
||||
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this._setSettingsVisibility(Main.sessionMode.allowSettings);
|
||||
},
|
||||
|
||||
addAction: function(title, callback) {
|
||||
@ -888,9 +872,6 @@ const PopupMenuBase = new Lang.Class({
|
||||
},
|
||||
|
||||
addSettingsAction: function(title, desktopFile) {
|
||||
if (!Main.sessionMode.allowSettings)
|
||||
return null;
|
||||
|
||||
let menuItem = this.addAction(title, function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||
|
||||
@ -902,9 +883,20 @@ const PopupMenuBase = new Lang.Class({
|
||||
Main.overview.hide();
|
||||
app.activate();
|
||||
});
|
||||
|
||||
menuItem.actor.visible = Main.sessionMode.allowSettings;
|
||||
this._settingsActions[desktopFile] = menuItem;
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
_setSettingsVisibility: function(visible) {
|
||||
for (let id in this._settingsActions) {
|
||||
let item = this._settingsActions[id];
|
||||
item.actor.visible = visible;
|
||||
}
|
||||
},
|
||||
|
||||
isEmpty: function() {
|
||||
return this.box.get_n_children() == 0;
|
||||
},
|
||||
@ -942,7 +934,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
_connectSubMenuSignals: function(object, menu) {
|
||||
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
|
||||
this.emit('activate');
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
}));
|
||||
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
|
||||
if (this._activeMenuItem && this._activeMenuItem != submenuItem)
|
||||
@ -977,7 +969,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
}));
|
||||
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
||||
this.emit('activate', menuItem);
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
}));
|
||||
// the weird name is to avoid a conflict with some random property
|
||||
// the menuItem may have, called destroyId
|
||||
@ -1047,14 +1039,17 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
if (menuItem instanceof PopupMenuSection) {
|
||||
this._connectSubMenuSignals(menuItem, menuItem);
|
||||
menuItem._closingId = this.connect('open-state-changed',
|
||||
menuItem._parentOpenStateChangedId = this.connect('open-state-changed',
|
||||
function(self, open) {
|
||||
if (!open)
|
||||
menuItem.close(false);
|
||||
if (open)
|
||||
menuItem.open();
|
||||
else
|
||||
menuItem.close();
|
||||
});
|
||||
menuItem.connect('destroy', Lang.bind(this, function() {
|
||||
menuItem.disconnect(menuItem._subMenuActivateId);
|
||||
menuItem.disconnect(menuItem._subMenuActiveChangeId);
|
||||
this.disconnect(menuItem._parentOpenStateChangedId);
|
||||
|
||||
this.length--;
|
||||
}));
|
||||
@ -1067,7 +1062,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
this._connectItemSignals(menuItem);
|
||||
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
|
||||
if (!open)
|
||||
menuItem.menu.close(false);
|
||||
menuItem.menu.close(BoxPointer.PopupAnimation.FADE);
|
||||
});
|
||||
} else if (menuItem instanceof PopupSeparatorMenuItem) {
|
||||
this._connectItemSignals(menuItem);
|
||||
@ -1089,7 +1084,8 @@ const PopupMenuBase = new Lang.Class({
|
||||
let columnWidths = [];
|
||||
let items = this.box.get_children();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (!items[i].visible)
|
||||
if (!items[i].visible &&
|
||||
!(items[i]._delegate instanceof PopupSubMenu && items[i-1].visible))
|
||||
continue;
|
||||
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
|
||||
let itemColumnWidths = items[i]._delegate.getColumnWidths();
|
||||
@ -1154,9 +1150,9 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
toggle: function() {
|
||||
if (this.isOpen)
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
else
|
||||
this.open(true);
|
||||
this.open(BoxPointer.PopupAnimation.FULL);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -1164,6 +1160,9 @@ const PopupMenuBase = new Lang.Class({
|
||||
this.actor.destroy();
|
||||
|
||||
this.emit('destroy');
|
||||
|
||||
Main.sessionMode.disconnect(this._sessionUpdatedId);
|
||||
this._sessionUpdatedId = 0;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PopupMenuBase.prototype);
|
||||
@ -1185,7 +1184,6 @@ const PopupMenu = new Lang.Class({
|
||||
this.actor = this._boxPointer.actor;
|
||||
this.actor._delegate = this;
|
||||
this.actor.style_class = 'popup-menu-boxpointer';
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
|
||||
this._boxWrapper = new Shell.GenericContainer();
|
||||
this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth));
|
||||
@ -1215,15 +1213,6 @@ const PopupMenu = new Lang.Class({
|
||||
this.box.allocate(box, flags);
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape) {
|
||||
this.close(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
setArrowOrigin: function(origin) {
|
||||
this._boxPointer.setArrowOrigin(origin);
|
||||
},
|
||||
@ -1250,19 +1239,42 @@ const PopupMenu = new Lang.Class({
|
||||
},
|
||||
|
||||
close: function(animate) {
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
|
||||
if (this._activeMenuItem)
|
||||
this._activeMenuItem.setActive(false);
|
||||
|
||||
this._boxPointer.hide(animate);
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.hide(animate);
|
||||
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
|
||||
this.isOpen = false;
|
||||
this.emit('open-state-changed', false);
|
||||
}
|
||||
});
|
||||
|
||||
const PopupDummyMenu = new Lang.Class({
|
||||
Name: 'PopupDummyMenu',
|
||||
|
||||
_init: function(sourceActor) {
|
||||
this.sourceActor = sourceActor;
|
||||
this.actor = sourceActor;
|
||||
this.actor._delegate = this;
|
||||
},
|
||||
|
||||
isChildMenu: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
open: function() { this.emit('open-state-changed', true); },
|
||||
close: function() { this.emit('open-state-changed', false); },
|
||||
toggle: function() {},
|
||||
destroy: function() {
|
||||
this.emit('destroy');
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(PopupDummyMenu.prototype);
|
||||
|
||||
const PopupSubMenu = new Lang.Class({
|
||||
Name: 'PopupSubMenu',
|
||||
Extends: PopupMenuBase,
|
||||
@ -1280,24 +1292,6 @@ const PopupSubMenu = new Lang.Class({
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
vscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
|
||||
// StScrollbar plays dirty tricks with events, calling
|
||||
// clutter_set_motion_events_enabled (FALSE) during the scroll; this
|
||||
// confuses our event tracking, so we just turn it off during the
|
||||
// scroll.
|
||||
let vscroll = this.actor.get_vscroll_bar();
|
||||
vscroll.connect('scroll-start',
|
||||
Lang.bind(this, function() {
|
||||
let topMenu = this._getTopMenu();
|
||||
if (topMenu)
|
||||
topMenu.passEvents = true;
|
||||
}));
|
||||
vscroll.connect('scroll-stop',
|
||||
Lang.bind(this, function() {
|
||||
let topMenu = this._getTopMenu();
|
||||
if (topMenu)
|
||||
topMenu.passEvents = false;
|
||||
}));
|
||||
|
||||
this.actor.add_actor(this.box);
|
||||
this.actor._delegate = this;
|
||||
this.actor.clip_to_allocation = true;
|
||||
@ -1347,6 +1341,11 @@ const PopupSubMenu = new Lang.Class({
|
||||
this.actor.vscrollbar_policy =
|
||||
needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
|
||||
|
||||
if (needsScrollbar)
|
||||
this.actor.add_style_pseudo_class('scrolled');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('scrolled');
|
||||
|
||||
// It looks funny if we animate with a scrollbar (at what point is
|
||||
// the scrollbar added?) so just skip that case
|
||||
if (animate && needsScrollbar)
|
||||
@ -1419,7 +1418,7 @@ const PopupSubMenu = new Lang.Class({
|
||||
// Move focus back to parent menu if the user types Left.
|
||||
|
||||
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
this.sourceActor._delegate.setActive(true);
|
||||
return true;
|
||||
}
|
||||
@ -1453,7 +1452,7 @@ const PopupMenuSection = new Lang.Class({
|
||||
|
||||
// deliberately ignore any attempt to open() or close(), but emit the
|
||||
// corresponding signal so children can still pick it up
|
||||
open: function(animate) { this.emit('open-state-changed', true); },
|
||||
open: function() { this.emit('open-state-changed', true); },
|
||||
close: function() { this.emit('open-state-changed', false); },
|
||||
|
||||
destroy: function() {
|
||||
@ -1501,7 +1500,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.KEY_Right) {
|
||||
this.menu.open(true);
|
||||
this.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
|
||||
@ -1513,7 +1512,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
this.menu.open(true);
|
||||
this.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
},
|
||||
|
||||
_onButtonReleaseEvent: function(actor) {
|
||||
@ -1530,7 +1529,6 @@ const PopupComboMenu = new Lang.Class({
|
||||
|
||||
this.actor = this.box;
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
sourceActor.connect('style-changed',
|
||||
Lang.bind(this, this._onSourceActorStyleChanged));
|
||||
@ -1538,15 +1536,6 @@ const PopupComboMenu = new Lang.Class({
|
||||
global.focus_manager.add_group(this.actor);
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape) {
|
||||
this.close(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onKeyFocusIn: function(actor) {
|
||||
let items = this._getMenuItems();
|
||||
let activeItem = items[this._activeItemPos];
|
||||
@ -1892,10 +1881,6 @@ const RemoteMenu = new Lang.Class({
|
||||
}
|
||||
|
||||
item.actor.reactive = item.actor.can_focus = action.enabled;
|
||||
if (action.enabled)
|
||||
item.actor.remove_style_pseudo_class('insensitive');
|
||||
else
|
||||
item.actor.add_style_pseudo_class('insensitive');
|
||||
|
||||
destroyId = item.connect('destroy', Lang.bind(this, function() {
|
||||
item.disconnect(destroyId);
|
||||
@ -2028,11 +2013,6 @@ const RemoteMenu = new Lang.Class({
|
||||
for (let i = 0; i < action.items.length; i++) {
|
||||
let item = action.items[i];
|
||||
item.actor.reactive = item.actor.can_focus = action.enabled;
|
||||
|
||||
if (action.enabled)
|
||||
item.actor.remove_style_pseudo_class('insensitive');
|
||||
else
|
||||
item.actor.add_style_pseudo_class('insensitive');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2046,20 +2026,14 @@ const PopupMenuManager = new Lang.Class({
|
||||
|
||||
_init: function(owner) {
|
||||
this._owner = owner;
|
||||
this.grabbed = false;
|
||||
|
||||
this._eventCaptureId = 0;
|
||||
this._enterEventId = 0;
|
||||
this._leaveEventId = 0;
|
||||
this._keyFocusNotifyId = 0;
|
||||
this._activeMenu = null;
|
||||
this._grabHelper = new GrabHelper.GrabHelper(owner.actor);
|
||||
this._menus = [];
|
||||
this._menuStack = [];
|
||||
this._preGrabInputMode = null;
|
||||
this._grabbedFromKeynav = false;
|
||||
},
|
||||
|
||||
addMenu: function(menu, position) {
|
||||
if (this._findMenu(menu) > -1)
|
||||
return;
|
||||
|
||||
let menudata = {
|
||||
menu: menu,
|
||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||
@ -2072,6 +2046,8 @@ const PopupMenuManager = new Lang.Class({
|
||||
|
||||
let source = menu.sourceActor;
|
||||
if (source) {
|
||||
if (!menu.blockSourceEvents)
|
||||
this._grabHelper.addActor(source);
|
||||
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
|
||||
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
|
||||
}
|
||||
@ -2084,7 +2060,7 @@ const PopupMenuManager = new Lang.Class({
|
||||
|
||||
removeMenu: function(menu) {
|
||||
if (menu == this._activeMenu)
|
||||
this._closeMenu();
|
||||
this._closeMenu(menu);
|
||||
|
||||
let position = this._findMenu(menu);
|
||||
if (position == -1) // not a menu we manage
|
||||
@ -2101,79 +2077,29 @@ const PopupMenuManager = new Lang.Class({
|
||||
if (menudata.focusInId)
|
||||
menu.sourceActor.disconnect(menudata.focusInId);
|
||||
|
||||
if (menu.sourceActor)
|
||||
this._grabHelper.removeActor(menu.sourceActor);
|
||||
this._menus.splice(position, 1);
|
||||
},
|
||||
|
||||
_grab: function() {
|
||||
Main.pushModal(this._owner.actor);
|
||||
|
||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
||||
// captured-event doesn't see enter/leave events
|
||||
this._enterEventId = global.stage.connect('enter-event', Lang.bind(this, this._onEventCapture));
|
||||
this._leaveEventId = global.stage.connect('leave-event', Lang.bind(this, this._onEventCapture));
|
||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
|
||||
this.grabbed = true;
|
||||
get activeMenu() {
|
||||
let actor = this._grabHelper.currentGrab.actor;
|
||||
if (actor)
|
||||
return actor._delegate;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
_ungrab: function() {
|
||||
global.stage.disconnect(this._eventCaptureId);
|
||||
this._eventCaptureId = 0;
|
||||
global.stage.disconnect(this._enterEventId);
|
||||
this._enterEventId = 0;
|
||||
global.stage.disconnect(this._leaveEventId);
|
||||
this._leaveEventId = 0;
|
||||
global.stage.disconnect(this._keyFocusNotifyId);
|
||||
this._keyFocusNotifyId = 0;
|
||||
|
||||
this.grabbed = false;
|
||||
Main.popModal(this._owner.actor);
|
||||
ignoreRelease: function() {
|
||||
return this._grabHelper.ignoreRelease();
|
||||
},
|
||||
|
||||
_onMenuOpenState: function(menu, open) {
|
||||
if (open) {
|
||||
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
|
||||
this._menuStack.push(this._activeMenu);
|
||||
menu.actor.grab_key_focus();
|
||||
}
|
||||
this._activeMenu = menu;
|
||||
this._grabHelper.grab({ actor: menu.actor, modal: true, focus: menu.sourceActor,
|
||||
onUngrab: Lang.bind(this, this._closeMenu, menu) });
|
||||
} else {
|
||||
if (this._menuStack.length > 0) {
|
||||
this._activeMenu = this._menuStack.pop();
|
||||
if (menu.sourceActor)
|
||||
menu.sourceActor.grab_key_focus();
|
||||
this._didPop = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check what the focus was before calling pushModal/popModal
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._activeMenuContains(focus);
|
||||
|
||||
if (open) {
|
||||
if (!this.grabbed) {
|
||||
this._preGrabInputMode = global.stage_input_mode;
|
||||
this._grabbedFromKeynav = hadFocus;
|
||||
this._grab();
|
||||
}
|
||||
|
||||
if (hadFocus)
|
||||
focus.grab_key_focus();
|
||||
else
|
||||
menu.actor.grab_key_focus();
|
||||
} else if (menu == this._activeMenu) {
|
||||
if (this.grabbed)
|
||||
this._ungrab();
|
||||
this._activeMenu = null;
|
||||
|
||||
if (this._grabbedFromKeynav) {
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED)
|
||||
global.stage_input_mode = Shell.StageInputMode.FOCUSED;
|
||||
if (hadFocus && menu.sourceActor)
|
||||
menu.sourceActor.grab_key_focus();
|
||||
else if (focus)
|
||||
focus.grab_key_focus();
|
||||
}
|
||||
this._grabHelper.ungrab({ actor: menu.actor });
|
||||
}
|
||||
},
|
||||
|
||||
@ -2185,87 +2111,38 @@ const PopupMenuManager = new Lang.Class({
|
||||
this.removeMenu(childMenu);
|
||||
},
|
||||
|
||||
// change the currently-open menu without dropping grab
|
||||
_changeMenu: function(newMenu) {
|
||||
if (this._activeMenu) {
|
||||
// _onOpenMenuState will drop the grab if it sees
|
||||
// this._activeMenu being closed; so clear _activeMenu
|
||||
// before closing it to keep that from happening
|
||||
let oldMenu = this._activeMenu;
|
||||
this._activeMenu = null;
|
||||
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
||||
this._menuStack[i].close(false);
|
||||
oldMenu.close(false);
|
||||
newMenu.open(false);
|
||||
} else
|
||||
newMenu.open(true);
|
||||
let oldMenu = this.activeMenu;
|
||||
if (oldMenu) {
|
||||
oldMenu.close(BoxPointer.PopupAnimation.FADE);
|
||||
newMenu.open(BoxPointer.PopupAnimation.FADE);
|
||||
} else {
|
||||
newMenu.open(BoxPointer.PopupAnimation.FULL);
|
||||
}
|
||||
},
|
||||
|
||||
_onMenuSourceEnter: function(menu) {
|
||||
if (!this.grabbed || menu == this._activeMenu)
|
||||
if (!this._grabHelper.grabbed)
|
||||
return false;
|
||||
|
||||
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
|
||||
if (this._grabHelper.isActorGrabbed(menu.actor))
|
||||
return false;
|
||||
|
||||
if (this._menuStack.indexOf(menu) != -1)
|
||||
return false;
|
||||
|
||||
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
|
||||
let isChildMenu = this._grabHelper.grabStack.some(function(grab) {
|
||||
let existingMenu = grab.actor._delegate;
|
||||
return existingMenu.isChildMenu(menu);
|
||||
});
|
||||
if (isChildMenu)
|
||||
return false;
|
||||
|
||||
this._changeMenu(menu);
|
||||
return false;
|
||||
},
|
||||
|
||||
_onKeyFocusChanged: function() {
|
||||
if (!this.grabbed || !this._activeMenu)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
if (focus) {
|
||||
if (this._activeMenuContains(focus))
|
||||
return;
|
||||
if (this._menuStack.length > 0)
|
||||
return;
|
||||
if (focus._delegate && focus._delegate.menu &&
|
||||
this._findMenu(focus._delegate.menu) != -1)
|
||||
return;
|
||||
}
|
||||
|
||||
this._closeMenu();
|
||||
},
|
||||
|
||||
_onMenuDestroy: function(menu) {
|
||||
this.removeMenu(menu);
|
||||
},
|
||||
|
||||
_activeMenuContains: function(actor) {
|
||||
return this._activeMenu != null
|
||||
&& (this._activeMenu.actor.contains(actor) ||
|
||||
(this._activeMenu.sourceActor && this._activeMenu.sourceActor.contains(actor)));
|
||||
},
|
||||
|
||||
_eventIsOnActiveMenu: function(event) {
|
||||
return this._activeMenuContains(event.get_source());
|
||||
},
|
||||
|
||||
_shouldBlockEvent: function(event) {
|
||||
let src = event.get_source();
|
||||
|
||||
if (this._activeMenu != null && this._activeMenu.actor.contains(src))
|
||||
return false;
|
||||
|
||||
for (let i = 0; i < this._menus.length; i++) {
|
||||
let menu = this._menus[i].menu;
|
||||
if (menu.sourceActor && !menu.blockSourceEvents && menu.sourceActor.contains(src)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_findMenu: function(item) {
|
||||
for (let i = 0; i < this._menus.length; i++) {
|
||||
let menudata = this._menus[i];
|
||||
@ -2275,44 +2152,11 @@ const PopupMenuManager = new Lang.Class({
|
||||
return -1;
|
||||
},
|
||||
|
||||
_onEventCapture: function(actor, event) {
|
||||
if (!this.grabbed)
|
||||
return false;
|
||||
|
||||
if (this._owner.menuEventFilter &&
|
||||
this._owner.menuEventFilter(event))
|
||||
return true;
|
||||
|
||||
if (this._activeMenu != null && this._activeMenu.passEvents)
|
||||
return false;
|
||||
|
||||
if (this._didPop) {
|
||||
this._didPop = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
let activeMenuContains = this._eventIsOnActiveMenu(event);
|
||||
let eventType = event.type();
|
||||
|
||||
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
|
||||
if (activeMenuContains) {
|
||||
return false;
|
||||
} else {
|
||||
this._closeMenu();
|
||||
return true;
|
||||
}
|
||||
} else if (eventType == Clutter.EventType.BUTTON_PRESS && !activeMenuContains) {
|
||||
this._closeMenu();
|
||||
return true;
|
||||
} else if (!this._shouldBlockEvent(event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_closeMenu: function() {
|
||||
if (this._activeMenu != null)
|
||||
this._activeMenu.close(true);
|
||||
_closeMenu: function(isUser, menu) {
|
||||
// If this isn't a user action, we called close()
|
||||
// on the BoxPointer ourselves, so we shouldn't
|
||||
// reanimate.
|
||||
if (isUser)
|
||||
menu.close(BoxPointer.PopupAnimation.FULL);
|
||||
}
|
||||
});
|
||||
|
@ -29,91 +29,167 @@ const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
||||
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
|
||||
<method name="GetInitialResultSet">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
<method name="GetSubsearchResultSet">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
<method name="GetResultMetas">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="aa{sv}" direction="out" />
|
||||
</method>
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
|
||||
|
||||
function loadRemoteSearchProviders(addProviderCallback) {
|
||||
let dataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < dataDirs.length; i++) {
|
||||
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']);
|
||||
let dir = Gio.file_new_for_path(path);
|
||||
if (!dir.query_exists(null))
|
||||
continue;
|
||||
loadRemoteSearchProvidersFromDir(dir, addProviderCallback);
|
||||
let data = { loadedProviders: [],
|
||||
objectPaths: {},
|
||||
addProviderCallback: addProviderCallback };
|
||||
FileUtils.collectFromDatadirsAsync('search-providers',
|
||||
{ loadedCallback: remoteProvidersLoaded,
|
||||
processFile: loadRemoteSearchProvider,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
function loadRemoteSearchProvider(file, info, data) {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
let path = file.get_path();
|
||||
|
||||
try {
|
||||
keyfile.load_from_file(path, 0);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
|
||||
let dirPath = dir.get_path();
|
||||
FileUtils.listDirAsync(dir, Lang.bind(this, function(files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
let path = GLib.build_filenamev([dirPath, files[i].get_name()]);
|
||||
if (!keyfile.has_group(KEY_FILE_GROUP))
|
||||
return;
|
||||
|
||||
try {
|
||||
keyfile.load_from_file(path, 0);
|
||||
} catch(e) {
|
||||
continue;
|
||||
}
|
||||
let remoteProvider;
|
||||
try {
|
||||
let group = KEY_FILE_GROUP;
|
||||
let busName = keyfile.get_string(group, 'BusName');
|
||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||
|
||||
if (!keyfile.has_group(KEY_FILE_GROUP))
|
||||
continue;
|
||||
if (data.objectPaths[objectPath])
|
||||
return;
|
||||
|
||||
let remoteProvider, title;
|
||||
try {
|
||||
let group = KEY_FILE_GROUP;
|
||||
let busName = keyfile.get_string(group, 'BusName');
|
||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||
|
||||
let appInfo = null;
|
||||
try {
|
||||
let desktopId = keyfile.get_string(group, 'DesktopId');
|
||||
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
let icon;
|
||||
if (appInfo) {
|
||||
icon = appInfo.get_icon();
|
||||
title = appInfo.get_name();
|
||||
} else {
|
||||
let iconName = keyfile.get_string(group, 'Icon');
|
||||
icon = new Gio.ThemedIcon({ name: iconName });
|
||||
title = keyfile.get_locale_string(group, 'Title', null);
|
||||
}
|
||||
|
||||
remoteProvider = new RemoteSearchProvider(title,
|
||||
icon,
|
||||
busName,
|
||||
objectPath);
|
||||
} catch(e) {
|
||||
log('Failed to add search provider "%s": %s'.format(title, e.toString()));
|
||||
continue;
|
||||
}
|
||||
|
||||
addProviderCallback(remoteProvider);
|
||||
let appInfo = null;
|
||||
try {
|
||||
let desktopId = keyfile.get_string(group, 'DesktopId');
|
||||
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
||||
} catch (e) {
|
||||
log('Ignoring search provider ' + path + ': missing DesktopId');
|
||||
return;
|
||||
}
|
||||
}));
|
||||
|
||||
};
|
||||
let version = '1';
|
||||
try {
|
||||
version = keyfile.get_string(group, 'Version');
|
||||
} catch (e) {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
if (version >= 2)
|
||||
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
|
||||
else
|
||||
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
||||
|
||||
data.objectPaths[objectPath] = remoteProvider;
|
||||
data.loadedProviders.push(remoteProvider);
|
||||
} catch(e) {
|
||||
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
function remoteProvidersLoaded(loadState) {
|
||||
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
let sortOrder = searchSettings.get_strv('sort-order');
|
||||
let numSorted = sortOrder.length;
|
||||
|
||||
loadState.loadedProviders.sort(
|
||||
function(providerA, providerB) {
|
||||
let idxA, idxB;
|
||||
let appIdA, appIdB;
|
||||
|
||||
appIdA = providerA.appInfo.get_id();
|
||||
appIdB = providerB.appInfo.get_id();
|
||||
|
||||
idxA = sortOrder.indexOf(appIdA);
|
||||
idxB = sortOrder.indexOf(appIdB);
|
||||
|
||||
// if no provider is found in the order, use alphabetical order
|
||||
if ((idxA == -1) && (idxB == -1))
|
||||
return GLib.utf8_collate(providerA.title, providerB.title);
|
||||
|
||||
if (numSorted > 1) {
|
||||
// if providerA is the last, it goes after everything
|
||||
if ((idxA + 1) == numSorted)
|
||||
return 1;
|
||||
// if providerB is the last, it goes after everything
|
||||
else if ((idxB + 1) == numSorted)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if providerA isn't found, it's sorted after providerB
|
||||
if (idxA == -1)
|
||||
return 1;
|
||||
|
||||
// if providerB isn't found, it's sorted after providerA
|
||||
if (idxB == -1)
|
||||
return -1;
|
||||
|
||||
// finally, if both providers are found, return their order in the list
|
||||
return (idxA - idxB);
|
||||
});
|
||||
|
||||
loadState.loadedProviders.forEach(
|
||||
function(provider) {
|
||||
loadState.addProviderCallback(provider);
|
||||
});
|
||||
}
|
||||
|
||||
const RemoteSearchProvider = new Lang.Class({
|
||||
Name: 'RemoteSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function(title, icon, dbusName, dbusPath) {
|
||||
this._proxy = new SearchProviderProxy(Gio.DBus.session,
|
||||
dbusName, dbusPath);
|
||||
_init: function(appInfo, dbusName, dbusPath, proxyType) {
|
||||
if (!proxyType)
|
||||
proxyType = SearchProviderProxy;
|
||||
|
||||
this.proxy = new proxyType(Gio.DBus.session,
|
||||
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
|
||||
|
||||
this.appInfo = appInfo;
|
||||
this.id = appInfo.get_id();
|
||||
this.isRemoteProvider = true;
|
||||
|
||||
this.parent(title.toUpperCase());
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
},
|
||||
|
||||
_onProxyConstructed: function(proxy) {
|
||||
// Do nothing
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
if (meta['gicon']) {
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
||||
icon_size: size,
|
||||
icon_type: St.IconType.FULLCOLOR });
|
||||
icon_size: size });
|
||||
} else if (meta['icon-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = meta['icon-data'];
|
||||
@ -122,10 +198,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
width, height, rowStride, size);
|
||||
}
|
||||
|
||||
// Ugh, but we want to fall back to something ...
|
||||
return new St.Icon({ icon_name: 'text-x-generic',
|
||||
icon_size: size,
|
||||
icon_type: St.IconType.FULLCOLOR });
|
||||
return null;
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
@ -138,9 +211,9 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
this._proxy.GetInitialResultSetRemote(terms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
this.proxy.GetInitialResultSetRemote(terms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
|
||||
this.searchSystem.pushResults(this, []);
|
||||
@ -151,9 +224,9 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
this._proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
|
||||
this.searchSystem.pushResults(this, []);
|
||||
@ -182,9 +255,9 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
this._proxy.GetResultMetasRemote(ids,
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
this.proxy.GetResultMetasRemote(ids,
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
|
||||
callback([]);
|
||||
@ -192,8 +265,32 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
this._proxy.ActivateResultRemote(id);
|
||||
this.proxy.ActivateResultRemote(id);
|
||||
},
|
||||
|
||||
launchSearch: function(terms) {
|
||||
// the provider is not compatible with the new version of the interface, launch
|
||||
// the app itself but warn so we can catch the error in logs
|
||||
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
|
||||
this.appInfo.launch([], global.create_app_launch_context());
|
||||
}
|
||||
});
|
||||
|
||||
const RemoteSearchProvider2 = new Lang.Class({
|
||||
Name: 'RemoteSearchProvider2',
|
||||
Extends: RemoteSearchProvider,
|
||||
|
||||
_init: function(appInfo, dbusName, dbusPath) {
|
||||
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
|
||||
|
||||
this.canLaunchSearch = true;
|
||||
},
|
||||
|
||||
activateResult: function(id, terms) {
|
||||
this.proxy.ActivateResultRemote(id, terms, global.get_current_time());
|
||||
},
|
||||
|
||||
launchSearch: function(terms) {
|
||||
this.proxy.LaunchSearchRemote(terms, global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
@ -202,11 +202,12 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
|
||||
let label = new St.Label({ style_class: 'run-dialog-label',
|
||||
text: _("Please enter a command:") });
|
||||
text: _("Enter a Command") });
|
||||
|
||||
this.contentLayout.add(label, { y_align: St.Align.START });
|
||||
|
||||
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
|
||||
let entry = new St.Entry({ style_class: 'run-dialog-entry',
|
||||
can_focus: true });
|
||||
ShellEntry.addContextMenu(entry);
|
||||
|
||||
entry.label_actor = label;
|
||||
@ -219,7 +220,9 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
this.contentLayout.add(this._errorBox, { expand: true });
|
||||
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic',
|
||||
icon_size: 24,
|
||||
style_class: 'run-dialog-error-icon' });
|
||||
|
||||
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
|
||||
|
||||
@ -234,6 +237,10 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
this._errorBox.hide();
|
||||
|
||||
this.setButtons([{ action: Lang.bind(this, this.close),
|
||||
label: _("Close"),
|
||||
key: Clutter.Escape }]);
|
||||
|
||||
this._pathCompleter = new Gio.FilenameCompleter();
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
@ -244,20 +251,12 @@ const RunDialog = new Lang.Class({
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
this.popModal();
|
||||
if (e.get_state() & Clutter.ModifierType.CONTROL_MASK)
|
||||
this._run(o.get_text(), true);
|
||||
else
|
||||
this._run(o.get_text(), false);
|
||||
if (!this._commandError)
|
||||
this._run(o.get_text(),
|
||||
e.get_state() & Clutter.ModifierType.CONTROL_MASK);
|
||||
if (!this._commandError ||
|
||||
!this.pushModal())
|
||||
this.close();
|
||||
else {
|
||||
if (!this.pushModal())
|
||||
this.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.slash) {
|
||||
|
1016
js/ui/screenShield.js
Normal file
288
js/ui/search.js
@ -1,291 +1,25 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
|
||||
|
||||
// Not currently referenced by the search API, but
|
||||
// this enumeration can be useful for provider
|
||||
// implementations.
|
||||
const MatchType = {
|
||||
NONE: 0,
|
||||
SUBSTRING: 1,
|
||||
PREFIX: 2
|
||||
};
|
||||
|
||||
const SearchResultDisplay = new Lang.Class({
|
||||
Name: 'SearchResultDisplay',
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.actor = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* renderResults:
|
||||
* @results: List of identifier strings
|
||||
* @terms: List of search term strings
|
||||
*
|
||||
* Display the given search matches which resulted
|
||||
* from the given terms. It's expected that not
|
||||
* all results will fit in the space for the container
|
||||
* actor; in this case, show as many as makes sense
|
||||
* for your result type.
|
||||
*
|
||||
* The terms are useful for search match highlighting.
|
||||
*/
|
||||
renderResults: function(results, terms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* clear:
|
||||
* Remove all results from this display.
|
||||
*/
|
||||
clear: function() {
|
||||
this.actor.destroy_all_children();
|
||||
},
|
||||
|
||||
/**
|
||||
* getVisibleResultCount:
|
||||
*
|
||||
* Returns: The number of actors visible.
|
||||
*/
|
||||
getVisibleResultCount: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* SearchProvider:
|
||||
*
|
||||
* Subclass this object to add a new result type
|
||||
* to the search system, then call registerProvider()
|
||||
* in SearchSystem with an instance.
|
||||
* Search is asynchronous and uses the
|
||||
* getInitialResultSet()/getSubsearchResultSet() methods.
|
||||
*/
|
||||
const SearchProvider = new Lang.Class({
|
||||
Name: 'SearchProvider',
|
||||
|
||||
_init: function(title) {
|
||||
this.title = title;
|
||||
this.searchSystem = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* getInitialResultSet:
|
||||
* @terms: Array of search terms, treated as logical AND
|
||||
*
|
||||
* Called when the user first begins a search (most likely
|
||||
* therefore a single term of length one or two), or when
|
||||
* a new term is added.
|
||||
*
|
||||
* Should "return" an array of result identifier strings representing
|
||||
* items which match the given search terms. This
|
||||
* is expected to be a substring match on the metadata for a given
|
||||
* item. Ordering of returned results is up to the discretion of the provider,
|
||||
* but you should follow these heruistics:
|
||||
*
|
||||
* * Put items where the term matches multiple criteria (e.g. name and
|
||||
* description) before single matches
|
||||
* * Put items which match on a prefix before non-prefix substring matches
|
||||
*
|
||||
* We say "return" above, but in order to make the query asynchronous, use
|
||||
* this.searchSystem.pushResults();. The return value should be ignored.
|
||||
*
|
||||
* This function should be fast; do not perform unindexed full-text searches
|
||||
* or network queries.
|
||||
*/
|
||||
getInitialResultSet: function(terms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getSubsearchResultSet:
|
||||
* @previousResults: Array of item identifiers
|
||||
* @newTerms: Updated search terms
|
||||
*
|
||||
* Called when a search is performed which is a "subsearch" of
|
||||
* the previous search; i.e. when every search term has exactly
|
||||
* one corresponding term in the previous search which is a prefix
|
||||
* of the new term.
|
||||
*
|
||||
* This allows search providers to only search through the previous
|
||||
* result set, rather than possibly performing a full re-query.
|
||||
*
|
||||
* Similar to getInitialResultSet, the return value for this will
|
||||
* be ignored; use this.searchSystem.pushResults();.
|
||||
*/
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getResultMetas:
|
||||
* @ids: Result identifier strings
|
||||
*
|
||||
* Call callback with array of objects with 'id', 'name', (both strings) and
|
||||
* 'createIcon' (function(size) returning a Clutter.Texture) properties
|
||||
* with the same number of members as @ids
|
||||
*/
|
||||
getResultMetas: function(ids, callback) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultContainer:
|
||||
*
|
||||
* Search providers may optionally override this to render their
|
||||
* results in a custom fashion. The default implementation
|
||||
* will create a vertical list.
|
||||
*
|
||||
* Returns: An instance of SearchResultDisplay.
|
||||
*/
|
||||
createResultContainerActor: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultActor:
|
||||
* @resultMeta: Object with result metadata
|
||||
* @terms: Array of search terms, should be used for highlighting
|
||||
*
|
||||
* Search providers may optionally override this to render a
|
||||
* particular serch result in a custom fashion. The default
|
||||
* implementation will show the icon next to the name.
|
||||
*
|
||||
* The actor should be an instance of St.Widget, with the style class
|
||||
* 'search-result-content'.
|
||||
*/
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* activateResult:
|
||||
* @id: Result identifier string
|
||||
*
|
||||
* Called when the user chooses a given result.
|
||||
*/
|
||||
activateResult: function(id) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchProvider.prototype);
|
||||
|
||||
const OpenSearchSystem = new Lang.Class({
|
||||
Name: 'OpenSearchSystem',
|
||||
|
||||
_init: function() {
|
||||
this._providers = [];
|
||||
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
|
||||
this._refresh();
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
let res = [];
|
||||
for (let i = 0; i < this._providers.length; i++)
|
||||
res.push({ id: i, name: this._providers[i].name });
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
setSearchTerms: function(terms) {
|
||||
this._terms = terms;
|
||||
},
|
||||
|
||||
_checkSupportedProviderLanguage: function(provider) {
|
||||
if (provider.url.search(/{language}/) == -1)
|
||||
return true;
|
||||
|
||||
let langs = GLib.get_language_names();
|
||||
|
||||
langs.push('en');
|
||||
let lang = null;
|
||||
for (let i = 0; i < langs.length; i++) {
|
||||
for (let k = 0; k < provider.langs.length; k++) {
|
||||
if (langs[i] == provider.langs[k])
|
||||
lang = langs[i];
|
||||
}
|
||||
if (lang)
|
||||
break;
|
||||
}
|
||||
provider.lang = lang;
|
||||
return lang != null;
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
let searchTerms = this._terms.join(' ');
|
||||
|
||||
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
|
||||
if (url.match('{language}'))
|
||||
url = url.replace('{language}', this._providers[id].lang);
|
||||
|
||||
try {
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
} catch (e) {
|
||||
// TODO: remove this after glib will be removed from moduleset
|
||||
// In the default jhbuild, gio is in our prefix but gvfs is not
|
||||
Util.spawn(['gvfs-open', url])
|
||||
}
|
||||
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
_addProvider: function(fileName) {
|
||||
let path = global.datadir + '/open-search-providers/' + fileName;
|
||||
let source = Shell.get_file_contents_utf8_sync(path);
|
||||
let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source);
|
||||
let provider ={ name: name,
|
||||
url: url,
|
||||
id: this._providers.length,
|
||||
icon_uri: icon_uri,
|
||||
langs: langs };
|
||||
if (this._checkSupportedProviderLanguage(provider)) {
|
||||
this._providers.push(provider);
|
||||
this.emit('changed');
|
||||
}
|
||||
},
|
||||
|
||||
_refresh: function() {
|
||||
this._providers = [];
|
||||
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
|
||||
let file = Gio.file_new_for_path(global.datadir + '/open-search-providers');
|
||||
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let enabled = true;
|
||||
let name = files[i].get_name();
|
||||
for (let k = 0; k < names.length; k++)
|
||||
if (names[k] == name)
|
||||
enabled = false;
|
||||
if (enabled)
|
||||
this._addProvider(name);
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(OpenSearchSystem.prototype);
|
||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
|
||||
|
||||
const SearchSystem = new Lang.Class({
|
||||
Name: 'SearchSystem',
|
||||
|
||||
_init: function() {
|
||||
this._providers = [];
|
||||
this._remoteProviders = [];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
registerProvider: function (provider) {
|
||||
provider.searchSystem = this;
|
||||
this._providers.push(provider);
|
||||
|
||||
if (provider.isRemoteProvider)
|
||||
this._remoteProviders.push(provider);
|
||||
},
|
||||
|
||||
unregisterProvider: function (provider) {
|
||||
@ -294,12 +28,20 @@ const SearchSystem = new Lang.Class({
|
||||
return;
|
||||
provider.searchSystem = null;
|
||||
this._providers.splice(index, 1);
|
||||
|
||||
let remoteIndex = this._remoteProviders.indexOf(provider);
|
||||
if (remoteIndex != -1)
|
||||
this._remoteProviders.splice(index, 1);
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
return this._providers;
|
||||
},
|
||||
|
||||
getRemoteProviders: function() {
|
||||
return this._remoteProviders;
|
||||
},
|
||||
|
||||
getTerms: function() {
|
||||
return this._previousTerms;
|
||||
},
|
||||
@ -354,7 +96,7 @@ const SearchSystem = new Lang.Class({
|
||||
results.push([provider, []]);
|
||||
provider.getSubsearchResultSet(previousResults, terms);
|
||||
} catch (error) {
|
||||
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -364,7 +106,7 @@ const SearchSystem = new Lang.Class({
|
||||
results.push([provider, []]);
|
||||
provider.getInitialResultSet(terms);
|
||||
} catch (error) {
|
||||
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,11 @@ const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Separator = imports.ui.separator;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
@ -22,33 +23,103 @@ const SearchResult = new Lang.Class({
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.actor = new St.Button({ style_class: 'search-result',
|
||||
reactive: true,
|
||||
this.terms = terms;
|
||||
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
|
||||
this.actor._delegate = this;
|
||||
this._dragActorSource = null;
|
||||
this.actor.connect('clicked', Lang.bind(this, this.activate));
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResult = new Lang.Class({
|
||||
Name: 'ListSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
ICON_SIZE: 64,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'list-search-result';
|
||||
this.actor.x_fill = true;
|
||||
|
||||
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
// An icon for, or thumbnail of, content
|
||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||
if (icon) {
|
||||
content.add(icon);
|
||||
}
|
||||
|
||||
let details = new St.BoxLayout({ vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let title = new St.Label({ style_class: 'list-search-result-title',
|
||||
text: this.metaInfo['name'] })
|
||||
details.add(title, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
// TODO: should highlight terms in the description here
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'list-search-result-description',
|
||||
text: '"' + this.metaInfo['description'] + '"' });
|
||||
details.add(description, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const GridSearchResult = new Lang.Class({
|
||||
Name: 'GridSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'grid-search-result';
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
let dragSource = null;
|
||||
|
||||
if (content == null) {
|
||||
content = new St.Bin({ style_class: 'search-result-content',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
accessible_role: Atk.Role.PUSH_BUTTON });
|
||||
content = new St.Bin();
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
content.set_child(icon.actor);
|
||||
this._dragActorSource = icon.icon;
|
||||
content.label_actor = icon.label;
|
||||
dragSource = icon.icon;
|
||||
} else {
|
||||
if (content._delegate && content._delegate.getDragActorSource)
|
||||
this._dragActorSource = content._delegate.getDragActorSource();
|
||||
dragSource = content._delegate.getDragActorSource();
|
||||
}
|
||||
this._content = content;
|
||||
this.actor.set_child(content);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
|
||||
this.actor.set_child(content);
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
@ -63,32 +134,18 @@ const SearchResult = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this._content.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._content.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onResultClicked: function(actor) {
|
||||
this.activate();
|
||||
if (!dragSource)
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
dragSource = content;
|
||||
this._dragActorSource = dragSource;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
if (this._dragActorSource)
|
||||
return this._dragActorSource;
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
return this._content;
|
||||
return this._dragActorSource;
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
getDragActor: function() {
|
||||
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||
},
|
||||
|
||||
@ -96,51 +153,52 @@ const SearchResult = new Lang.Class({
|
||||
if (this.provider.dragActivateResult)
|
||||
this.provider.dragActivateResult(this.metaInfo.id, params);
|
||||
else
|
||||
this.provider.activateResult(this.metaInfo.id, params);
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResults = new Lang.Class({
|
||||
Name: 'ListSearchResults',
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
Extends: Search.SearchResultDisplay,
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
_init: function(provider, grid) {
|
||||
this.parent(provider);
|
||||
|
||||
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.START });
|
||||
|
||||
this.actor.set_child(this._grid.actor);
|
||||
this._width = 0;
|
||||
this.actor.connect('notify::width', Lang.bind(this, function() {
|
||||
this._width = this.actor.width;
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
let results = this.getResultsForDisplay();
|
||||
if (results.length == 0)
|
||||
return;
|
||||
|
||||
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
|
||||
this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||
this.providerIcon = new ProviderIcon(provider);
|
||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
provider.launchSearch(this._terms);
|
||||
Main.overview.toggle();
|
||||
}));
|
||||
}));
|
||||
|
||||
this.actor.add(this.providerIcon, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._content);
|
||||
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
|
||||
- alreadyVisible;
|
||||
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
|
||||
let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
|
||||
|
||||
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
|
||||
|
||||
return this._notDisplayedResult.splice(0, numResults);
|
||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
||||
return newResults;
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this._grid.visibleItemsCount();
|
||||
return this._content.get_n_children();
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return this._notDisplayedResult.length > 0;
|
||||
},
|
||||
|
||||
setResults: function(results, terms) {
|
||||
@ -152,7 +210,68 @@ const GridSearchResults = new Lang.Class({
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new SearchResult(this.provider, metas[i], this._terms);
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this._content.destroy_all_children();
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this.getVisibleResultCount() > 0)
|
||||
return this._content.get_child_at_index(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||
|
||||
this.actor.set_child(this._grid.actor);
|
||||
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||
let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
|
||||
- alreadyVisible;
|
||||
|
||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
||||
return newResults;
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this._grid.visibleItemsCount();
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return this._notDisplayedResult.length > 0;
|
||||
},
|
||||
|
||||
setResults: function(results, terms) {
|
||||
// copy the lists
|
||||
this._notDisplayedResult = results.slice(0);
|
||||
this._terms = terms.slice(0);
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
@ -173,10 +292,9 @@ const GridSearchResults = new Lang.Class({
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
|
||||
_init: function(searchSystem, openSearchSystem) {
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||
this._openSearchSystem = openSearchSystem;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
vertical: true });
|
||||
@ -184,105 +302,67 @@ const SearchResults = new Lang.Class({
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
|
||||
let scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
scrollView.add_actor(this._content);
|
||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this._scrollView.add_actor(this._content);
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this._scrollView.add_action(action);
|
||||
|
||||
this.actor.add(scrollView, { x_fill: true,
|
||||
y_fill: false,
|
||||
expand: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let adjustment = scrollView.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
}));
|
||||
this.actor.add(this._scrollView, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
||||
this._content.add(this._statusText);
|
||||
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this._content.add(this._statusBin, { expand: true });
|
||||
this._statusBin.add_actor(this._statusText);
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerMeta = [];
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderMeta(this._providers[i]);
|
||||
}
|
||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||
this.actor.add(this._searchProvidersBox);
|
||||
|
||||
this._openSearchProviders = [];
|
||||
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
|
||||
this._updateOpenSearchProviderButtons();
|
||||
|
||||
this._highlightDefault = false;
|
||||
this._defaultResult = null;
|
||||
},
|
||||
|
||||
_updateOpenSearchProviderButtons: function() {
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++)
|
||||
this._openSearchProviders[i].actor.destroy();
|
||||
this._openSearchProviders = this._openSearchSystem.getProviders();
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++)
|
||||
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
|
||||
},
|
||||
|
||||
_createOpenSearchProviderButton: function(provider) {
|
||||
let button = new St.Button({ style_class: 'dash-search-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_align: St.Align.MIDDLE });
|
||||
let bin = new St.Bin({ x_fill: false,
|
||||
x_align:St.Align.MIDDLE });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._openSearchSystem.activateResult(provider.id);
|
||||
}));
|
||||
let title = new St.Label({ text: provider.name,
|
||||
style_class: 'dash-search-button-label' });
|
||||
|
||||
button.label_actor = title;
|
||||
bin.set_child(title);
|
||||
button.set_child(bin);
|
||||
provider.actor = button;
|
||||
|
||||
button.setSelected = function(selected) {
|
||||
if (selected)
|
||||
button.add_style_pseudo_class('selected');
|
||||
else
|
||||
button.remove_style_pseudo_class('selected');
|
||||
};
|
||||
button.activate = Lang.bind(this, function() {
|
||||
this._openSearchSystem.activateResult(provider.id);
|
||||
});
|
||||
button.actor = button;
|
||||
|
||||
this._searchProvidersBox.add(button);
|
||||
_onPan: function(action) {
|
||||
let [dist, dx, dy] = action.get_motion_delta(0);
|
||||
let adjustment = this._scrollView.vscroll.adjustment;
|
||||
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
||||
return false;
|
||||
},
|
||||
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
let title = new St.Label({ style_class: 'search-section-header',
|
||||
text: provider.title });
|
||||
providerBox.add(title);
|
||||
let providerIcon = null;
|
||||
let resultDisplay = null;
|
||||
|
||||
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
||||
if (provider.appInfo) {
|
||||
resultDisplay = new ListSearchResults(provider);
|
||||
providerIcon = resultDisplay.providerIcon;
|
||||
} else {
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
|
||||
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
providerBox.add(resultDisplayBin, { expand: true });
|
||||
let resultDisplay = provider.createResultContainerActor();
|
||||
if (resultDisplay == null) {
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||
providerBox.add(separator.actor);
|
||||
|
||||
this._providerMeta.push({ provider: provider,
|
||||
actor: providerBox,
|
||||
icon: providerIcon,
|
||||
resultDisplay: resultDisplay });
|
||||
this._content.add(providerBox);
|
||||
},
|
||||
@ -314,20 +394,18 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
reset: function() {
|
||||
this._searchSystem.reset();
|
||||
this._statusText.hide();
|
||||
this._statusBin.hide();
|
||||
this._clearDisplay();
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
this.reset();
|
||||
this._statusText.set_text(_("Searching..."));
|
||||
this._statusText.show();
|
||||
this._statusBin.show();
|
||||
},
|
||||
|
||||
doSearch: function (searchString) {
|
||||
this._searchSystem.updateSearch(searchString);
|
||||
let terms = this._searchSystem.getTerms();
|
||||
this._openSearchSystem.setSearchTerms(terms);
|
||||
},
|
||||
|
||||
_metaForProvider: function(provider) {
|
||||
@ -350,9 +428,6 @@ const SearchResults = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!newDefaultResult)
|
||||
newDefaultResult = this._searchProvidersBox.get_first_child();
|
||||
|
||||
if (newDefaultResult != this._defaultResult) {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(false);
|
||||
@ -373,10 +448,10 @@ const SearchResults = new Lang.Class({
|
||||
}
|
||||
|
||||
if (!haveResults) {
|
||||
this._statusText.set_text(_("No matching results."));
|
||||
this._statusText.show();
|
||||
this._statusText.set_text(_("No results."));
|
||||
this._statusBin.show();
|
||||
} else {
|
||||
this._statusText.hide();
|
||||
this._statusBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
@ -394,6 +469,11 @@ const SearchResults = new Lang.Class({
|
||||
meta.resultDisplay.setResults(providerResults, terms);
|
||||
let results = meta.resultDisplay.getResultsForDisplay();
|
||||
|
||||
if (meta.icon)
|
||||
meta.icon.moreIcon.visible =
|
||||
meta.resultDisplay.hasMoreResults() &&
|
||||
provider.canLaunchSearch;
|
||||
|
||||
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.actor.show();
|
||||
@ -439,10 +519,37 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
let from = this._defaultResult ? this._defaultResult.actor : null;
|
||||
this.actor.navigate_focus(from, direction, false);
|
||||
if (this._defaultResult) {
|
||||
// The default result appears focused, so navigate directly to the
|
||||
// next result.
|
||||
this.actor.navigate_focus(global.stage.key_focus, direction, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ProviderIcon = new Lang.Class({
|
||||
Name: 'ProviderIcon',
|
||||
Extends: St.Button,
|
||||
|
||||
PROVIDER_ICON_SIZE: 48,
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.parent({ style_class: 'search-provider-icon',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true });
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this.set_child(this._content);
|
||||
|
||||
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
|
||||
visible: false,
|
||||
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
|
||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
||||
gicon: provider.appInfo.get_icon() });
|
||||
this._content.add_actor(icon);
|
||||
this._content.add_actor(this.moreIcon);
|
||||
}
|
||||
});
|
||||
|
34
js/ui/separator.js
Normal file
@ -0,0 +1,34 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Cairo = imports.cairo;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const HorizontalSeparator = new Lang.Class({
|
||||
Name: 'HorizontalSeparator',
|
||||
|
||||
_init: function (params) {
|
||||
this.actor = new St.DrawingArea(params);
|
||||
this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
|
||||
},
|
||||
|
||||
_onRepaint: function(area) {
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let margin = themeNode.get_length('-margin-horizontal');
|
||||
let gradientHeight = themeNode.get_length('-gradient-height');
|
||||
let startColor = themeNode.get_color('-gradient-start');
|
||||
let endColor = themeNode.get_color('-gradient-end');
|
||||
|
||||
let gradientWidth = (width - margin * 2);
|
||||
let gradientOffset = (height - gradientHeight) / 2;
|
||||
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
|
||||
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
cr.setSource(pattern);
|
||||
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
|
||||
cr.fill();
|
||||
}
|
||||
});
|
@ -1,124 +1,219 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
|
||||
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||
};
|
||||
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] =
|
||||
imports.ui.status.bluetooth.Indicator;
|
||||
|
||||
try {
|
||||
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] =
|
||||
imports.ui.status.network.NMApplet;
|
||||
} catch(e) {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
|
||||
const DEFAULT_MODE = 'user';
|
||||
const DEFAULT_MODE = 'restrictive';
|
||||
|
||||
const _modes = {
|
||||
'gdm': { hasOverview: false,
|
||||
hasAppMenu: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: true,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
createSession: Main.createGDMSession,
|
||||
extraStylesheet: global.datadir + '/theme/gdm.css',
|
||||
statusArea: {
|
||||
order: [
|
||||
'a11y', 'display', 'keyboard',
|
||||
'volume', 'battery', 'powerMenu'
|
||||
],
|
||||
implementation: {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
|
||||
}
|
||||
}
|
||||
},
|
||||
'restrictive': {
|
||||
parentMode: null,
|
||||
hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
enabledExtensions: [],
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
hasWindows: false,
|
||||
hasNotifications: false,
|
||||
isLocked: false,
|
||||
isGreeter: false,
|
||||
isPrimary: false,
|
||||
unlockDialog: null,
|
||||
components: [],
|
||||
panel: {
|
||||
left: [],
|
||||
center: [],
|
||||
right: []
|
||||
},
|
||||
panelStyle: null
|
||||
},
|
||||
|
||||
'initial-setup': { hasOverview: false,
|
||||
hasAppMenu: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
createSession: Main.createInitialSetupSession,
|
||||
extraStylesheet: null,
|
||||
statusArea: {
|
||||
order: [
|
||||
'a11y', 'keyboard', 'volume'
|
||||
],
|
||||
implementation: {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'keyboard': imports.ui.status.keyboard.XKBIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator
|
||||
}
|
||||
}
|
||||
},
|
||||
'gdm': {
|
||||
hasNotifications: true,
|
||||
isGreeter: true,
|
||||
isPrimary: true,
|
||||
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
||||
components: ['polkitAgent'],
|
||||
panel: {
|
||||
left: ['logo'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'display', 'keyboard',
|
||||
'volume', 'battery', 'powerMenu']
|
||||
},
|
||||
panelStyle: 'login-screen'
|
||||
},
|
||||
|
||||
'user': { hasOverview: true,
|
||||
hasAppMenu: true,
|
||||
showCalendarEvents: true,
|
||||
allowSettings: true,
|
||||
allowExtensions: true,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: true,
|
||||
hasWorkspaces: true,
|
||||
createSession: Main.createUserSession,
|
||||
extraStylesheet: null,
|
||||
statusArea: {
|
||||
order: [
|
||||
'input-method', 'a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'userMenu'
|
||||
],
|
||||
implementation: STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION
|
||||
}
|
||||
}
|
||||
'lock-screen': {
|
||||
isLocked: true,
|
||||
isGreeter: undefined,
|
||||
unlockDialog: undefined,
|
||||
components: ['polkitAgent', 'telepathyClient'],
|
||||
panel: {
|
||||
left: ['userMenu'],
|
||||
center: [],
|
||||
right: ['lockScreen']
|
||||
},
|
||||
panelStyle: 'lock-screen'
|
||||
},
|
||||
|
||||
'unlock-dialog': {
|
||||
isLocked: true,
|
||||
unlockDialog: undefined,
|
||||
components: ['polkitAgent', 'telepathyClient'],
|
||||
panel: {
|
||||
left: ['userMenu'],
|
||||
center: [],
|
||||
right: ['a11y', 'keyboard', 'lockScreen']
|
||||
},
|
||||
panelStyle: 'unlock-screen'
|
||||
},
|
||||
|
||||
'initial-setup': {
|
||||
isPrimary: true,
|
||||
components: ['keyring'],
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume']
|
||||
}
|
||||
},
|
||||
|
||||
'user': {
|
||||
hasOverview: true,
|
||||
showCalendarEvents: true,
|
||||
allowSettings: true,
|
||||
allowExtensions: true,
|
||||
hasRunDialog: true,
|
||||
hasWorkspaces: true,
|
||||
hasWindows: true,
|
||||
hasNotifications: true,
|
||||
isLocked: false,
|
||||
isPrimary: true,
|
||||
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
|
||||
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
|
||||
'keyring', 'recorder', 'autorunManager', 'automountManager'],
|
||||
panel: {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'userMenu']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function _getModes(modesLoadedCallback) {
|
||||
FileUtils.collectFromDatadirsAsync('modes',
|
||||
{ processFile: _loadMode,
|
||||
loadedCallback: modesLoadedCallback,
|
||||
data: _modes });
|
||||
}
|
||||
|
||||
function _loadMode(file, info, loadedData) {
|
||||
let name = info.get_name();
|
||||
let suffix = name.indexOf('.json');
|
||||
let modeName = suffix == -1 ? name : name.slice(name, suffix);
|
||||
|
||||
if (loadedData.hasOwnProperty(modeName))
|
||||
return;
|
||||
|
||||
let fileContent, success, tag, newMode;
|
||||
try {
|
||||
[success, fileContent, tag] = file.load_contents(null);
|
||||
newMode = JSON.parse(fileContent);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadedData[modeName] = {};
|
||||
let propBlacklist = ['unlockDialog'];
|
||||
for (let prop in loadedData[DEFAULT_MODE]) {
|
||||
if (newMode[prop] !== undefined &&
|
||||
propBlacklist.indexOf(prop) == -1)
|
||||
loadedData[modeName][prop]= newMode[prop];
|
||||
}
|
||||
loadedData[modeName]['isPrimary'] = true;
|
||||
}
|
||||
|
||||
function listModes() {
|
||||
let modes = Object.getOwnPropertyNames(_modes);
|
||||
for (let i = 0; i < modes.length; i++)
|
||||
print(modes[i]);
|
||||
_getModes(function(modes) {
|
||||
let names = Object.getOwnPropertyNames(modes);
|
||||
for (let i = 0; i < names.length; i++)
|
||||
if (_modes[names[i]].isPrimary)
|
||||
print(names[i]);
|
||||
Mainloop.quit('listModes');
|
||||
});
|
||||
Mainloop.run('listModes');
|
||||
}
|
||||
|
||||
const SessionMode = new Lang.Class({
|
||||
Name: 'SessionMode',
|
||||
|
||||
_init: function() {
|
||||
let params = _modes[global.session_mode];
|
||||
global.connect('notify::session-mode', Lang.bind(this, this._sync));
|
||||
this._modes = _modes;
|
||||
this._modeStack = [DEFAULT_MODE];
|
||||
this._sync();
|
||||
|
||||
params = Params.parse(params, _modes[DEFAULT_MODE]);
|
||||
|
||||
this._createSession = params.createSession;
|
||||
delete params.createSession;
|
||||
|
||||
Lang.copyProperties(params, this);
|
||||
_getModes(Lang.bind(this, function(modes) {
|
||||
this._modes = modes;
|
||||
let primary = modes[global.session_mode] &&
|
||||
modes[global.session_mode].isPrimary;
|
||||
let mode = primary ? global.session_mode : 'user';
|
||||
this._modeStack = [mode];
|
||||
this._sync();
|
||||
}));
|
||||
},
|
||||
|
||||
createSession: function() {
|
||||
if (this._createSession)
|
||||
this._createSession();
|
||||
pushMode: function(mode) {
|
||||
this._modeStack.push(mode);
|
||||
this._sync();
|
||||
},
|
||||
|
||||
popMode: function(mode) {
|
||||
if (this.currentMode != mode || this._modeStack.length === 1)
|
||||
throw new Error("Invalid SessionMode.popMode");
|
||||
this._modeStack.pop();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
switchMode: function(to) {
|
||||
if (this.currentMode == to)
|
||||
return;
|
||||
this._modeStack[this._modeStack.length - 1] = to;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
get currentMode() {
|
||||
return this._modeStack[this._modeStack.length - 1];
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let params = this._modes[this.currentMode];
|
||||
let defaults;
|
||||
if (params.parentMode)
|
||||
defaults = Params.parse(this._modes[params.parentMode],
|
||||
this._modes[DEFAULT_MODE]);
|
||||
else
|
||||
defaults = this._modes[DEFAULT_MODE];
|
||||
params = Params.parse(params, defaults);
|
||||
|
||||
// A simplified version of Lang.copyProperties, handles
|
||||
// undefined as a special case for "no change / inherit from previous mode"
|
||||
for (let prop in params) {
|
||||
if (params[prop] !== undefined)
|
||||
this[prop] = params[prop];
|
||||
}
|
||||
|
||||
this.emit('updated');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SessionMode.prototype);
|
||||
|