Compare commits
1016 Commits
wip/screen
...
3.6.0
Author | SHA1 | Date | |
---|---|---|---|
8f2a6f8387 | |||
908bf3b117 | |||
6a739afd25 | |||
506490e32d | |||
846771f2a1 | |||
759b7584e0 | |||
e5cb224598 | |||
e32df1b405 | |||
db9f91b687 | |||
0941357068 | |||
d8993c52d0 | |||
6424b2dd03 | |||
2a4eb3ed1a | |||
c9fa00cce1 | |||
18eedbc02d | |||
ef9f63fe59 | |||
f8ce788425 | |||
6c1bd95643 | |||
2ed7ee8f71 | |||
2e63709450 | |||
6611d639a8 | |||
fe124e6ab3 | |||
fee0a8527d | |||
2f6b00403f | |||
dbe2c117e3 | |||
c4b1ccb6d6 | |||
2c70ee7e43 | |||
f7826616b8 | |||
9f476a12dd | |||
6f3cf0ae50 | |||
cd024e21f0 | |||
c4c470c1f3 | |||
dc9ad8df80 | |||
74d6225993 | |||
e1e0c5035d | |||
adf6d0eb82 | |||
90df435345 | |||
83d57211db | |||
0a8eeb2827 | |||
0c324c42f4 | |||
3adf54a952 | |||
8076c66a4c | |||
f9c583a636 | |||
30d536b19c | |||
1957899146 | |||
b52c83d88a | |||
27ff388413 | |||
d4306f7768 | |||
5d0a57c97d | |||
a87ba467ae | |||
7df7cd01eb | |||
098d805a8b | |||
4a21034a00 | |||
7904e359f2 | |||
0c8a94beb8 | |||
c13a573792 | |||
f9b42e12ae | |||
5c2031b768 | |||
49643882d4 | |||
175ddaa3a1 | |||
cc1d6e97b8 | |||
79b96ab301 | |||
44d9316023 | |||
5b0c9a74fb | |||
3429fc3e4c | |||
d11d8d5353 | |||
1742bd6ded | |||
9f6e118ea0 | |||
717bbd3bb5 | |||
e8ebe4de14 | |||
0ff614ccd4 | |||
d0a77b7e0c | |||
2815889090 | |||
1317956663 | |||
a2303d8895 | |||
2161c793dc | |||
a2f943db8f | |||
23a31b9c00 | |||
2591d1b94b | |||
300f97f102 | |||
7e3f6c3066 | |||
601d232064 | |||
8854ac84ad | |||
0302c3fbd4 | |||
6b6fdc6cfe | |||
7126ce86ec | |||
760bf52f75 | |||
6d791d31de | |||
21adc98d70 | |||
6fd7a56568 | |||
fba427fcaf | |||
7753361ffb | |||
c1590d9ed7 | |||
23c1138a58 | |||
2acb097662 | |||
449d116af2 | |||
110240981d | |||
ac5c6de929 | |||
51bdc44352 | |||
bafe34696d | |||
114f6f577f | |||
1f30670c1d | |||
10884ef7f5 | |||
5e12e5f42a | |||
e7a2e1f268 | |||
cc6744055f | |||
e6ba7c6e40 | |||
ea4855e908 | |||
5c7da4b0e6 | |||
9659d934ac | |||
de93677271 | |||
6ef3c628e6 | |||
f2af0be9ac | |||
ef7b74a104 | |||
906368d916 | |||
809cbf58c6 | |||
a5d60050a2 | |||
2c130c8668 | |||
c0ff02d9c7 | |||
cb627db392 | |||
33bd6bc75b | |||
ea5bf109cb | |||
b150a2842e | |||
2a46c39019 | |||
5ca5f026c5 | |||
15f22add79 | |||
131fa6a359 | |||
2d184e1842 | |||
95a55a2c1c | |||
8eecbb5c17 | |||
abfdcaa1b5 | |||
8ee74e5661 | |||
ff31ccdd30 | |||
f6645a41d2 | |||
daceb8105f | |||
4beba796d7 | |||
70c34baafb | |||
e71c016477 | |||
633bbd8a9e | |||
4c82df56e9 | |||
0d62ec5b03 | |||
38f943ef81 | |||
e421953fcd | |||
f39098a4f2 | |||
b203a95a78 | |||
2bb1a6792a | |||
e30d45a2b0 | |||
afcd90ae27 | |||
94c1d5a18c | |||
4254fa3d38 | |||
3b293e91e3 | |||
3d6320295e | |||
7654f1ca3e | |||
e62c66b153 | |||
638507caff | |||
107f5de58e | |||
b1451523ca | |||
f7a95b5edc | |||
be290fafe7 | |||
b7ae74edb9 | |||
b3041ae9fc | |||
7499b04638 | |||
bf2d2071fc | |||
488820daec | |||
77c15b76b5 | |||
e71129aa68 | |||
6f5b700833 | |||
9627864ca8 | |||
2a8625ffae | |||
ed82c3763c | |||
22266899dc | |||
9dfd1bfa41 | |||
409af28cb7 | |||
4fe604bfe8 | |||
49df72ceda | |||
2db029bcdb | |||
7fd128eabc | |||
5e3d8dd3eb | |||
785be2f327 | |||
8cf9baa132 | |||
09e3aed770 | |||
452ac297ab | |||
7ebb5c6a10 | |||
00e0d24a6a | |||
8a269041fb | |||
9f48adcff9 | |||
48e7d732b9 | |||
30048b74d1 | |||
c866b0dbfd | |||
1c79f18b13 | |||
6d704be88b | |||
5af389c087 | |||
475dde4193 | |||
8c534163e1 | |||
657887b241 | |||
10da35cbef | |||
00f15c1075 | |||
19f92df8ab | |||
8639cf8060 | |||
2501d29e9d | |||
8109dd684e | |||
5a259dd6b0 | |||
2ed28211ed | |||
9d0eaa216f | |||
58477282fe | |||
6b016c2528 | |||
b98c5f94ee | |||
ad8bdb929a | |||
5030d59fcc | |||
ec52928736 | |||
7f479e18e6 | |||
7999e9020d | |||
43ba93a817 | |||
b37e02c90a | |||
be5df17a4a | |||
ffbc1fd190 | |||
457fb604dd | |||
09c81f79f6 | |||
0725a7d836 | |||
f957ae71c3 | |||
011c9d1beb | |||
aed589a98d | |||
3830e90642 | |||
3710b88ab9 | |||
5c990f103e | |||
2890fc9d7d | |||
0223507a2e | |||
59412a9405 | |||
110d58bbc3 | |||
eda17defb2 | |||
f563fb124e | |||
14d0a96999 | |||
f0474ffccc | |||
11ce6845f2 | |||
70d610b5e4 | |||
f5ca649977 | |||
8954d33019 | |||
2e6205bc05 | |||
e0dea63079 | |||
7c244b01c6 | |||
3f5edf7c3e | |||
92d8d65543 | |||
cebd8e14e9 | |||
16e92a7ca3 | |||
59e2710137 | |||
f1ca96bbf0 | |||
ec01f5d5ee | |||
2a800e4ce0 | |||
ca2e09fe8b | |||
7e343f11f2 | |||
db20a54861 | |||
ec4f6b7f91 | |||
08a69cdcbd | |||
aa9c095c8e | |||
b257029d3e | |||
cb9062f818 | |||
3a1f623ea3 | |||
47d46e367e | |||
e5534e86ab | |||
e0ca572572 | |||
ca4b86e7ca | |||
f177bd0b51 | |||
11086e8ef8 | |||
1237aef727 | |||
79bfea5970 | |||
50f8ae6fc7 | |||
73ec86797b | |||
703417a760 | |||
3013a87bd2 | |||
833cb2556b | |||
8f4f8fccdb | |||
38e4ab1cea | |||
366a3e2c35 | |||
fcb175ebb3 | |||
a76cc79f88 | |||
1a6d74fcb2 | |||
eb351b1882 | |||
43caace1b6 | |||
167ed7c35b | |||
69735940ec | |||
1a7fad129d | |||
6931e6cf0f | |||
87e021cd2e | |||
d3f5d94afe | |||
a3d8b5e526 | |||
5e46abfa03 | |||
f4740bac65 | |||
dc10e61a20 | |||
82c8aad157 | |||
4ba1f26e4d | |||
f8805e8311 | |||
c815979f2a | |||
54e5ffcac1 | |||
3fd0502cb9 | |||
8aed51180f | |||
e616877fd2 | |||
df15ee4074 | |||
4cf3acd0ec | |||
77ea16e18f | |||
85d993386a | |||
14a7da767b | |||
2f3df71cc1 | |||
dc13e72b89 | |||
2beff9896a | |||
43af6f4792 | |||
04d1a35cb5 | |||
f075b36d4f | |||
c21b1e5fe0 | |||
042c1fd54b | |||
ee6086373b | |||
382310fe51 | |||
d52cb510fc | |||
c1de2788b1 | |||
41dc9e0894 | |||
d3b0d23d8f | |||
f96dcaccbe | |||
2672f205fd | |||
1ba21ed367 | |||
79408d359c | |||
12c76e53cb | |||
ac44426557 | |||
684e5bec15 | |||
70d19530c4 | |||
e34ab6c5ae | |||
b3cb9d8459 | |||
df3fa973ad | |||
1f4895428d | |||
417941a1bf | |||
973a6b21a2 | |||
b50604d070 | |||
a1ecf459b3 | |||
6ea80eadd6 | |||
9dafaa2c0c | |||
50e5736c03 | |||
46fcfb7629 | |||
8a86540090 | |||
785ab8192b | |||
3c386e0c50 | |||
bec48492ab | |||
13a10f8afd | |||
00fe97a55a | |||
9e665e4903 | |||
efc128e681 | |||
6851c5443a | |||
baf08dd688 | |||
67115b3e9b | |||
4ca2697271 | |||
4696bfbb80 | |||
18c62a1987 | |||
d60a4e97d6 | |||
43883a6945 | |||
58ff5c6cc6 | |||
3902e8bff0 | |||
5d6f37017b | |||
ecbf2f1429 | |||
56dc2eb96e | |||
2154a22c90 | |||
3fafe0da07 | |||
983535224e | |||
88effdd9c3 | |||
e9a4843eb0 | |||
c50132b29f | |||
e92a6b3c6c | |||
66a36bf591 | |||
39559f0a09 | |||
712623946f | |||
47ce72e011 | |||
7f8c5c4f64 | |||
64fdff6ace | |||
fa2b413095 | |||
46b7a95ec8 | |||
55922c0568 | |||
1c927868d8 | |||
0a1f0e58d0 | |||
9024c5d7ac | |||
370de395f7 | |||
8ebd2ff9eb | |||
83a1c7283d | |||
695f84e14f | |||
54f3036f86 | |||
446583400e | |||
3776c50986 | |||
4ff0697ee7 | |||
cd43c4983b | |||
0f065ebce6 | |||
a2e0e0ad06 | |||
d721fc2c17 | |||
becaeafeac | |||
99c97707ac | |||
75ed427e04 | |||
d1e35d11d0 | |||
ee0102e86e | |||
776b71bad5 | |||
37da87d9e1 | |||
ea14d74141 | |||
4b60a8deb8 | |||
7cca0a9cd8 | |||
f347aba01b | |||
8c40ded498 | |||
c267a7a7f9 | |||
0959adcfe3 | |||
7d12f47bf8 | |||
dff673533d | |||
816a29d582 | |||
27fbe4cd83 | |||
2ee6f49a93 | |||
0ea2d98768 | |||
b5dc78a968 | |||
ebe7890a49 | |||
ef4231b9c0 | |||
7e2bab48c9 | |||
eebab34b62 | |||
05d613f3a3 | |||
5c6b1fd0cc | |||
f8eab659d9 | |||
4a214d8238 | |||
44b4741e35 | |||
95b9863a70 | |||
4dfe3d21e1 | |||
3568e6d42d | |||
c3ae4542b2 | |||
3031036cb0 | |||
11dc510ae4 | |||
79c25d1dc8 | |||
1fd17b5027 | |||
7a6b2abfeb | |||
fe8e62c6a8 | |||
e937fd07e5 | |||
c668d16a00 | |||
c33e2d1971 | |||
0711a70398 | |||
cb5095370e | |||
8f71920622 | |||
3da0e9e86a | |||
e8c8fecb35 | |||
8bb2c425c6 | |||
1f6d807882 | |||
8d1ed9f88a | |||
d766f865f3 | |||
c540cb5e16 | |||
b36029f6c4 | |||
a8e6bfcb3d | |||
9c7222daea | |||
ab5c0bcfcf | |||
1a2dcf213e | |||
6f8e7f07f3 | |||
0ac4787f1a | |||
16bbe9782b | |||
5594c119f5 | |||
a909017a2b | |||
9a7914eee9 | |||
98c461dcd8 | |||
5991c8dca3 | |||
b9226fbcbd | |||
b6375d3e40 | |||
8dc9ceb2bc | |||
6cb713b0e3 | |||
dafa27fccd | |||
190f2f82ac | |||
43767ab2b9 | |||
3042e647ff | |||
a004ad6088 | |||
addb247c33 | |||
8f129e1c9d | |||
341b6a1290 | |||
6a9b1996e4 | |||
a5f2d289bd | |||
e3a8228065 | |||
fd04c59e63 | |||
f8b1a84b81 | |||
b53d18fe1c | |||
d0a94768a6 | |||
80fde70995 | |||
cbb56b6128 | |||
cbd2188c04 | |||
2b30afa618 | |||
1a65374e21 | |||
3e94f6bc3c | |||
1dbbb4f91c | |||
b3b847fadd | |||
ff25a5e251 | |||
aaf61cbbbe | |||
ca7c4edd41 | |||
028e83181b | |||
31c3783c7c | |||
f63371dadb | |||
9f07a3dd4a | |||
06262a903e | |||
e01e79d9fb | |||
2c627cad10 | |||
b33e51be45 | |||
b17f5b678d | |||
c6a6ae594a | |||
9f02129af3 | |||
6037350380 | |||
5de38cd9f6 | |||
7950c4afdd | |||
e304854fa5 | |||
d7f54213f1 | |||
12a71be076 | |||
855f0dbcad | |||
03db0d630e | |||
5a35c57866 | |||
d11027e8c8 | |||
aa733b5e53 | |||
355ec9ceca | |||
aa30c6a323 | |||
ad5572f275 | |||
9082b4df48 | |||
c3179583c3 | |||
4db87839a6 | |||
b018d49dc3 | |||
83362226cf | |||
1e3796967e | |||
8d7fa54af4 | |||
59cd9b4220 | |||
59dfb5866d | |||
a29ceaa8ec | |||
782e11b96c | |||
fd3be5b7de | |||
0b9a5c3441 | |||
7431c4d611 | |||
ec78dd60fc | |||
b479eec758 | |||
413ace2eb1 | |||
d8390ef77f | |||
e7e56e175a | |||
e875b9cdca | |||
66197b18b6 | |||
ed991f79b1 | |||
48b70f358d | |||
56adbda2dc | |||
7dbc78c95f | |||
3ffa1e35e8 | |||
114fb4219b | |||
4d7d66bc1f | |||
e134d2e5a5 | |||
cca84761a5 | |||
c2a5d6f61b | |||
4449007bb4 | |||
30cd1e84bc | |||
dcf872b485 | |||
c9d51ca775 | |||
96c9f8058b | |||
a3f4bca14e | |||
e55f2dd3b9 | |||
247566ca1d | |||
aa120e0902 | |||
5c7992beef | |||
6a615f30dc | |||
bfea41b771 | |||
a2465e0670 | |||
57044ea9a0 | |||
1392be2952 | |||
9e503aa69c | |||
a004389f25 | |||
d474261912 | |||
144e959cab | |||
626488e659 | |||
5bae5574f0 | |||
ce14d6d9db | |||
519addf6ac | |||
fd87468e36 | |||
75e49610cb | |||
414fe75d02 | |||
26031eb761 | |||
be801dff5f | |||
5fd47ed742 | |||
f7f2f50435 | |||
e0bb15e572 | |||
0e4171a87c | |||
34cb92ff4c | |||
22eea750f3 | |||
c3afe1a83a | |||
904ceba6b2 | |||
a28d639c3b | |||
c0652bcd8f | |||
54dc0fd123 | |||
c22a00afee | |||
2c3ec7846f | |||
46db9edacc | |||
4e6fa56c87 | |||
8970e17911 | |||
ac6c808124 | |||
26d3b1929e | |||
a29507e452 | |||
2c073fb005 | |||
d9c3b83d1e | |||
f5e58c500f | |||
5e865f5bc4 | |||
01a1255967 | |||
dd80f39049 | |||
19e4c953ef | |||
ef218c95fc | |||
eec9dba855 | |||
f46a165886 | |||
ec47bd1604 | |||
bfd4016c1c | |||
63cdb1848e | |||
10a0762fe7 | |||
76472b86ed | |||
edb96cde70 | |||
e06ecb8f0c | |||
2791d948e9 | |||
360e6e790a | |||
d0807c8276 | |||
75d0362cd8 | |||
4f7c554d8d | |||
8b81f23caf | |||
0098c2b7f7 | |||
f0e03b5e82 | |||
be2f1001a5 | |||
cf08b4d56a | |||
04074f883f | |||
14d3235f1a | |||
e00c1cbd20 | |||
3df3f0d9dc | |||
985db40547 | |||
c9fa0fdff0 | |||
fe69ea305b | |||
ff9088e42b | |||
f556cdf0ca | |||
c671ff74c6 | |||
04570ac783 | |||
6ab25cd791 | |||
a04350f7ce | |||
b7018de7e0 | |||
d212d57466 | |||
c4e7d8ed8c | |||
464813ecbb | |||
9812771dcd | |||
6f605598de | |||
85bc8ccccc | |||
e756c2dbce | |||
e82fe14f00 | |||
de65739c01 | |||
e1ec89a133 | |||
bdb3410d9d | |||
168e0b5a42 | |||
f906cfe5f6 | |||
9faac81a37 | |||
b90e7eb95c | |||
1e286e43ad | |||
539993b4f4 | |||
1363d30f79 | |||
3a48daaa64 | |||
d2b4a65e65 | |||
1ead290c23 | |||
6658660355 | |||
85ab019987 | |||
460cda2aa1 | |||
2d913578e1 | |||
34831796f6 | |||
8754b2767c | |||
04fb688f7d | |||
58dbd285fc | |||
cf6f149888 | |||
8d017ceaf1 | |||
02428019fa | |||
b4464929cb | |||
3ea22f8b0e | |||
9745e97e14 | |||
6c6e182ecc | |||
7973dd45b7 | |||
a1837dde68 | |||
4448b65a18 | |||
2231c23c4d | |||
f17fc43d6e | |||
f9dbe56785 | |||
8845a2170c | |||
a4b1ebd8c3 | |||
66adeef9bd | |||
20769f68a7 | |||
e92719b98d | |||
59246babea | |||
23e86d7dd5 | |||
c99e8eb29d | |||
7949397958 | |||
1d1359b58f | |||
8915bb4892 | |||
6a117ac12f | |||
67689f1a6d | |||
48b83f1ffd | |||
7da0f398a5 | |||
7e277fdd4a | |||
5d10d8566b | |||
a10295f584 | |||
46cf9faa11 | |||
971341bb53 | |||
2b2a235a49 | |||
970b9deeaa | |||
fd256b624c | |||
698fb64be9 | |||
9561f77b17 | |||
c3d3d346d4 | |||
e43fe98263 | |||
04dbf15d9b | |||
de72065a4a | |||
a1bb0ec738 | |||
1a33cd9584 | |||
00279dbd04 | |||
eb759cf22f | |||
ae16da4e81 | |||
965287e724 | |||
3d5312e8d2 | |||
bc91b7dcae | |||
4d77eb94ff | |||
1b8d03f945 | |||
de2dcfeb99 | |||
c7196a519f | |||
0d82ce5210 | |||
3ce9ad05b3 | |||
ab75faac74 | |||
9e25e13218 | |||
69e1503c6d | |||
7eaf231e56 | |||
a347a72091 | |||
560daec913 | |||
d9e968d863 | |||
556d5d181e | |||
4e4092f9e8 | |||
fd62aba71c | |||
ef0aa65774 | |||
6b5f9a647a | |||
01c07fbea1 | |||
81929c2a92 | |||
d9ff8e3122 | |||
0c4692ae58 | |||
df56ff4f09 | |||
96cdc9c4eb | |||
3b4ad5cd7d | |||
317c6b77f3 | |||
e5f5a2adaa | |||
594c3174ab | |||
5b7e4bb4a7 | |||
f7c0f826d4 | |||
66af7de6d6 | |||
d1815a36d0 | |||
61de3de909 | |||
36888a34d6 | |||
646435ee3e | |||
cbb8d5b0dc | |||
ebee01b355 | |||
0e3795b2f3 | |||
de73128d47 | |||
281b0a3e63 | |||
c414f9ac9d | |||
77242cfec0 | |||
8ebbba6eb8 | |||
0804cefbee | |||
2404d2935d | |||
e6f5e21b5d | |||
c303c6b5c1 | |||
f58e8f2a35 | |||
ededba0c6d | |||
e112fa92fe | |||
7524210d1f | |||
201dc05416 | |||
2b34978993 | |||
447246da74 | |||
1cf2bb6646 | |||
de1eafb564 | |||
22f0099a5d | |||
300eb87016 | |||
aa38b16368 | |||
266bfdf739 | |||
19ef6b0421 | |||
75570b7995 | |||
3290bfae68 | |||
0805d7a35f | |||
11278a0814 | |||
86de6f5861 | |||
498b023989 | |||
5265884af9 | |||
cdbe0bbf38 | |||
feef35a8ca | |||
3ff51da529 | |||
496e9f7b16 | |||
0b30dc29a5 | |||
15b4d29e70 | |||
9eee4b7687 | |||
196f6c241a | |||
c9296191a8 | |||
c7b4022283 | |||
a7ecc4cdd6 | |||
df5298d59c | |||
b79d17332e | |||
675572a41a | |||
0078fe9349 | |||
dc004f6eb7 | |||
07f1a05ab4 | |||
54292a99af | |||
f5933c8cb8 | |||
6700f86145 | |||
b31d22488e | |||
6382eeb8fd | |||
b07345a55c | |||
06febdce22 | |||
c31d9d5e3d | |||
cda8a545f1 | |||
f850e92524 | |||
cc9d53e038 | |||
7b048fc092 | |||
4ba4a501fd | |||
2b2cce6896 | |||
c1f51a7bf3 | |||
465556f0d8 | |||
db7ac5208a | |||
cfcd1bc014 | |||
e63f7e8779 | |||
7911154bad | |||
ec0730f3e5 | |||
76005f5adf | |||
94493cde35 | |||
a1f68720e5 | |||
022376dd56 | |||
934e5aacab | |||
35b142f23f | |||
41c3795a7b | |||
95d7099133 | |||
33dc9abb9b | |||
5e4edac14d | |||
cb5ae92986 | |||
ebbd295ebe | |||
65d23fb9a3 | |||
ad6d986172 | |||
985641cc2e | |||
de0a714081 | |||
3f942302d1 | |||
96396163cf | |||
0c736c4561 | |||
33ad9d1035 | |||
d57658d059 | |||
031206cf1f | |||
12c7cc278d | |||
9d3750b9b8 | |||
ba4b9f229e | |||
850fe98cbb | |||
8a9e3e0df2 | |||
a277569d31 | |||
7ed9516884 | |||
ecff2fa2b7 | |||
e49b94658c | |||
e4f1572a3a | |||
b5b13322d8 | |||
c25e7f3c41 | |||
f6a2c92bfa | |||
ed17418101 | |||
5264f39209 | |||
de69c719fb | |||
ab3173487d | |||
a3fcb8c284 | |||
ba92cfa064 | |||
6bee51ed33 | |||
122bca49ea | |||
19318a1eeb | |||
a7a46bbe1c | |||
3d26224180 | |||
940ddb104c | |||
5617f91281 | |||
a9a863aab4 | |||
ca26347dea | |||
41a14e808e | |||
6452501275 | |||
b61ada72cc | |||
ace42d845c | |||
850b6f28e5 | |||
3c81e9f0e7 | |||
e20ea19f34 | |||
ce041a3190 | |||
3a01aaf7fb | |||
ec4a2aae95 | |||
de8a66d4ce | |||
dc3d3acb3b | |||
48d6eb168f | |||
6190d6c962 | |||
76616dc98c | |||
82c2f5221d | |||
a3bbb7be14 | |||
9e1a2cfeac | |||
c6fabe504a | |||
61a17d7fab | |||
865cfa5211 | |||
e2b857adae | |||
f3924ccd91 | |||
86e3e59530 | |||
02e4726ba6 | |||
66e470e073 | |||
afaa5c24d6 | |||
518282e169 | |||
d955adbbad | |||
83d3225e57 | |||
6fa45975bf | |||
e6087efb40 | |||
4ce2f80a2f | |||
0862d1c804 | |||
333e380340 | |||
58f77a19ed | |||
f2d883dab2 | |||
7ba8c7c2b5 | |||
0bf6c93faa | |||
b9f0158278 | |||
25ee41f344 | |||
5436634829 | |||
915524e1ab | |||
d579cd1605 | |||
cb5941ec55 | |||
a5ac183d86 | |||
a36de92bb9 | |||
01f9d551f1 | |||
399df66b18 | |||
723a1c843a | |||
e333263fd6 | |||
507df9eea1 | |||
a9a3687ea0 | |||
f05c649c61 | |||
a2dfba1842 | |||
c199da4dfa | |||
ee9033e12f | |||
54788d750e | |||
78e894c6f2 | |||
32107ba8b5 | |||
6122f65e7a | |||
43fd29f9bf | |||
54c624b356 | |||
8c33adfd29 | |||
2e8881b77c | |||
64aa729edd | |||
ccf95b738d | |||
b2847fedd3 | |||
8befcb9bba | |||
988fc52303 | |||
7293ddb22c | |||
f23c118e81 | |||
f68b3be35a | |||
3d95e7bb11 | |||
337c484f01 | |||
5d98e2bf04 | |||
6f300d0cc6 | |||
3422e1dca7 | |||
963c6ae567 | |||
6304169926 | |||
221afde55e | |||
0ae87270ad | |||
9d33baec70 | |||
0f37b22cdb | |||
47afd87e84 | |||
700c06023e | |||
4fea5b5ca3 | |||
521bddc1cc | |||
bdfff20ec2 | |||
bdd05aba3b | |||
89c2538ff1 | |||
7680819108 | |||
d79e8b84c9 | |||
731317230a | |||
5046938913 | |||
0e8fd45559 | |||
6099a5dbc3 | |||
2daa98a694 | |||
5d2c6496fa | |||
817dbbe73f | |||
29c89c82f8 | |||
0b714bd479 | |||
8c94a5afb9 | |||
aeb117c9d1 | |||
a2d4f133b7 | |||
b833aff3c8 | |||
6601e4ddba | |||
815da2d0ec | |||
ddd35b3653 | |||
8a32894c83 | |||
49d8e6da40 | |||
8089f24c81 | |||
b6aab53d10 | |||
55a4517cd1 | |||
b095319a16 | |||
5ea5806730 | |||
bfbf812148 | |||
168e9eeac1 | |||
dd79c1a79a | |||
fe3402589b | |||
74dcaff21c | |||
0a7968a2e5 | |||
00091a2acb | |||
c5aa834b6a | |||
b1bde46694 | |||
49e4fa494e | |||
4622c52b71 | |||
e9ac5dd5f4 | |||
7570c43d11 | |||
4332e7ec49 | |||
9f26f1e225 | |||
0c0319c415 | |||
c16dbd7607 | |||
7b1f10a5fe | |||
dce74749b7 | |||
1ba88b8c42 | |||
9c50b57d46 | |||
900bd3ee97 | |||
b4affe00a7 | |||
ca49c84bc1 | |||
2cddf60226 | |||
d9c3951f02 | |||
aa5997d975 | |||
c51acf7c2a | |||
348044bc8a | |||
7dbdf2aa07 | |||
c933731ead | |||
e7da715994 | |||
6d82aefad4 | |||
8a11ab7d96 | |||
f465086405 | |||
70313a8b79 | |||
71679c38be | |||
addd943074 |
7
.gitignore
vendored
@ -16,6 +16,7 @@ config.log
|
|||||||
config.status
|
config.status
|
||||||
config
|
config
|
||||||
configure
|
configure
|
||||||
|
data/50-gnome-shell-*.xml
|
||||||
data/gnome-shell.desktop
|
data/gnome-shell.desktop
|
||||||
data/gnome-shell.desktop.in
|
data/gnome-shell.desktop.in
|
||||||
data/gnome-shell-extension-prefs.desktop
|
data/gnome-shell-extension-prefs.desktop
|
||||||
@ -23,6 +24,8 @@ data/gnome-shell-extension-prefs.desktop.in
|
|||||||
data/gschemas.compiled
|
data/gschemas.compiled
|
||||||
data/org.gnome.shell.gschema.xml
|
data/org.gnome.shell.gschema.xml
|
||||||
data/org.gnome.shell.gschema.valid
|
data/org.gnome.shell.gschema.valid
|
||||||
|
data/org.gnome.shell.evolution.calendar.gschema.xml
|
||||||
|
data/org.gnome.shell.evolution.calendar.gschema.valid
|
||||||
docs/reference/*/*.args
|
docs/reference/*/*.args
|
||||||
docs/reference/*/*.bak
|
docs/reference/*/*.bak
|
||||||
docs/reference/*/*.hierarchy
|
docs/reference/*/*.hierarchy
|
||||||
@ -48,6 +51,7 @@ po/gnome-shell.pot
|
|||||||
po/*.header
|
po/*.header
|
||||||
po/*.sed
|
po/*.sed
|
||||||
po/*.sin
|
po/*.sin
|
||||||
|
po/.intltool-merge-cache
|
||||||
po/Makefile.in.in
|
po/Makefile.in.in
|
||||||
po/Makevars.template
|
po/Makevars.template
|
||||||
po/POTFILES
|
po/POTFILES
|
||||||
@ -60,6 +64,8 @@ src/*-enum-types.[ch]
|
|||||||
src/*-marshal.[ch]
|
src/*-marshal.[ch]
|
||||||
src/Makefile
|
src/Makefile
|
||||||
src/Makefile.in
|
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/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||||
src/gnome-shell
|
src/gnome-shell
|
||||||
src/gnome-shell-calendar-server
|
src/gnome-shell-calendar-server
|
||||||
@ -68,6 +74,7 @@ src/gnome-shell-extension-prefs
|
|||||||
src/gnome-shell-hotplug-sniffer
|
src/gnome-shell-hotplug-sniffer
|
||||||
src/gnome-shell-jhbuild
|
src/gnome-shell-jhbuild
|
||||||
src/gnome-shell-perf-helper
|
src/gnome-shell-perf-helper
|
||||||
|
src/gnome-shell-perf-tool
|
||||||
src/gnome-shell-real
|
src/gnome-shell-real
|
||||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||||
src/run-js-test
|
src/run-js-test
|
||||||
|
464
NEWS
@ -1,3 +1,455 @@
|
|||||||
|
3.6.0
|
||||||
|
=====
|
||||||
|
* keyboard: Make input source items accessible [Florian; #684462]
|
||||||
|
* Don't show network dialogs in the lock screen [Giovanni; #684384]
|
||||||
|
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
|
||||||
|
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
|
||||||
|
* Fix alignment of search section headers in RTL locales [Florian; #684379]
|
||||||
|
* screenShield: Fix unlock animation [Florian; #684591]
|
||||||
|
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
|
||||||
|
* userMenu: Fix texture updates on icon changes [Florian; #679268]
|
||||||
|
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
|
||||||
|
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
|
||||||
|
#683546]
|
||||||
|
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
|
||||||
|
Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
|
||||||
|
Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
|
||||||
|
Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
|
||||||
|
Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
|
||||||
|
Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
|
||||||
|
Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
|
||||||
|
Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
|
||||||
|
Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
|
||||||
|
YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
|
||||||
|
Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
|
||||||
|
Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
|
||||||
|
Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
|
||||||
|
|
||||||
|
3.5.92
|
||||||
|
======
|
||||||
|
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
|
||||||
|
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
|
||||||
|
* Add zoom out animation on login [Jasper; #683170]
|
||||||
|
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
|
||||||
|
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
|
||||||
|
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
|
||||||
|
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
|
||||||
|
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
|
||||||
|
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
|
||||||
|
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
|
||||||
|
* Change stage background color to grey [Adel; #683514]
|
||||||
|
* messageTray: Update style of summary counters [Debarshi; #682891]
|
||||||
|
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
|
||||||
|
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
|
||||||
|
* Hide the a11y menu in the lock screen, but show it in the login screen
|
||||||
|
[Giovanni; #682542]
|
||||||
|
* Fix show-apps button dropping off the dash [Florian; #683340]
|
||||||
|
* Fix committing strings to shell entries from input method [Florian; #658325]
|
||||||
|
* Make IBus display strings consistent with control-center [Rui; #683124]
|
||||||
|
* Fix missing short codes for some input sources [Rui; #683613]
|
||||||
|
* Remove support for long-press from entry context menus [Jasper; #683509]
|
||||||
|
* screenShield: Add box-shadow to the shield [Florian]
|
||||||
|
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
|
||||||
|
* Fix extension styling [Giovanni; #682128]
|
||||||
|
* Fix on-screen keyboard not working with system-modal dialogs
|
||||||
|
[Giovanni; #664309]
|
||||||
|
* Fix insensitive styling for popup menu items [Giovanni; #683988]
|
||||||
|
* Disable the message tray dwell when the user is interacting [Owen; #683811]
|
||||||
|
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
|
||||||
|
* Autostart fprintd when necessary [Ray; #683131]
|
||||||
|
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
|
||||||
|
* Make Return key dismiss screenshield [Ray; #683889]
|
||||||
|
* Fix keyboard navigation in the message tray [Florian; #682243]
|
||||||
|
* Remove the places & devices search provider [Giovanni; #683506]
|
||||||
|
* Enable hot corner while the message tray is up [Florian; #682255]
|
||||||
|
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
|
||||||
|
* Fix fish flickering [Giovanni; #684154]
|
||||||
|
* Fix extension ordering with !important [Jasper; #684163]
|
||||||
|
* Allow the shell to run without the screenshield [Giovanni; #683060]
|
||||||
|
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
|
||||||
|
* Improve transition to the login dialog [Jasper; #682428]
|
||||||
|
* Keep unlock dialog around until shield animation ends [Florian; #684342]
|
||||||
|
* Expose shell keybindings in System Settings [Florian; #671010]
|
||||||
|
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
|
||||||
|
#672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
|
||||||
|
#683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
|
||||||
|
#683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
|
||||||
|
#684162, #684214, #684343]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
|
||||||
|
Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
|
||||||
|
Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||||
|
Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
|
||||||
|
Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
|
||||||
|
Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
|
||||||
|
Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
|
||||||
|
Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
|
||||||
|
Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
|
||||||
|
Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
|
||||||
|
Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
|
||||||
|
Fran Diéguez [gl], Praveen Illa [te]
|
||||||
|
|
||||||
|
3.5.91
|
||||||
|
======
|
||||||
|
* Improve modal dialog styling of network secret prompts [Jasper; #682412]
|
||||||
|
* Fix visibility of non-active workspaces during overview transition
|
||||||
|
[Florian; #682002]
|
||||||
|
* Improve scrollbar theming [Cosimo; #682476]
|
||||||
|
* Make sure the app menu remains hidden in locked state [Florian; #682475]
|
||||||
|
* Add tooltip to show-applications icon [Jasper; #682445]
|
||||||
|
* Do not add duplicate remote search providers [Florian; #682470]
|
||||||
|
* Handle 'popup-menu' signal on summary items [Florian; #682486]
|
||||||
|
* Fix dwelling during mouse-down [Owen; #682385]
|
||||||
|
* Set label actor for endSessionDialog.ListItem [Alejandro; #677503]
|
||||||
|
* Don't match on comments when searching applications [Florian; #682529]
|
||||||
|
* Make workspace selector more similar to the mockup [Stefano; #662087]
|
||||||
|
* Fix extension installation and reloading [Jasper; #682578]
|
||||||
|
* Hide removable devices in the lock screen [Giovanni; #681143]
|
||||||
|
* Reset cancellable after hitting Escape on login screen [Alban; #681537]
|
||||||
|
* Fix suspend from the user menu [Giovanni; #682746]
|
||||||
|
* Set label actor for summary items in message tray [Alejandro; #677229]
|
||||||
|
* Set label for the "Show applications" dash button [Alejandro; #682366]
|
||||||
|
* Load extensions as late as possible [Jasper; #682822]
|
||||||
|
* Improve mount operation dialogs [Jon; #682645]
|
||||||
|
* Remove "Connect to ..." item from places search [Florian; #682817]
|
||||||
|
* Don't auto-expand notifications with actions [Giovanni; #682738]
|
||||||
|
* Add a new lock screen menu to combine volume network and power
|
||||||
|
[Giovanni; #682540]
|
||||||
|
* Add support for pre-edit to StIMText [Daiki; #664041]
|
||||||
|
* Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540]
|
||||||
|
* Use monitor geometry for dwelling [Florian; #683044]
|
||||||
|
* Add support for surrounding-text to StIMText [Daiki; #683015]
|
||||||
|
* Improve the placement and style of the "No results" text [Jasper; #683135]
|
||||||
|
* Remove broken network device activation policy [Giovanni; #683136]
|
||||||
|
* Hide power status icon when no battery is present [Tim; #683080]
|
||||||
|
* Ensure summary items are square and have spacing [Debarshi; #682248]
|
||||||
|
* Fix close buttons overlapping screen edge [Debarshi; #682343]
|
||||||
|
* Escape the tray when a legacy icon is clicked [Giovanni; #682244]
|
||||||
|
* Update arrow in the screen shield to match latest mockups [Giovanni; #682285]
|
||||||
|
* Allow lifting the screen shield with the mouse wheel [Giovanni; #683164]
|
||||||
|
* Make sure to show the app menu after unlocking the screen [Jasper; #683154]
|
||||||
|
* Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui;
|
||||||
|
#582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546,
|
||||||
|
#682683, #682710, #682998, #683073, #683137, #683156]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
|
||||||
|
Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner,
|
||||||
|
Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb],
|
||||||
|
Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru],
|
||||||
|
Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el],
|
||||||
|
Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it]
|
||||||
|
|
||||||
|
3.5.90
|
||||||
|
======
|
||||||
|
* Use symbolic icons for workspace switch OSD [Jon; #680738]
|
||||||
|
* Lock screen improvements:
|
||||||
|
- Hide user menu and a11y menu in the screen lock [Giovanni; #681143]
|
||||||
|
- Bump the lock screen slightly when pressing a key [Giovanni; #681143]
|
||||||
|
- Constrain vertical movement of the screen shield [Giovanni; #681143]
|
||||||
|
- Return to lock screen on idle [Giovanni; #682041]
|
||||||
|
- Unlock screen automatically after fast-user switching [Giovanni; #682096]
|
||||||
|
- Fix "other user" label [Ray; #681750]
|
||||||
|
* Constrain content of system modals to primary monitor [#681743]
|
||||||
|
* Respect automatic lock setting on suspend/user-switch [Giovanni; #680231]
|
||||||
|
* Improve styling of keyring prompt [Jasper; #681821]
|
||||||
|
* Do not hard-code <super> as overlay-key [Florian; #665547]
|
||||||
|
* Update style of attached modal dialogs [Florian; #681601]
|
||||||
|
* a11y: allow navigation on non reactive items [Alejandro; #667439, #667439]
|
||||||
|
* Implement mode-less overview design [Joost, Florian; #682109]
|
||||||
|
* Implement message-tray redesign:
|
||||||
|
- Restyle the message tray [Ana, Allan, Florian; #677213, #682342]
|
||||||
|
- Move the desktop upwards when showing the tray [Debarshi; #681392]
|
||||||
|
- Add a close button to notifications [Ana, Jasper; #682253]
|
||||||
|
- Add a keybinding to toggle the tray [Debarshi; #681392]
|
||||||
|
- Make the tray keyboard navigable [Debarshi; #681519]
|
||||||
|
- Add dwelling at the bottom of the screen to open the tray [Owen; #682310]
|
||||||
|
- Don't time out banners when the user is inactive [Marina, Jasper]
|
||||||
|
- Misc fixes and cleanups [Jasper, Marina]
|
||||||
|
* Fix showing "Next Week" on Sundays [Sebastian; #682198]
|
||||||
|
* Delay restoring IM presence until the network comes up [Florian; #677982]
|
||||||
|
* Display enterprise login hint [Ray; #681975]
|
||||||
|
* Ignore unrecognized/irrelevant network devices/connections [Dan; #682364]
|
||||||
|
* Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico;
|
||||||
|
#643687, #682045, #682189]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann,
|
||||||
|
Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro,
|
||||||
|
Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor,
|
||||||
|
Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he],
|
||||||
|
Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa],
|
||||||
|
Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl],
|
||||||
|
Мирослав Николић [sr, sr@latin]
|
||||||
|
|
||||||
|
3.5.5
|
||||||
|
=====
|
||||||
|
* Update style to match mockups [Allan]
|
||||||
|
- improve calendar layout and legibility
|
||||||
|
- update notifications and menus
|
||||||
|
- use a common style for entries
|
||||||
|
- update scrollbars to match GTK+
|
||||||
|
- improve clock/unlock button in lock screen
|
||||||
|
- update polkit dialogs [Jasper]
|
||||||
|
* Fix login dialog growing when selecting different users [Florian; #675076]
|
||||||
|
* Implement screen lock in the shell [Giovanni]
|
||||||
|
- restructure login code to be shared with session unlock [#619955]
|
||||||
|
- add initial screen shield / unlock dialog implementation [#619955]
|
||||||
|
- implement (optional) notification list on lock shield [#619955]
|
||||||
|
- update login dialog style to match lock screen [#619955]
|
||||||
|
- filter notifications to only show new ones on the screen lock [#681143]
|
||||||
|
- make notifications scrollable if necessary [#681143]
|
||||||
|
- use correct application names in notifications [#681143]
|
||||||
|
- allow to return to the shield by pressing Escape [#681143]
|
||||||
|
* Minor login dialog improvements [Florian]
|
||||||
|
- update style to match the overall visuals [#660913]
|
||||||
|
- indicate whether users are logged in [#658185]
|
||||||
|
* Add support for background-repeat CSS property [Jasper; #680801]
|
||||||
|
* Add :active pseudo class on scroll handles [Florian]
|
||||||
|
* Remove markup from translated strings [Matthias; #681270]
|
||||||
|
* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
|
||||||
|
Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
|
||||||
|
#681101, #681382]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||||
|
Piotr Drąg , Florian Müllner, Jasper St. Pierre
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
|
||||||
|
Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
|
||||||
|
Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
|
||||||
|
Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
|
||||||
|
Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
|
||||||
|
|
||||||
|
3.5.4
|
||||||
|
=====
|
||||||
|
* Fix wrong result handling of remote calls [Florian; #678852]
|
||||||
|
* dateMenu: Fix regression that caused no date to be displayed [Colin]
|
||||||
|
* WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992]
|
||||||
|
* Show the workspace switcher for move-to-workspace keybinding
|
||||||
|
[Giovanni, Jasper; #674104, #660839, #679005]
|
||||||
|
* userMenu: Move "Power off" item to the bottom [Florian; #678887]
|
||||||
|
* Remove contacts search provider [Florian, Rui; #677442]
|
||||||
|
* network: don't ask for always-ask secrets when interaction isn't allowed
|
||||||
|
[Dan; #679091]
|
||||||
|
* PolkitAgent: Look for the right password prompt [Matthias; #675300]
|
||||||
|
* Implement extension updates [Jasper; #679099]
|
||||||
|
* userMenu: Don't disconnect account signals when disabled [Guillaume; #669112]
|
||||||
|
* Fix startup notification when opening calendar [Florian; #677907]
|
||||||
|
* networkAgent: use absolute path if configured [Clemens; #679212]
|
||||||
|
* recorder: Port to GStreamer-1.0 API [Florian; #679445]
|
||||||
|
* telepathyClient: don't add log messages on presence changes [Ana; #669508]
|
||||||
|
* lookingGlass: Don't use a signal callback on 'paint' to draw the border
|
||||||
|
[Jasper; #679464]
|
||||||
|
* Add support for inhibiting automount [Hans; #678597]
|
||||||
|
* Implemented banner support for the login screen [Matthias, Marius; #665346]
|
||||||
|
* boxpointer: Flip side if we would end outside the monitor [Rui; #678164]
|
||||||
|
* boxpointer: Change 'animate' parameter on show/hide to a bitmask
|
||||||
|
[Rui; #678337]
|
||||||
|
* Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499]
|
||||||
|
* UserMenu: show "Install Updates & Restart" when appropriate
|
||||||
|
[Giovanni; #677394, #680080]
|
||||||
|
* messageTray: don't show the message tray when a new notification is shown
|
||||||
|
[Ana; #677210]
|
||||||
|
* panel: don't break when indicator has no menu [Jean-Philippe; #678694]
|
||||||
|
* appMenu: Disable app menu during startup animations [Florian; #672322]
|
||||||
|
* autorun: Add a notification when unmounting drives [Cosimo; #676125]
|
||||||
|
* st-icon: Fix potential crash involving shadows [Jasper; #679776]
|
||||||
|
* Remove manual garbage collection on tweeners end [Cosimo; #679832]
|
||||||
|
* dash: hide tooltips when overview begins hiding [Stefano; #674241]
|
||||||
|
* Update modal dialog animation for new centered position [Florian; #674499]
|
||||||
|
* calendar: Fix grid lines in RTL locales [Florian; #679879]
|
||||||
|
* Integrate IBus with keyboard indicator [Rui; #641531]
|
||||||
|
* Move ibus status icon under keyboard [Matthias]
|
||||||
|
* gdm: port from libgdmgreeter to libgdm [Ray; #676401]
|
||||||
|
* Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico;
|
||||||
|
#678978, #672790, #679847, #679944]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi,
|
||||||
|
Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini,
|
||||||
|
Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska,
|
||||||
|
Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg],
|
||||||
|
Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he],
|
||||||
|
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be],
|
||||||
|
Praveen Illa [te]
|
||||||
|
|
||||||
|
3.5.3
|
||||||
|
=====
|
||||||
|
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
|
||||||
|
* messageTray: Don't show non urgent notifications while in fullscreen
|
||||||
|
[Adel; #677590]
|
||||||
|
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
|
||||||
|
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
|
||||||
|
* appDisplay: Don't show apps in NoDisplay categories in the All view
|
||||||
|
[Jasper; #658176]
|
||||||
|
* st: Trigger theme updates on resolution changes [Florian; #677975]
|
||||||
|
* Always enable a11y [Bastien; #678095]
|
||||||
|
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
|
||||||
|
* shell-app: Update app menu if necessary [Florian; #676238]
|
||||||
|
* Enable the Screen Reader menu item [Matthias; #663256]
|
||||||
|
* Disable unredirection when a modal operation is active [Giovanni]
|
||||||
|
* Make folks optional [Colin]
|
||||||
|
* Improve mount-operation support [Cosimo]
|
||||||
|
- Fix exception when showing password entry [#678428]
|
||||||
|
- Close the password entry on operation abort [#673787]
|
||||||
|
- autorun: Don't allow autorun for things we mount on startup [#660595]
|
||||||
|
- Turn passphrase prompt into a dialog [#674962]
|
||||||
|
- Implement org.Gtk.MountOperationHandler [#678516]
|
||||||
|
* Network menu improvements
|
||||||
|
- Sort Wifi networks by strength [Giovanni; #658946]
|
||||||
|
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
|
||||||
|
* clock: Switch to using GnomeWallClock [Colin; #657074]
|
||||||
|
* remoteSearch: Allow to reference .desktop file for Title/Icon
|
||||||
|
[Florian; #678816]
|
||||||
|
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
|
||||||
|
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
|
||||||
|
#658955, #677497, #678396, #678502]
|
||||||
|
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||||
|
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
|
||||||
|
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
|
||||||
|
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
|
||||||
|
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
|
||||||
|
|
||||||
|
3.5.2
|
||||||
|
=====
|
||||||
|
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
|
||||||
|
* popupMenu: make sure to break the grab when the slider is not visible
|
||||||
|
[Stefano; #672713]
|
||||||
|
* st-theme-node-drawing: Don't use GL types [Neil; #672711]
|
||||||
|
* Mirror Evolution calendar settings into our own schema [Owen; #674424]
|
||||||
|
* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
|
||||||
|
* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
|
||||||
|
* NetworkMenu: use network-offline while loading [Giovanni; #674426]
|
||||||
|
* lookingGlass: Remove the Errors tab [Jasper; #675104]
|
||||||
|
* searchDisplay: Reset keyboard focus after displaying async results
|
||||||
|
[Rui; #675078]
|
||||||
|
* gdm: don't fail if fprintd is unavailable [Ray; #675006]
|
||||||
|
* messageTray: Fix scrolling up [Jasper; #661615]
|
||||||
|
* main: Close the recorder instead of pausing it [Rui; #675128]
|
||||||
|
* Accessibility [Alejandro]
|
||||||
|
- Use the proper label_actor for date menu on top panel [#675307]
|
||||||
|
- Set the proper role/label_actor for SearchResult.content [#672242]
|
||||||
|
- do not expose a label text if 'hidden' style class is used [#675341]
|
||||||
|
* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
|
||||||
|
* theme: use a smaller border-radius for top bar [Jakub; #672430]
|
||||||
|
* placeDisplay: use new bookmark file location [Matthias; #675443]
|
||||||
|
* port all synchronous search providers to the async API [Jasper, Rui; #675328]
|
||||||
|
* NetworkAgent: disallow multiple requests for the same connection/setting
|
||||||
|
[Giovanni; #674961]
|
||||||
|
* userMenu: Update to latest mockups [Florian; #675802]
|
||||||
|
* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
|
||||||
|
* messageTray: Make Source usable without subclassing [Jasper; #661236]
|
||||||
|
* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
|
||||||
|
* Fix formatting of bluetooth passkey [Florian; #651251]
|
||||||
|
* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
|
||||||
|
* Don't use global.log() [Jasper; #675790]
|
||||||
|
* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
|
||||||
|
* shell-app: Raise windows in reverse order to preserve the stacking
|
||||||
|
[Rui; #676371]
|
||||||
|
* Generalize gdm-mode [Florian; #676156]
|
||||||
|
* Switch string formatting to the one inside gjs [Jasper; #675479]
|
||||||
|
* extensionUtils: Support subdirectories in getCurrentExtension
|
||||||
|
[Jasper; #677001]
|
||||||
|
* panel: Refuse to add (legacy) status icons not part of the session mode
|
||||||
|
[Florian; #677058]
|
||||||
|
* Add an initial-setup mode [Matthias; #676697]
|
||||||
|
* status/keyboard: Port to the new input sources settings [Rui; #641531]
|
||||||
|
* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
|
||||||
|
* userMenu: Indicate progress on status changes [Florian; #659067]
|
||||||
|
* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
|
||||||
|
* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
|
||||||
|
* endSessionDialog: Factor out _updateDescription from _updateContent
|
||||||
|
[Alejandro; #674210]
|
||||||
|
* Fix empathy's appMenu freezing the shell [Alban; #676447]
|
||||||
|
* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
|
||||||
|
#672272]
|
||||||
|
* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
|
||||||
|
Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
|
||||||
|
#675370, #676347, #676806, #677097]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
|
||||||
|
Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
|
||||||
|
Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
|
||||||
|
Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
|
||||||
|
Colin Walters, Dan Winship, Ron Yorston
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
|
||||||
|
Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
|
||||||
|
Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
|
||||||
|
Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
|
||||||
|
Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
|
||||||
|
|
||||||
|
3.4.1
|
||||||
|
=====
|
||||||
|
* Fix crash that occurred when an icon theme change caused unexpected
|
||||||
|
reentrancy in the icon loading code [Jasper; #673512]
|
||||||
|
* Don't show system and other disabled users in the GDM user list
|
||||||
|
[Adel; #673784]
|
||||||
|
* Make gnome-shell-calendar-server initialize GTK+ so it can display
|
||||||
|
password prompts if needed [#673608; Owen, Rico]
|
||||||
|
* Adapt to Mutter API change for keybinding addition [Florian; #673014]
|
||||||
|
* Fix crash when an extension was installed as both a user extension
|
||||||
|
and a system extension [#673613; Jasper]
|
||||||
|
* Fix bug where chat entry could end up partially offscreen [Joost, 661944]
|
||||||
|
* Fix problem where icons weren't updating when theme was changed
|
||||||
|
[#672941; Florian]
|
||||||
|
* Look for Evolution calendar settings in GSettings, not GConf [#673610; Owen]
|
||||||
|
* Add <super>F10 for the application menu [#672909; Florian]
|
||||||
|
* Fix %Id format characters to work in translations [#673106; Cosimo]
|
||||||
|
(were already used in fa translation)
|
||||||
|
* Fix error when NetworkManager restarts [#673043; Giovanni]
|
||||||
|
* Improve efficiency of overview redraws by working around Clutter issue
|
||||||
|
[Stefano; #670636]
|
||||||
|
* Misc bug fixes [Florian, Giovanni, Jasper, Rui, Stefano;
|
||||||
|
#672592, #672641, #672719, #673187, #673233, #673656]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, Adel Gadllah, Rui Matos,
|
||||||
|
Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz,
|
||||||
|
Joost Verdoorn
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Khaled Hosny [ar], Ihar Hrachyshka [be], Alexander Shopov [bg], Gil Forcada,
|
||||||
|
Jordi Serratosa [ca], Petr Kovar [cs], Bruce Cowan [en_GB],
|
||||||
|
Carles Ferrando [ca@valencia], Wolfgang Stöggl [de], Daniel Mustieles [es],
|
||||||
|
Arash Mousavi [fa], Bruno Brouard [fr], Fran Diéguez [gl],
|
||||||
|
Sweta Kothari [gu], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
||||||
|
Shankar Prasad [kn], Žygimantas Beručka [lt], Rudolfs Mazurs [lv],
|
||||||
|
Sandeep Sheshrao Shedmake [mr], Kjartan Maraas [nb], Piotr Drąg [pl],
|
||||||
|
Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl],
|
||||||
|
Miroslav Nikolić [sr], Sasi Bhushan, Praveen Illa [te], Yinghua Wang [zh_CN]
|
||||||
|
|
||||||
3.4.0
|
3.4.0
|
||||||
=====
|
=====
|
||||||
* Don't crash when taking screenshots [Jasper; #672775]
|
* Don't crash when taking screenshots [Jasper; #672775]
|
||||||
@ -8,10 +460,10 @@ Contributors:
|
|||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
|
Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
|
||||||
Alexander Shopov [bg], Marek Černocký [cz], Jiri Grönroos, Timo Jyrinki [fi],
|
Alexander Shopov [bg], Marek Černocký [cs], Jiri Grönroos, Timo Jyrinki [fi],
|
||||||
Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
|
Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
|
||||||
Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
|
Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
|
||||||
Mattias Põldaru [et], Changwoo Ryu [kr], Rudolfs Mazurs [lv],
|
Mattias Põldaru [et], Changwoo Ryu [ko], Rudolfs Mazurs [lv],
|
||||||
Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
|
Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
|
||||||
Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
|
Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||||
|
|
||||||
@ -80,7 +532,7 @@ Contributors:
|
|||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
|
Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
|
||||||
Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cz],
|
Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cs],
|
||||||
Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
|
Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
|
||||||
Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
|
Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
|
||||||
Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
|
Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
|
||||||
@ -129,7 +581,7 @@ Contributors:
|
|||||||
Will Thompson, Stef Walter
|
Will Thompson, Stef Walter
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cz],
|
Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cs],
|
||||||
Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
|
Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
|
||||||
Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
|
Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||||
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
|
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
|
||||||
@ -260,7 +712,7 @@ Contributors:
|
|||||||
Marina Zhurakhinskaya
|
Marina Zhurakhinskaya
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
Petr Kovar [cz], Kris Thomsen [dk], Daniel Mustieles [es],
|
Petr Kovar [cs], Kris Thomsen [dk], Daniel Mustieles [es],
|
||||||
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
|
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||||
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
|
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
|
||||||
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
|
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
|
||||||
@ -423,7 +875,7 @@ Contributors:
|
|||||||
Translations:
|
Translations:
|
||||||
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
|
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
|
||||||
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
|
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
|
||||||
Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
|
Petr Kovar [cs], Mario Blättermann [de], Kris Thomsen [dk],
|
||||||
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
|
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
|
||||||
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
|
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
|
||||||
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
"It can be used only by extensions.gnome.org"
|
"It can be used only by extensions.gnome.org"
|
||||||
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
|
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
|
||||||
|
|
||||||
#define PLUGIN_API_VERSION 3
|
#define PLUGIN_API_VERSION 5
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GDBusProxy *proxy;
|
GDBusProxy *proxy;
|
||||||
@ -163,6 +163,7 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
|||||||
plugin->newp = NPP_New;
|
plugin->newp = NPP_New;
|
||||||
plugin->destroy = NPP_Destroy;
|
plugin->destroy = NPP_Destroy;
|
||||||
plugin->getvalue = NPP_GetValue;
|
plugin->getvalue = NPP_GetValue;
|
||||||
|
plugin->setwindow = NPP_SetWindow;
|
||||||
|
|
||||||
return NPERR_NO_ERROR;
|
return NPERR_NO_ERROR;
|
||||||
}
|
}
|
||||||
@ -224,7 +225,7 @@ NPP_New(NPMIMEType mimetype,
|
|||||||
NULL, /* interface info */
|
NULL, /* interface info */
|
||||||
"org.gnome.Shell",
|
"org.gnome.Shell",
|
||||||
"/org/gnome/Shell",
|
"/org/gnome/Shell",
|
||||||
"org.gnome.Shell",
|
"org.gnome.Shell.Extensions",
|
||||||
NULL, /* GCancellable */
|
NULL, /* GCancellable */
|
||||||
&error);
|
&error);
|
||||||
if (!data->proxy)
|
if (!data->proxy)
|
||||||
@ -267,6 +268,7 @@ typedef struct {
|
|||||||
NPObject parent;
|
NPObject parent;
|
||||||
NPP instance;
|
NPP instance;
|
||||||
GDBusProxy *proxy;
|
GDBusProxy *proxy;
|
||||||
|
GSettings *settings;
|
||||||
NPObject *listener;
|
NPObject *listener;
|
||||||
NPObject *restart_listener;
|
NPObject *restart_listener;
|
||||||
gint signal_id;
|
gint signal_id;
|
||||||
@ -323,6 +325,9 @@ on_shell_appeared (GDBusConnection *connection,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SHELL_SCHEMA "org.gnome.shell"
|
||||||
|
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
|
||||||
|
|
||||||
static NPObject *
|
static NPObject *
|
||||||
plugin_object_allocate (NPP instance,
|
plugin_object_allocate (NPP instance,
|
||||||
NPClass *klass)
|
NPClass *klass)
|
||||||
@ -332,6 +337,7 @@ plugin_object_allocate (NPP instance,
|
|||||||
|
|
||||||
obj->instance = instance;
|
obj->instance = instance;
|
||||||
obj->proxy = g_object_ref (data->proxy);
|
obj->proxy = g_object_ref (data->proxy);
|
||||||
|
obj->settings = g_settings_new (SHELL_SCHEMA);
|
||||||
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
|
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
|
||||||
G_CALLBACK (on_shell_signal), obj);
|
G_CALLBACK (on_shell_signal), obj);
|
||||||
|
|
||||||
@ -367,39 +373,14 @@ plugin_object_deallocate (NPObject *npobj)
|
|||||||
g_slice_free (PluginObject, obj);
|
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
|
static inline gboolean
|
||||||
uuid_is_valid (const gchar *uuid)
|
uuid_is_valid (NPString string)
|
||||||
{
|
{
|
||||||
gsize i;
|
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)
|
if (c < 32 || c >= 127)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -462,8 +443,73 @@ jsonify_variant (GVariant *variant,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_args (const gchar *format_str,
|
||||||
|
uint32_t argc,
|
||||||
|
const NPVariant *argv,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
gsize i;
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
if (strlen (format_str) != argc)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
va_start (args, argv);
|
||||||
|
|
||||||
|
for (i = 0; format_str[i]; i++)
|
||||||
|
{
|
||||||
|
gpointer arg_location;
|
||||||
|
const NPVariant arg = argv[i];
|
||||||
|
|
||||||
|
arg_location = va_arg (args, gpointer);
|
||||||
|
|
||||||
|
switch (format_str[i])
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
NPString string;
|
||||||
|
|
||||||
|
if (!NPVARIANT_IS_STRING (arg))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
string = NPVARIANT_TO_STRING (arg);
|
||||||
|
|
||||||
|
if (!uuid_is_valid (string))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
if (!NPVARIANT_IS_BOOLEAN (arg))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'o':
|
||||||
|
if (!NPVARIANT_IS_OBJECT (arg))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
out:
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_list_extensions (PluginObject *obj,
|
plugin_list_extensions (PluginObject *obj,
|
||||||
|
uint32_t argc,
|
||||||
|
const NPVariant *args,
|
||||||
NPVariant *result)
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
@ -489,90 +535,158 @@ plugin_list_extensions (PluginObject *obj,
|
|||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_enable_extension (PluginObject *obj,
|
plugin_enable_extension (PluginObject *obj,
|
||||||
NPString uuid,
|
uint32_t argc,
|
||||||
gboolean enabled)
|
const NPVariant *argv,
|
||||||
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
gboolean ret;
|
||||||
if (!uuid_is_valid (uuid_str))
|
gchar *uuid;
|
||||||
{
|
gboolean enabled;
|
||||||
g_free (uuid_str);
|
gsize length;
|
||||||
|
gchar **uuids;
|
||||||
|
const gchar **new_uuids;
|
||||||
|
|
||||||
|
if (!parse_args ("ub", argc, argv, &uuid, &enabled))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
|
||||||
|
length = g_strv_length (uuids);
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
|
||||||
|
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
|
||||||
|
new_uuids[length] = uuid;
|
||||||
|
new_uuids[length + 1] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gsize i = 0, j = 0;
|
||||||
|
new_uuids = g_new (const gchar *, length);
|
||||||
|
for (i = 0; i < length; i ++)
|
||||||
|
{
|
||||||
|
if (g_str_equal (uuids[i], uuid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
new_uuids[j] = uuids[i];
|
||||||
|
j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_dbus_proxy_call (obj->proxy,
|
new_uuids[j] = NULL;
|
||||||
(enabled ? "EnableExtension" : "DisableExtension"),
|
}
|
||||||
g_variant_new ("(s)", uuid_str),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1, /* timeout */
|
|
||||||
NULL, /* cancellable */
|
|
||||||
NULL, /* callback */
|
|
||||||
NULL /* user_data */);
|
|
||||||
|
|
||||||
g_free (uuid_str);
|
ret = g_settings_set_strv (obj->settings,
|
||||||
|
ENABLED_EXTENSIONS_KEY,
|
||||||
|
new_uuids);
|
||||||
|
|
||||||
return TRUE;
|
g_strfreev (uuids);
|
||||||
|
g_free (new_uuids);
|
||||||
|
g_free (uuid);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _AsyncClosure AsyncClosure;
|
||||||
|
|
||||||
|
struct _AsyncClosure {
|
||||||
|
PluginObject *obj;
|
||||||
|
NPObject *callback;
|
||||||
|
NPObject *errback;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
install_extension_cb (GObject *proxy,
|
||||||
|
GAsyncResult *async_res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
AsyncClosure *async_closure = (AsyncClosure *) user_data;
|
||||||
|
GError *error = NULL;
|
||||||
|
GVariant *res;
|
||||||
|
NPVariant args[1];
|
||||||
|
NPVariant result = { NPVariantType_Void };
|
||||||
|
NPObject *callback;
|
||||||
|
|
||||||
|
res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
|
||||||
|
|
||||||
|
if (res == NULL)
|
||||||
|
{
|
||||||
|
if (g_dbus_error_is_remote_error (error))
|
||||||
|
g_dbus_error_strip_remote_error (error);
|
||||||
|
STRINGZ_TO_NPVARIANT (error->message, args[0]);
|
||||||
|
callback = async_closure->errback;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *string_result;
|
||||||
|
g_variant_get (res, "(&s)", &string_result);
|
||||||
|
STRINGZ_TO_NPVARIANT (string_result, args[0]);
|
||||||
|
callback = async_closure->callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
funcs.invokeDefault (async_closure->obj->instance,
|
||||||
|
callback, args, 1, &result);
|
||||||
|
|
||||||
|
funcs.releasevariantvalue (&result);
|
||||||
|
|
||||||
|
funcs.releaseobject (async_closure->callback);
|
||||||
|
funcs.releaseobject (async_closure->errback);
|
||||||
|
g_slice_free (AsyncClosure, async_closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_install_extension (PluginObject *obj,
|
plugin_install_extension (PluginObject *obj,
|
||||||
NPString uuid,
|
uint32_t argc,
|
||||||
NPString version_tag)
|
const NPVariant *argv,
|
||||||
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
gchar *uuid;
|
||||||
gchar *version_tag_str;
|
NPObject *callback, *errback;
|
||||||
|
AsyncClosure *async_closure;
|
||||||
|
|
||||||
if (!uuid_is_valid (uuid_str))
|
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
|
||||||
{
|
|
||||||
g_free (uuid_str);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
version_tag_str = g_strndup (version_tag.UTF8Characters,
|
async_closure = g_slice_new (AsyncClosure);
|
||||||
version_tag.UTF8Length);
|
async_closure->obj = obj;
|
||||||
|
async_closure->callback = funcs.retainobject (callback);
|
||||||
|
async_closure->errback = funcs.retainobject (errback);
|
||||||
|
|
||||||
g_dbus_proxy_call (obj->proxy,
|
g_dbus_proxy_call (obj->proxy,
|
||||||
"InstallRemoteExtension",
|
"InstallRemoteExtension",
|
||||||
g_variant_new ("(ss)",
|
g_variant_new ("(s)", uuid),
|
||||||
uuid_str,
|
|
||||||
version_tag_str),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1, /* timeout */
|
-1, /* timeout */
|
||||||
NULL, /* cancellable */
|
NULL, /* cancellable */
|
||||||
NULL, /* callback */
|
install_extension_cb,
|
||||||
NULL /* user_data */);
|
async_closure);
|
||||||
|
|
||||||
g_free (uuid_str);
|
g_free (uuid);
|
||||||
g_free (version_tag_str);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_uninstall_extension (PluginObject *obj,
|
plugin_uninstall_extension (PluginObject *obj,
|
||||||
NPString uuid,
|
uint32_t argc,
|
||||||
|
const NPVariant *argv,
|
||||||
NPVariant *result)
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
GVariant *res;
|
GVariant *res;
|
||||||
gchar *uuid_str;
|
gchar *uuid;
|
||||||
|
|
||||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
if (!parse_args ("u", argc, argv, &uuid))
|
||||||
if (!uuid_is_valid (uuid_str))
|
|
||||||
{
|
|
||||||
g_free (uuid_str);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||||
"UninstallExtension",
|
"UninstallExtension",
|
||||||
g_variant_new ("(s)",
|
g_variant_new ("(s)", uuid),
|
||||||
uuid_str),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1, /* timeout */
|
-1, /* timeout */
|
||||||
NULL, /* cancellable */
|
NULL, /* cancellable */
|
||||||
&error);
|
&error);
|
||||||
|
|
||||||
g_free (uuid_str);
|
g_free (uuid);
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -586,29 +700,26 @@ plugin_uninstall_extension (PluginObject *obj,
|
|||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_get_info (PluginObject *obj,
|
plugin_get_info (PluginObject *obj,
|
||||||
NPString uuid,
|
uint32_t argc,
|
||||||
|
const NPVariant *argv,
|
||||||
NPVariant *result)
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
GVariant *res;
|
GVariant *res;
|
||||||
gchar *uuid_str;
|
gchar *uuid;
|
||||||
|
|
||||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
if (!parse_args ("u", argc, argv, &uuid))
|
||||||
if (!uuid_is_valid (uuid_str))
|
|
||||||
{
|
|
||||||
g_free (uuid_str);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||||
"GetExtensionInfo",
|
"GetExtensionInfo",
|
||||||
g_variant_new ("(s)", uuid_str),
|
g_variant_new ("(s)", uuid),
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1, /* timeout */
|
-1, /* timeout */
|
||||||
NULL, /* cancellable */
|
NULL, /* cancellable */
|
||||||
&error);
|
&error);
|
||||||
|
|
||||||
g_free (uuid_str);
|
g_free (uuid);
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
@ -622,30 +733,25 @@ plugin_get_info (PluginObject *obj,
|
|||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_get_errors (PluginObject *obj,
|
plugin_get_errors (PluginObject *obj,
|
||||||
NPString uuid,
|
uint32_t argc,
|
||||||
|
const NPVariant *argv,
|
||||||
NPVariant *result)
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
GVariant *res;
|
GVariant *res;
|
||||||
gchar *uuid_str;
|
gchar *uuid;
|
||||||
|
|
||||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
if (!parse_args ("u", argc, argv, &uuid))
|
||||||
if (!uuid_is_valid (uuid_str))
|
|
||||||
{
|
|
||||||
g_free (uuid_str);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||||
"GetExtensionErrors",
|
"GetExtensionErrors",
|
||||||
g_variant_new ("(s)", uuid_str),
|
g_variant_new ("(s)", uuid),
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1, /* timeout */
|
-1, /* timeout */
|
||||||
NULL, /* cancellable */
|
NULL, /* cancellable */
|
||||||
&error);
|
&error);
|
||||||
|
|
||||||
g_free (uuid_str);
|
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
g_warning ("Failed to retrieve errors: %s", error->message);
|
g_warning ("Failed to retrieve errors: %s", error->message);
|
||||||
@ -658,28 +764,24 @@ plugin_get_errors (PluginObject *obj,
|
|||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_launch_extension_prefs (PluginObject *obj,
|
plugin_launch_extension_prefs (PluginObject *obj,
|
||||||
NPString uuid,
|
uint32_t argc,
|
||||||
|
const NPVariant *argv,
|
||||||
NPVariant *result)
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
gchar *uuid_str;
|
gchar *uuid;
|
||||||
|
|
||||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
if (!parse_args ("u", argc, argv, &uuid))
|
||||||
if (!uuid_is_valid (uuid_str))
|
|
||||||
{
|
|
||||||
g_free (uuid_str);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
g_dbus_proxy_call (obj->proxy,
|
g_dbus_proxy_call (obj->proxy,
|
||||||
"LaunchExtensionPrefs",
|
"LaunchExtensionPrefs",
|
||||||
g_variant_new ("(s)", uuid_str),
|
g_variant_new ("(s)", uuid),
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
-1, /* timeout */
|
-1, /* timeout */
|
||||||
NULL, /* cancellable */
|
NULL, /* cancellable */
|
||||||
NULL, /* callback */
|
NULL, /* callback */
|
||||||
NULL /* user_data */);
|
NULL /* user_data */);
|
||||||
|
|
||||||
g_free (uuid_str);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,10 +835,40 @@ plugin_get_shell_version (PluginObject *obj,
|
|||||||
return ret;
|
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
|
static bool
|
||||||
plugin_object_invoke (NPObject *npobj,
|
plugin_object_invoke (NPObject *npobj,
|
||||||
NPIdentifier name,
|
NPIdentifier name,
|
||||||
const NPVariant *args,
|
const NPVariant *argv,
|
||||||
uint32_t argc,
|
uint32_t argc,
|
||||||
NPVariant *result)
|
NPVariant *result)
|
||||||
{
|
{
|
||||||
@ -748,61 +880,13 @@ plugin_object_invoke (NPObject *npobj,
|
|||||||
|
|
||||||
VOID_TO_NPVARIANT (*result);
|
VOID_TO_NPVARIANT (*result);
|
||||||
|
|
||||||
if (!plugin_object_has_method (npobj, name))
|
#define METHOD(x) \
|
||||||
|
if (name == x##_id) \
|
||||||
|
return plugin_##x (obj, argc, argv, result);
|
||||||
|
METHODS
|
||||||
|
#undef METHOD
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (name == list_extensions_id)
|
|
||||||
return plugin_list_extensions (obj, result);
|
|
||||||
else if (name == get_info_id)
|
|
||||||
{
|
|
||||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
|
||||||
|
|
||||||
return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result);
|
|
||||||
}
|
|
||||||
else if (name == enable_extension_id)
|
|
||||||
{
|
|
||||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
|
||||||
if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE;
|
|
||||||
|
|
||||||
return plugin_enable_extension (obj,
|
|
||||||
NPVARIANT_TO_STRING(args[0]),
|
|
||||||
NPVARIANT_TO_BOOLEAN(args[1]));
|
|
||||||
}
|
|
||||||
else if (name == install_extension_id)
|
|
||||||
{
|
|
||||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
|
||||||
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
|
|
||||||
|
|
||||||
return plugin_install_extension (obj,
|
|
||||||
NPVARIANT_TO_STRING(args[0]),
|
|
||||||
NPVARIANT_TO_STRING(args[1]));
|
|
||||||
}
|
|
||||||
else if (name == uninstall_extension_id)
|
|
||||||
{
|
|
||||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
|
||||||
|
|
||||||
return plugin_uninstall_extension (obj,
|
|
||||||
NPVARIANT_TO_STRING(args[0]),
|
|
||||||
result);
|
|
||||||
}
|
|
||||||
else if (name == get_errors_id)
|
|
||||||
{
|
|
||||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
|
||||||
|
|
||||||
return plugin_get_errors (obj,
|
|
||||||
NPVARIANT_TO_STRING(args[0]),
|
|
||||||
result);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -946,3 +1030,12 @@ NPP_GetValue(NPP instance,
|
|||||||
|
|
||||||
return NPERR_NO_ERROR;
|
return NPERR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Opera tries to call NPP_SetWindow without checking the
|
||||||
|
* NULL pointer beforehand. */
|
||||||
|
NPError
|
||||||
|
NPP_SetWindow(NPP instance,
|
||||||
|
NPWindow *window)
|
||||||
|
{
|
||||||
|
return NPERR_NO_ERROR;
|
||||||
|
}
|
||||||
|
66
configure.ac
@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
AC_INIT([gnome-shell],[3.4.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
AC_INIT([gnome-shell],[3.6.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||||
@ -44,15 +44,15 @@ AC_SUBST(PYTHON)
|
|||||||
|
|
||||||
# We need at least this, since gst_plugin_register_static() was added
|
# 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.
|
# 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=
|
recorder_modules=
|
||||||
build_recorder=false
|
build_recorder=false
|
||||||
AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
|
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)
|
AC_MSG_RESULT(yes)
|
||||||
build_recorder=true
|
build_recorder=true
|
||||||
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
|
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
|
||||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT(no)
|
AC_MSG_RESULT(no)
|
||||||
@ -60,30 +60,32 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
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
|
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||||
GJS_MIN_VERSION=1.29.18
|
GJS_MIN_VERSION=1.33.2
|
||||||
MUTTER_MIN_VERSION=3.3.92
|
MUTTER_MIN_VERSION=3.6.0
|
||||||
FOLKS_MIN_VERSION=0.5.2
|
|
||||||
GTK_MIN_VERSION=3.3.9
|
GTK_MIN_VERSION=3.3.9
|
||||||
GIO_MIN_VERSION=2.31.6
|
GIO_MIN_VERSION=2.31.6
|
||||||
LIBECAL_MIN_VERSION=2.32.0
|
LIBECAL_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
||||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||||
POLKIT_MIN_VERSION=0.100
|
POLKIT_MIN_VERSION=0.100
|
||||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||||
GCR_MIN_VERSION=3.3.90
|
GCR_MIN_VERSION=3.3.90
|
||||||
|
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
|
||||||
|
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||||
|
|
||||||
# Collect more than 20 libraries for a prize!
|
# Collect more than 20 libraries for a prize!
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||||
libxml-2.0
|
libxml-2.0
|
||||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||||
folks >= $FOLKS_MIN_VERSION
|
atk-bridge-2.0
|
||||||
libmutter >= $MUTTER_MIN_VERSION
|
libmutter >= $MUTTER_MIN_VERSION
|
||||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||||
libgnome-menu-3.0 $recorder_modules
|
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||||
|
$recorder_modules
|
||||||
gdk-x11-3.0 libsoup-2.4
|
gdk-x11-3.0 libsoup-2.4
|
||||||
gl
|
gl
|
||||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
@ -95,7 +97,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
|||||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||||
libnm-glib libnm-util gnome-keyring-1
|
libnm-glib libnm-util gnome-keyring-1
|
||||||
gcr-3 >= $GCR_MIN_VERSION)
|
gcr-3 >= $GCR_MIN_VERSION
|
||||||
|
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||||
|
|
||||||
@ -103,13 +106,10 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
|||||||
|
|
||||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||||
|
|
||||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
|
||||||
|
|
||||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
|
||||||
AC_SUBST(JHBUILD_TYPELIBDIR)
|
|
||||||
|
|
||||||
saved_CFLAGS=$CFLAGS
|
saved_CFLAGS=$CFLAGS
|
||||||
saved_LIBS=$LIBS
|
saved_LIBS=$LIBS
|
||||||
@ -123,7 +123,7 @@ 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.2 x11)
|
||||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
|
||||||
|
|
||||||
AC_MSG_CHECKING([for bluetooth support])
|
AC_MSG_CHECKING([for bluetooth support])
|
||||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||||
@ -239,31 +239,6 @@ AC_ARG_ENABLE(jhbuild-wrapper-script,
|
|||||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
||||||
|
|
||||||
AC_MSG_CHECKING([location of system Certificate Authority list])
|
|
||||||
AC_ARG_WITH(ca-certificates,
|
|
||||||
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
|
|
||||||
[path to system Certificate Authority list])])
|
|
||||||
|
|
||||||
if test "$with_ca_certificates" = "no"; then
|
|
||||||
AC_MSG_RESULT([disabled])
|
|
||||||
else
|
|
||||||
if test -z "$with_ca_certificates"; then
|
|
||||||
for f in /etc/pki/tls/certs/ca-bundle.crt \
|
|
||||||
/etc/ssl/certs/ca-certificates.crt; do
|
|
||||||
if test -f "$f"; then
|
|
||||||
with_ca_certificates="$f"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if test -z "$with_ca_certificates"; then
|
|
||||||
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_MSG_RESULT($with_ca_certificates)
|
|
||||||
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
|
|
||||||
fi
|
|
||||||
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
|
|
||||||
|
|
||||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
||||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
||||||
|
|
||||||
@ -277,6 +252,7 @@ AC_CONFIG_FILES([
|
|||||||
docs/reference/st/Makefile
|
docs/reference/st/Makefile
|
||||||
docs/reference/st/st-docs.sgml
|
docs/reference/st/st-docs.sgml
|
||||||
js/Makefile
|
js/Makefile
|
||||||
|
src/calendar-server/evolution-calendar.desktop.in
|
||||||
src/Makefile
|
src/Makefile
|
||||||
browser-plugin/Makefile
|
browser-plugin/Makefile
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
|
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>
|
||||||
|
|
12
data/50-gnome-shell-system.xml.in
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?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"/>
|
||||||
|
|
||||||
|
</KeyListEntries>
|
||||||
|
|
@ -8,14 +8,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
|||||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||||
$< > $@ || rm $@
|
$< > $@ || rm $@
|
||||||
|
|
||||||
# Placeholder until we add intltool
|
@INTLTOOL_DESKTOP_RULE@
|
||||||
%.desktop:%.desktop.in
|
|
||||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
|
||||||
|
|
||||||
searchprovidersdir = $(pkgdatadir)/open-search-providers
|
|
||||||
dist_searchproviders_DATA = \
|
|
||||||
open-search-providers/google.xml \
|
|
||||||
open-search-providers/wikipedia.xml
|
|
||||||
|
|
||||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||||
introspection_DATA = org.gnome.ShellSearchProvider.xml
|
introspection_DATA = org.gnome.ShellSearchProvider.xml
|
||||||
@ -36,26 +29,39 @@ dist_theme_DATA = \
|
|||||||
theme/dash-placeholder.svg \
|
theme/dash-placeholder.svg \
|
||||||
theme/filter-selected-ltr.svg \
|
theme/filter-selected-ltr.svg \
|
||||||
theme/filter-selected-rtl.svg \
|
theme/filter-selected-rtl.svg \
|
||||||
theme/gdm.css \
|
|
||||||
theme/gnome-shell.css \
|
theme/gnome-shell.css \
|
||||||
|
theme/logged-in-indicator.svg \
|
||||||
|
theme/message-tray-background.png \
|
||||||
|
theme/noise-texture.png \
|
||||||
theme/panel-button-border.svg \
|
theme/panel-button-border.svg \
|
||||||
theme/panel-button-highlight-narrow.svg \
|
theme/panel-button-highlight-narrow.svg \
|
||||||
theme/panel-button-highlight-wide.svg \
|
theme/panel-button-highlight-wide.svg \
|
||||||
theme/process-working.svg \
|
theme/process-working.svg \
|
||||||
theme/running-indicator.svg \
|
theme/running-indicator.svg \
|
||||||
theme/scroll-hhandle.svg \
|
|
||||||
theme/scroll-vhandle.svg \
|
|
||||||
theme/source-button-border.svg \
|
theme/source-button-border.svg \
|
||||||
|
theme/summary-counter.svg \
|
||||||
theme/toggle-off-us.svg \
|
theme/toggle-off-us.svg \
|
||||||
theme/toggle-off-intl.svg \
|
theme/toggle-off-intl.svg \
|
||||||
theme/toggle-on-us.svg \
|
theme/toggle-on-us.svg \
|
||||||
theme/toggle-on-intl.svg \
|
theme/toggle-on-intl.svg \
|
||||||
theme/ws-switch-arrow-up.svg \
|
theme/ws-switch-arrow-up.png \
|
||||||
theme/ws-switch-arrow-down.svg
|
theme/ws-switch-arrow-down.png
|
||||||
|
|
||||||
|
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||||
|
keys_in_files = \
|
||||||
|
50-gnome-shell-screenshot.xml.in \
|
||||||
|
50-gnome-shell-system.xml.in \
|
||||||
|
$(NULL)
|
||||||
|
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||||
|
|
||||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||||
|
|
||||||
@INTLTOOL_XML_NOMERGE_RULE@
|
@INTLTOOL_XML_NOMERGE_RULE@
|
||||||
|
|
||||||
|
%.gschema.xml.in: %.gschema.xml.in.in Makefile
|
||||||
|
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
|
||||||
|
$< > $@ || rm $@
|
||||||
|
|
||||||
@GSETTINGS_RULES@
|
@GSETTINGS_RULES@
|
||||||
|
|
||||||
# We need to compile schemas at make time
|
# We need to compile schemas at make time
|
||||||
@ -68,24 +74,21 @@ all-local: gschemas.compiled
|
|||||||
convertdir = $(datadir)/GConf/gsettings
|
convertdir = $(datadir)/GConf/gsettings
|
||||||
convert_DATA = gnome-shell-overrides.convert
|
convert_DATA = gnome-shell-overrides.convert
|
||||||
|
|
||||||
shadersdir = $(pkgdatadir)/shaders
|
|
||||||
shaders_DATA = \
|
|
||||||
shaders/dim-window.glsl
|
|
||||||
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
gnome-shell.desktop.in.in \
|
gnome-shell.desktop.in.in \
|
||||||
gnome-shell-extension-prefs.desktop.in.in \
|
gnome-shell-extension-prefs.desktop.in.in \
|
||||||
$(introspection_DATA) \
|
$(introspection_DATA) \
|
||||||
$(menu_DATA) \
|
$(menu_DATA) \
|
||||||
$(shaders_DATA) \
|
|
||||||
$(convert_DATA) \
|
$(convert_DATA) \
|
||||||
org.gnome.shell.gschema.xml.in
|
$(keys_in_files) \
|
||||||
|
org.gnome.shell.gschema.xml.in.in
|
||||||
|
|
||||||
CLEANFILES = \
|
CLEANFILES = \
|
||||||
gnome-shell.desktop.in \
|
gnome-shell.desktop.in \
|
||||||
gnome-shell-extension-prefs.in \
|
gnome-shell-extension-prefs.in \
|
||||||
$(desktop_DATA) \
|
$(desktop_DATA) \
|
||||||
|
$(keys_DATA) \
|
||||||
$(gsettings_SCHEMAS) \
|
$(gsettings_SCHEMAS) \
|
||||||
gschemas.compiled
|
gschemas.compiled \
|
||||||
|
org.gnome.shell.gschema.valid \
|
||||||
|
org.gnome.shell.gschema.xml.in
|
||||||
|
@ -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>
|
|
@ -108,7 +108,7 @@
|
|||||||
</doc:summary>
|
</doc:summary>
|
||||||
</doc:doc>
|
</doc:doc>
|
||||||
</arg>
|
</arg>
|
||||||
<arg type="a{sv}" direction="out">
|
<arg type="aa{sv}" direction="out">
|
||||||
<doc:doc>
|
<doc:doc>
|
||||||
<doc:summary>
|
<doc:summary>
|
||||||
<doc:para>
|
<doc:para>
|
||||||
|
@ -39,10 +39,6 @@
|
|||||||
will be displayed in the favorites area.
|
will be displayed in the favorites area.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="disabled-open-search-providers" type="as">
|
|
||||||
<default>[]</default>
|
|
||||||
<_summary>disabled OpenSearch providers</_summary>
|
|
||||||
</key>
|
|
||||||
<key name="command-history" type="as">
|
<key name="command-history" type="as">
|
||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||||
@ -61,9 +57,9 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
|
|||||||
<_summary>Internally used to store the last session presence status for the user. The
|
<_summary>Internally used to store the last session presence status for the user. The
|
||||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||||
</key>
|
</key>
|
||||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
|
||||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||||
|
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
@ -78,6 +74,31 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
|
<schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
|
||||||
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
|
<key name="open-application-menu" type="as">
|
||||||
|
<default>["<Super>F10"]</default>
|
||||||
|
<_summary>Keybinding to open the application menu</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to open the application menu.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key name="toggle-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="toggle-recording" type="as">
|
||||||
|
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
|
||||||
|
<_summary>Keybinding to toggle the screen recorder</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to start/stop the builtin screen recorder.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
|
||||||
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
||||||
gettext-domain="@GETTEXT_PACKAGE@">
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
<key name="keyboard-type" type="s">
|
<key name="keyboard-type" type="s">
|
||||||
@ -89,24 +110,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
|
||||||
gettext-domain="@GETTEXT_PACKAGE@">
|
|
||||||
<key name="show-seconds" type="b">
|
|
||||||
<default>false</default>
|
|
||||||
<_summary>Show time with seconds</_summary>
|
|
||||||
<_description>
|
|
||||||
If true, display seconds in time.
|
|
||||||
</_description>
|
|
||||||
</key>
|
|
||||||
<key name="show-date" type="b">
|
|
||||||
<default>false</default>
|
|
||||||
<_summary>Show date in clock</_summary>
|
|
||||||
<_description>
|
|
||||||
If true, display date in the clock, in addition to time.
|
|
||||||
</_description>
|
|
||||||
</key>
|
|
||||||
</schema>
|
|
||||||
|
|
||||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
||||||
gettext-domain="@GETTEXT_PACKAGE@">
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
<key name="framerate" type="i">
|
<key name="framerate" type="i">
|
||||||
@ -129,7 +132,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
take care of its own output - this might be used to send the output
|
take care of its own output - this might be used to send the output
|
||||||
to an icecast server via shout2send or similar. When unset or set
|
to an icecast server via shout2send or similar. When unset or set
|
||||||
to an empty value, the default pipeline will be used. This is currently
|
to an empty value, the default pipeline will be used. This is currently
|
||||||
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
|
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
||||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||||
for a guess at the optimal thread count on the system.
|
for a guess at the optimal thread count on the system.
|
||||||
</_description>
|
</_description>
|
@ -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:xlink="http://www.w3.org/1999/xlink"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="28"
|
width="29"
|
||||||
height="25"
|
height="29"
|
||||||
id="svg10621"
|
id="svg10621"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.48.1 r9760"
|
inkscape:version="0.48.2 r9819"
|
||||||
sodipodi:docname="calendar-today.svg">
|
sodipodi:docname="calendar-today.svg">
|
||||||
<defs
|
<defs
|
||||||
id="defs10623">
|
id="defs10623">
|
||||||
@ -118,6 +118,17 @@
|
|||||||
fx="51"
|
fx="51"
|
||||||
fy="30"
|
fy="30"
|
||||||
r="42" />
|
r="42" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34508-1-3"
|
||||||
|
id="radialGradient3113"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||||
|
cx="51"
|
||||||
|
cy="30"
|
||||||
|
fx="51"
|
||||||
|
fy="30"
|
||||||
|
r="42" />
|
||||||
</defs>
|
</defs>
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
id="base"
|
id="base"
|
||||||
@ -127,20 +138,29 @@
|
|||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="15.839192"
|
inkscape:zoom="15.839192"
|
||||||
inkscape:cx="8.3750933"
|
inkscape:cx="20.652108"
|
||||||
inkscape:cy="8.0837211"
|
inkscape:cy="11.839084"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="true"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:window-width="1440"
|
inkscape:window-width="1280"
|
||||||
inkscape:window-height="843"
|
inkscape:window-height="741"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="26"
|
inkscape:window-y="27"
|
||||||
inkscape:window-maximized="1" />
|
inkscape:window-maximized="1"
|
||||||
|
borderlayer="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid3109"
|
||||||
|
empspacing="5"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true" />
|
||||||
|
</sodipodi:namedview>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata10626">
|
id="metadata10626">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
@ -157,31 +177,28 @@
|
|||||||
inkscape:label="Layer 1"
|
inkscape:label="Layer 1"
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
id="layer1"
|
id="layer1"
|
||||||
transform="translate(-469.08263,-536.99307)">
|
transform="translate(-469.08263,-532.99307)">
|
||||||
<g
|
|
||||||
id="g3003">
|
|
||||||
<path
|
<path
|
||||||
inkscape:export-ydpi="90"
|
sodipodi:type="arc"
|
||||||
inkscape:export-xdpi="90"
|
style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
inkscape:export-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"
|
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:cx="51"
|
||||||
sodipodi:type="arc" />
|
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
|
<rect
|
||||||
y="558.85046"
|
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
x="468.96878"
|
|
||||||
height="3.1425927"
|
|
||||||
width="28.149134"
|
|
||||||
id="rect2996"
|
id="rect2996"
|
||||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
width="31"
|
||||||
</g>
|
height="3"
|
||||||
|
x="468.08264"
|
||||||
|
y="558.99304" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</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 |
BIN
data/theme/noise-texture.png
Normal file
After Width: | Height: | Size: 78 KiB |
@ -9,7 +9,7 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="21"
|
width="17"
|
||||||
height="10"
|
height="10"
|
||||||
id="svg2"
|
id="svg2"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
@ -66,9 +66,9 @@
|
|||||||
<rect
|
<rect
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
id="rect3796"
|
id="rect3796"
|
||||||
width="3"
|
width="7"
|
||||||
height="2"
|
height="2"
|
||||||
x="9"
|
x="5"
|
||||||
y="8" />
|
y="8" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -1,64 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="10"
|
|
||||||
height="4"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47 r22583"
|
|
||||||
sodipodi:docname="scroll-hhandle.svg">
|
|
||||||
<defs
|
|
||||||
id="defs4">
|
|
||||||
</defs>
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
|
||||||
id="rect3592"
|
|
||||||
width="2"
|
|
||||||
height="4"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#rect3592"
|
|
||||||
id="use2825"
|
|
||||||
transform="translate(8,0)"
|
|
||||||
width="10"
|
|
||||||
height="4" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#use2825"
|
|
||||||
id="use2827"
|
|
||||||
transform="translate(-4,0)"
|
|
||||||
width="10"
|
|
||||||
height="4" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,62 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="4"
|
|
||||||
height="10"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47 r22583"
|
|
||||||
sodipodi:docname="scroll-hhandle.svg">
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
|
||||||
id="rect3592"
|
|
||||||
width="2"
|
|
||||||
height="4"
|
|
||||||
x="0"
|
|
||||||
y="-4"
|
|
||||||
rx="0"
|
|
||||||
ry="0"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#rect3592"
|
|
||||||
id="use3705"
|
|
||||||
transform="translate(0,4)"
|
|
||||||
width="4"
|
|
||||||
height="10" />
|
|
||||||
<use
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
xlink:href="#use3705"
|
|
||||||
id="use3707"
|
|
||||||
transform="translate(0,4)"
|
|
||||||
width="4"
|
|
||||||
height="10" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
120
data/theme/summary-counter.svg
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
id="Foreground"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 23.272727 23.272727"
|
||||||
|
enable-background="new 0 0 16 16"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.48.2 r9819"
|
||||||
|
sodipodi:docname="summary-counter.svg"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||||
|
id="metadata2399"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs2397"><linearGradient
|
||||||
|
id="linearGradient3173"><stop
|
||||||
|
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3175" /><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3177" /></linearGradient><inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 8 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="16 : 8 : 1"
|
||||||
|
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||||
|
id="perspective2401" /><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter16494-4"
|
||||||
|
x="-0.20989846"
|
||||||
|
width="1.4197969"
|
||||||
|
y="-0.20903821"
|
||||||
|
height="1.4180764"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.3282637"
|
||||||
|
id="feGaussianBlur16496-8" /></filter><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient16498-6"
|
||||||
|
id="radialGradient16504-1"
|
||||||
|
cx="7.6582627"
|
||||||
|
cy="5.8191104"
|
||||||
|
fx="7.6582627"
|
||||||
|
fy="5.8191104"
|
||||||
|
r="8.6928644"
|
||||||
|
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient16498-6"><stop
|
||||||
|
style="stop-color:#9FD0FF;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop16500-8" /><stop
|
||||||
|
style="stop-color:#3465A4;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
inkscape:window-height="709"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
guidetolerance="10.0"
|
||||||
|
gridtolerance="10.0"
|
||||||
|
objecttolerance="10.0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#000000"
|
||||||
|
id="base"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="11.313708"
|
||||||
|
inkscape:cx="15.386407"
|
||||||
|
inkscape:cy="13.739577"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="1179"
|
||||||
|
inkscape:current-layer="g16402-8"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:window-maximized="1"><inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid11246"
|
||||||
|
empspacing="5"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
|
||||||
|
style="display:inline"
|
||||||
|
id="g16402-8"
|
||||||
|
transform="translate(4.7533483,2.8238929)"><g
|
||||||
|
id="g3175-4"
|
||||||
|
transform="translate(-0.89995416,0.94028614)"><path
|
||||||
|
sodipodi:type="inkscape:offset"
|
||||||
|
inkscape:radius="0"
|
||||||
|
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
|
||||||
|
xlink:href="#path2394-32"
|
||||||
|
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
|
||||||
|
id="path16480-5"
|
||||||
|
inkscape:href="#path2394-32"
|
||||||
|
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
|
||||||
|
transform="translate(0,1.028519)" /><path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
|
||||||
|
id="path2394-32"
|
||||||
|
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
|
sodipodi:nodetypes="csssc"
|
||||||
|
inkscape:connector-curvature="0" /><g
|
||||||
|
id="g3172-6" /></g></g></svg>
|
After Width: | Height: | Size: 5.4 KiB |
@ -9,7 +9,7 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="64"
|
width="65"
|
||||||
height="22"
|
height="22"
|
||||||
id="svg3273"
|
id="svg3273"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
@ -9,7 +9,7 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="64"
|
width="65"
|
||||||
height="22"
|
height="22"
|
||||||
id="svg3012"
|
id="svg3012"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
BIN
data/theme/ws-switch-arrow-down.png
Normal file
After Width: | Height: | Size: 850 B |
@ -1,376 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
version="1.1"
|
|
||||||
width="96"
|
|
||||||
height="96"
|
|
||||||
id="svg25070"
|
|
||||||
inkscape:version="0.48.0 r9654"
|
|
||||||
sodipodi:docname="ws-switch-arrow-down.svg">
|
|
||||||
<metadata
|
|
||||||
id="metadata3353">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="718"
|
|
||||||
inkscape:window-height="480"
|
|
||||||
id="namedview3351"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="2.6979167"
|
|
||||||
inkscape:cx="48"
|
|
||||||
inkscape:cy="48"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg25070" />
|
|
||||||
<defs
|
|
||||||
id="defs25072">
|
|
||||||
<linearGradient
|
|
||||||
x1="-86.552246"
|
|
||||||
y1="185.439"
|
|
||||||
x2="-83.37072"
|
|
||||||
y2="197.31261"
|
|
||||||
id="linearGradient24957"
|
|
||||||
xlink:href="#linearGradient4034-0-4"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(6,0)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4034-0-4">
|
|
||||||
<stop
|
|
||||||
id="stop4036-5-7"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4038-9-6"
|
|
||||||
style="stop-color:#babdb6;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter24765">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix24767" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix24769" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
x1="-74.520325"
|
|
||||||
y1="169.06032"
|
|
||||||
x2="-74.520325"
|
|
||||||
y2="205.94189"
|
|
||||||
id="linearGradient24955"
|
|
||||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-5,0)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4632-1-3-9-3-2">
|
|
||||||
<stop
|
|
||||||
id="stop4634-1-8-3-9-0"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4636-1-9-9-8-8"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0.0274937" />
|
|
||||||
<stop
|
|
||||||
id="stop4638-8-3-9-6-6"
|
|
||||||
style="stop-color:#f2f2f2;stop-opacity:1"
|
|
||||||
offset="0.274937" />
|
|
||||||
<stop
|
|
||||||
id="stop4640-8-5-7-8-9"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0.38707438" />
|
|
||||||
<stop
|
|
||||||
id="stop4642-5-41-9-6-9"
|
|
||||||
style="stop-color:#d9dad8;stop-opacity:1"
|
|
||||||
offset="0.66528589" />
|
|
||||||
<stop
|
|
||||||
id="stop4644-5-2-7-9-2"
|
|
||||||
style="stop-color:#dfe0dd;stop-opacity:1"
|
|
||||||
offset="0.76745707" />
|
|
||||||
<stop
|
|
||||||
id="stop4646-3-2-3-7-3"
|
|
||||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
r="2.3554697"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
id="radialGradient24959"
|
|
||||||
xlink:href="#linearGradient4869-4-1"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-1">
|
|
||||||
<stop
|
|
||||||
id="stop4871-6-2"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-4"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0.31807542" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-1"
|
|
||||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
|
||||||
offset="0.74691135" />
|
|
||||||
<stop
|
|
||||||
id="stop4873-1-0"
|
|
||||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25011">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25013" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25015" />
|
|
||||||
</filter>
|
|
||||||
<radialGradient
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
r="2.3554697"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
id="radialGradient24961"
|
|
||||||
xlink:href="#linearGradient4869-4-0"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-0">
|
|
||||||
<stop
|
|
||||||
id="stop4871-6-8"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-5"
|
|
||||||
style="stop-color:#eeeeec;stop-opacity:1"
|
|
||||||
offset="0.31807542" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-5"
|
|
||||||
style="stop-color:#c8c9c6;stop-opacity:1"
|
|
||||||
offset="0.74691135" />
|
|
||||||
<stop
|
|
||||||
id="stop4873-1-4"
|
|
||||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25023">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25025" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25027" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898"
|
|
||||||
id="linearGradient24963"
|
|
||||||
xlink:href="#linearGradient4941"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4941">
|
|
||||||
<stop
|
|
||||||
id="stop4943"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4945"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25033">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25035" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25037" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898"
|
|
||||||
id="linearGradient24965"
|
|
||||||
xlink:href="#linearGradient4941-7"
|
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4941-7">
|
|
||||||
<stop
|
|
||||||
id="stop4943-2"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="0" />
|
|
||||||
<stop
|
|
||||||
id="stop4945-5"
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25043">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25045" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25047" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25049">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25051" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25053" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
id="filter25055">
|
|
||||||
<feColorMatrix
|
|
||||||
result="fbSourceGraphic"
|
|
||||||
values="1"
|
|
||||||
type="saturate"
|
|
||||||
id="feColorMatrix25057" />
|
|
||||||
<feColorMatrix
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
id="feColorMatrix25059" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
|
|
||||||
id="layer1">
|
|
||||||
<g
|
|
||||||
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
|
|
||||||
id="g4030-1-8"
|
|
||||||
style="stroke:#000000;stroke-opacity:1;display:inline">
|
|
||||||
<path
|
|
||||||
d="m -72.5,173.5 -14,14 14,14"
|
|
||||||
id="path3165-7-3"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
|
|
||||||
id="path4050-2-7-9-4"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
|
|
||||||
id="path4050-2-7-9-4-8"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
|
|
||||||
id="path4050-2-7-9-4-0"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
|
||||||
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
|
|
||||||
id="path4050-2-7-9-4-0-9"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
|
|
||||||
id="path3165-7-3-1"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
|
|
||||||
id="path3165-7-3-1-9"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 13 KiB |
BIN
data/theme/ws-switch-arrow-up.png
Normal file
After Width: | Height: | Size: 841 B |
@ -1,447 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="96"
|
|
||||||
height="96"
|
|
||||||
id="svg25070"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.48.0 r9654"
|
|
||||||
sodipodi:docname="ws-switch-arrow-up.svg">
|
|
||||||
<defs
|
|
||||||
id="defs25072">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 24 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="48 : 24 : 1"
|
|
||||||
inkscape:persp3d-origin="24 : 16 : 1"
|
|
||||||
id="perspective25078" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective24985"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4034-0-4"
|
|
||||||
id="linearGradient24957"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(6)"
|
|
||||||
x1="-86.552246"
|
|
||||||
y1="185.439"
|
|
||||||
x2="-83.37072"
|
|
||||||
y2="197.31261" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4034-0-4">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4036-5-7" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4038-9-6" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter24765"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix24767"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix24769"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
|
||||||
id="linearGradient24955"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-5)"
|
|
||||||
x1="-74.520325"
|
|
||||||
y1="169.06032"
|
|
||||||
x2="-74.520325"
|
|
||||||
y2="205.94189" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4632-1-3-9-3-2">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4634-1-8-3-9-0" />
|
|
||||||
<stop
|
|
||||||
id="stop4636-1-9-9-8-8"
|
|
||||||
offset="0.0274937"
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4638-8-3-9-6-6"
|
|
||||||
offset="0.274937"
|
|
||||||
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4640-8-5-7-8-9"
|
|
||||||
offset="0.38707438"
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4642-5-41-9-6-9"
|
|
||||||
offset="0.66528589"
|
|
||||||
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4644-5-2-7-9-2"
|
|
||||||
offset="0.76745707"
|
|
||||||
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4646-3-2-3-7-3" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4869-4-1"
|
|
||||||
id="radialGradient24959"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
r="2.3554697" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-1">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4871-6-2" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-4"
|
|
||||||
offset="0.31807542"
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-1"
|
|
||||||
offset="0.74691135"
|
|
||||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4873-1-0" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25011"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25013"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25015"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<radialGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4869-4-0"
|
|
||||||
id="radialGradient24961"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
|
||||||
cx="-33.412369"
|
|
||||||
cy="185.74171"
|
|
||||||
fx="-33.412369"
|
|
||||||
fy="185.74171"
|
|
||||||
r="2.3554697" />
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4869-4-0">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4871-6-8" />
|
|
||||||
<stop
|
|
||||||
id="stop4879-7-5"
|
|
||||||
offset="0.31807542"
|
|
||||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
id="stop4877-6-5"
|
|
||||||
offset="0.74691135"
|
|
||||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4873-1-4" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25023"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25025"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25027"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4941"
|
|
||||||
id="linearGradient24963"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4941">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4943" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4945" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25033"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25035"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25037"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4941-7"
|
|
||||||
id="linearGradient24965"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="-39.858727"
|
|
||||||
y1="184.61784"
|
|
||||||
x2="-38.244785"
|
|
||||||
y2="188.84898" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4941-7">
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4943-2" />
|
|
||||||
<stop
|
|
||||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4945-5" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
id="filter25043"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25045"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25047"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
id="filter25049"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25051"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25053"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
id="filter25055"
|
|
||||||
inkscape:label="Invert"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
inkscape:menu="Color"
|
|
||||||
inkscape:menu-tooltip="Invert colors"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25057"
|
|
||||||
type="saturate"
|
|
||||||
values="1"
|
|
||||||
result="fbSourceGraphic" />
|
|
||||||
<feColorMatrix
|
|
||||||
id="feColorMatrix25059"
|
|
||||||
in="fbSourceGraphic"
|
|
||||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="2.8284271"
|
|
||||||
inkscape:cx="-12.356322"
|
|
||||||
inkscape:cy="57.536221"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1200"
|
|
||||||
inkscape:window-height="840"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="0" />
|
|
||||||
<metadata
|
|
||||||
id="metadata25075">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0, 48)">
|
|
||||||
<g
|
|
||||||
id="g3181"
|
|
||||||
transform="matrix(0,1,-1,0,48.0003,-48)">
|
|
||||||
<g
|
|
||||||
style="stroke:#000000;stroke-opacity:1;display:inline"
|
|
||||||
transform="matrix(2,0,0,2,193.25,-374.967)"
|
|
||||||
id="g4030-1-8">
|
|
||||||
<path
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
d="m -72.5,173.5 -14,14 14,14"
|
|
||||||
id="path3165-7-3"
|
|
||||||
sodipodi:nodetypes="ccc"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4-8"
|
|
||||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4-0"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
|
|
||||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
|
||||||
sodipodi:ry="2.09375"
|
|
||||||
sodipodi:rx="2.09375"
|
|
||||||
sodipodi:cy="186.40625"
|
|
||||||
sodipodi:cx="-38.59375"
|
|
||||||
id="path4050-2-7-9-4-0-9"
|
|
||||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
|
||||||
sodipodi:type="arc" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
|
||||||
sodipodi:nodetypes="ccccscccsc"
|
|
||||||
id="path3165-7-3-1"
|
|
||||||
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
|
||||||
sodipodi:nodetypes="ccccccc"
|
|
||||||
id="path3165-7-3-1-9"
|
|
||||||
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
|
|
||||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 16 KiB |
@ -68,6 +68,10 @@ IGNORE_HFILES= \
|
|||||||
gactionobserver.h \
|
gactionobserver.h \
|
||||||
shell-recorder-src.h
|
shell-recorder-src.h
|
||||||
|
|
||||||
|
if !BUILD_RECORDER
|
||||||
|
IGNORE_HFILES += shell-recorder.h
|
||||||
|
endif
|
||||||
|
|
||||||
# Images to copy into HTML directory.
|
# Images to copy into HTML directory.
|
||||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
||||||
HTML_IMAGES=
|
HTML_IMAGES=
|
||||||
|
@ -29,8 +29,6 @@
|
|||||||
<chapter>
|
<chapter>
|
||||||
<title>Search</title>
|
<title>Search</title>
|
||||||
<xi:include href="xml/shell-app-system.xml"/>
|
<xi:include href="xml/shell-app-system.xml"/>
|
||||||
<xi:include href="xml/shell-contact-system.xml"/>
|
|
||||||
<xi:include href="xml/shell-doc-system.xml"/>
|
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter>
|
<chapter>
|
||||||
<title>Tray Icons</title>
|
<title>Tray Icons</title>
|
||||||
@ -42,7 +40,6 @@
|
|||||||
<chapter>
|
<chapter>
|
||||||
<title>Recorder</title>
|
<title>Recorder</title>
|
||||||
<xi:include href="xml/shell-recorder.xml"/>
|
<xi:include href="xml/shell-recorder.xml"/>
|
||||||
<xi:include href="xml/shell-recorder-src.xml"/>
|
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter>
|
<chapter>
|
||||||
<title>Integration helpers and utilities</title>
|
<title>Integration helpers and utilities</title>
|
||||||
|
@ -66,4 +66,11 @@ its dependencies to build from tarballs.</description>
|
|||||||
<gnome:userid>marinaz</gnome:userid>
|
<gnome:userid>marinaz</gnome:userid>
|
||||||
</foaf:Person>
|
</foaf:Person>
|
||||||
</maintainer>
|
</maintainer>
|
||||||
|
<maintainer>
|
||||||
|
<foaf:Person>
|
||||||
|
<foaf:name>Florian Müllner</foaf:name>
|
||||||
|
<foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
|
||||||
|
<gnome:userid>fmuellner</gnome:userid>
|
||||||
|
</foaf:Person>
|
||||||
|
</maintainer>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
NULL =
|
||||||
|
|
||||||
EXTRA_DIST = misc/config.js.in
|
EXTRA_DIST = misc/config.js.in
|
||||||
CLEANFILES = misc/config.js
|
CLEANFILES = misc/config.js
|
||||||
@ -6,9 +7,7 @@ misc/config.js: misc/config.js.in Makefile
|
|||||||
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
||||||
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
|
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
|
||||||
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
|
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
|
||||||
-e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
|
|
||||||
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
|
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
|
||||||
-e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \
|
|
||||||
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
|
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
|
||||||
-e "s|[@]datadir@|$(datadir)|g" \
|
-e "s|[@]datadir@|$(datadir)|g" \
|
||||||
-e "s|[@]libexecdir@|$(libexecdir)|g" \
|
-e "s|[@]libexecdir@|$(libexecdir)|g" \
|
||||||
@ -19,78 +18,75 @@ jsdir = $(pkgdatadir)/js
|
|||||||
|
|
||||||
nobase_dist_js_DATA = \
|
nobase_dist_js_DATA = \
|
||||||
gdm/batch.js \
|
gdm/batch.js \
|
||||||
gdm/consoleKit.js \
|
|
||||||
gdm/fingerprint.js \
|
gdm/fingerprint.js \
|
||||||
gdm/loginDialog.js \
|
gdm/loginDialog.js \
|
||||||
gdm/powerMenu.js \
|
gdm/powerMenu.js \
|
||||||
gdm/systemd.js \
|
gdm/realmd.js \
|
||||||
|
gdm/util.js \
|
||||||
extensionPrefs/main.js \
|
extensionPrefs/main.js \
|
||||||
misc/config.js \
|
misc/config.js \
|
||||||
misc/extensionUtils.js \
|
misc/extensionUtils.js \
|
||||||
misc/fileUtils.js \
|
misc/fileUtils.js \
|
||||||
misc/format.js \
|
|
||||||
misc/gnomeSession.js \
|
misc/gnomeSession.js \
|
||||||
misc/history.js \
|
misc/history.js \
|
||||||
misc/jsParse.js \
|
misc/jsParse.js \
|
||||||
|
misc/loginManager.js \
|
||||||
misc/modemManager.js \
|
misc/modemManager.js \
|
||||||
misc/params.js \
|
misc/params.js \
|
||||||
misc/screenSaver.js \
|
|
||||||
misc/util.js \
|
misc/util.js \
|
||||||
perf/core.js \
|
perf/core.js \
|
||||||
ui/altTab.js \
|
ui/altTab.js \
|
||||||
ui/appDisplay.js \
|
ui/appDisplay.js \
|
||||||
ui/appFavorites.js \
|
ui/appFavorites.js \
|
||||||
ui/automountManager.js \
|
|
||||||
ui/autorunManager.js \
|
|
||||||
ui/boxpointer.js \
|
ui/boxpointer.js \
|
||||||
ui/calendar.js \
|
ui/calendar.js \
|
||||||
ui/checkBox.js \
|
ui/checkBox.js \
|
||||||
ui/contactDisplay.js \
|
|
||||||
ui/ctrlAltTab.js \
|
ui/ctrlAltTab.js \
|
||||||
ui/dash.js \
|
ui/dash.js \
|
||||||
ui/dateMenu.js \
|
ui/dateMenu.js \
|
||||||
ui/dnd.js \
|
ui/dnd.js \
|
||||||
ui/endSessionDialog.js \
|
ui/endSessionDialog.js \
|
||||||
ui/environment.js \
|
|
||||||
ui/extensionSystem.js \
|
ui/extensionSystem.js \
|
||||||
|
ui/extensionDownloader.js \
|
||||||
|
ui/environment.js \
|
||||||
ui/flashspot.js \
|
ui/flashspot.js \
|
||||||
|
ui/ibusCandidatePopup.js\
|
||||||
|
ui/grabHelper.js \
|
||||||
ui/iconGrid.js \
|
ui/iconGrid.js \
|
||||||
ui/keyboard.js \
|
ui/keyboard.js \
|
||||||
ui/keyringPrompt.js \
|
|
||||||
ui/layout.js \
|
ui/layout.js \
|
||||||
ui/lightbox.js \
|
ui/lightbox.js \
|
||||||
ui/link.js \
|
|
||||||
ui/lookingGlass.js \
|
ui/lookingGlass.js \
|
||||||
ui/magnifier.js \
|
ui/magnifier.js \
|
||||||
ui/magnifierDBus.js \
|
ui/magnifierDBus.js \
|
||||||
ui/main.js \
|
ui/main.js \
|
||||||
ui/messageTray.js \
|
ui/messageTray.js \
|
||||||
ui/modalDialog.js \
|
ui/modalDialog.js \
|
||||||
ui/networkAgent.js \
|
ui/sessionMode.js \
|
||||||
ui/shellEntry.js \
|
ui/shellEntry.js \
|
||||||
ui/shellMountOperation.js \
|
ui/shellMountOperation.js \
|
||||||
ui/notificationDaemon.js \
|
ui/notificationDaemon.js \
|
||||||
ui/overview.js \
|
ui/overview.js \
|
||||||
ui/panel.js \
|
ui/panel.js \
|
||||||
ui/panelMenu.js \
|
ui/panelMenu.js \
|
||||||
ui/placeDisplay.js \
|
ui/pointerWatcher.js \
|
||||||
ui/polkitAuthenticationAgent.js \
|
|
||||||
ui/popupMenu.js \
|
ui/popupMenu.js \
|
||||||
ui/remoteSearch.js \
|
ui/remoteSearch.js \
|
||||||
ui/runDialog.js \
|
ui/runDialog.js \
|
||||||
|
ui/screenShield.js \
|
||||||
ui/scripting.js \
|
ui/scripting.js \
|
||||||
ui/search.js \
|
ui/search.js \
|
||||||
ui/searchDisplay.js \
|
ui/searchDisplay.js \
|
||||||
ui/shellDBus.js \
|
ui/shellDBus.js \
|
||||||
ui/statusIconDispatcher.js \
|
|
||||||
ui/status/accessibility.js \
|
ui/status/accessibility.js \
|
||||||
ui/status/keyboard.js \
|
ui/status/keyboard.js \
|
||||||
|
ui/status/lockScreenMenu.js \
|
||||||
ui/status/network.js \
|
ui/status/network.js \
|
||||||
ui/status/power.js \
|
ui/status/power.js \
|
||||||
ui/status/volume.js \
|
ui/status/volume.js \
|
||||||
ui/status/bluetooth.js \
|
ui/status/bluetooth.js \
|
||||||
ui/telepathyClient.js \
|
|
||||||
ui/tweener.js \
|
ui/tweener.js \
|
||||||
|
ui/unlockDialog.js \
|
||||||
ui/userMenu.js \
|
ui/userMenu.js \
|
||||||
ui/viewSelector.js \
|
ui/viewSelector.js \
|
||||||
ui/wanda.js \
|
ui/wanda.js \
|
||||||
@ -100,4 +96,13 @@ nobase_dist_js_DATA = \
|
|||||||
ui/workspaceThumbnail.js \
|
ui/workspaceThumbnail.js \
|
||||||
ui/workspacesView.js \
|
ui/workspacesView.js \
|
||||||
ui/workspaceSwitcherPopup.js \
|
ui/workspaceSwitcherPopup.js \
|
||||||
ui/xdndHandler.js
|
ui/xdndHandler.js \
|
||||||
|
ui/components/__init__.js \
|
||||||
|
ui/components/autorunManager.js \
|
||||||
|
ui/components/automountManager.js \
|
||||||
|
ui/components/networkAgent.js \
|
||||||
|
ui/components/polkitAgent.js \
|
||||||
|
ui/components/recorder.js \
|
||||||
|
ui/components/telepathyClient.js \
|
||||||
|
ui/components/keyring.js \
|
||||||
|
$(NULL)
|
||||||
|
@ -6,15 +6,14 @@ const GObject = imports.gi.GObject;
|
|||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
|
const Format = imports.format;
|
||||||
|
|
||||||
const _ = Gettext.gettext;
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const Format = imports.misc.format;
|
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
|
||||||
|
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
||||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
|
||||||
<signal name="ExtensionStatusChanged">
|
<signal name="ExtensionStatusChanged">
|
||||||
<arg type="s" name="uuid"/>
|
<arg type="s" name="uuid"/>
|
||||||
<arg type="i" name="state"/>
|
<arg type="i" name="state"/>
|
||||||
@ -162,7 +161,7 @@ const Application = new Lang.Class({
|
|||||||
vbox.add(toolbar);
|
vbox.add(toolbar);
|
||||||
let toolitem;
|
let toolitem;
|
||||||
|
|
||||||
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
|
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
|
||||||
use_markup: true });
|
use_markup: true });
|
||||||
toolitem = new Gtk.ToolItem({ child: label });
|
toolitem = new Gtk.ToolItem({ child: label });
|
||||||
toolbar.add(toolitem);
|
toolbar.add(toolitem);
|
||||||
@ -202,24 +201,18 @@ const Application = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_scanExtensions: function() {
|
_scanExtensions: function() {
|
||||||
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
|
let finder = new ExtensionUtils.ExtensionFinder();
|
||||||
if (ExtensionUtils.extensions[uuid] !== undefined)
|
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||||
return;
|
finder.scanExtensions();
|
||||||
|
|
||||||
let extension;
|
|
||||||
try {
|
|
||||||
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
|
||||||
} catch(e) {
|
|
||||||
global.logError('' + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let iter = this._model.append();
|
|
||||||
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
|
|
||||||
this._extensionIters[uuid] = iter;
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_extensionFound: function(signals, extension) {
|
||||||
|
let iter = this._model.append();
|
||||||
|
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||||
|
this._extensionIters[extension.uuid] = iter;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
_onActivate: function() {
|
_onActivate: function() {
|
||||||
this._window.present();
|
this._window.present();
|
||||||
},
|
},
|
||||||
@ -257,7 +250,7 @@ function initEnvironment() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
logError: function(s) {
|
logError: function(s) {
|
||||||
global.log('ERROR: ' + s);
|
log('ERROR: ' + s);
|
||||||
},
|
},
|
||||||
|
|
||||||
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
|
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
|
||||||
@ -268,7 +261,6 @@ function initEnvironment() {
|
|||||||
|
|
||||||
function main(argv) {
|
function main(argv) {
|
||||||
initEnvironment();
|
initEnvironment();
|
||||||
ExtensionUtils.init();
|
|
||||||
|
|
||||||
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
||||||
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
|
|
||||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
|
|
||||||
<method name='CanRestart'>
|
|
||||||
<arg type='b' direction='out'/>
|
|
||||||
</method>
|
|
||||||
<method name='CanStop'>
|
|
||||||
<arg type='b' direction='out'/>
|
|
||||||
</method>
|
|
||||||
<method name='Restart' />
|
|
||||||
<method name='Stop' />
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
|
||||||
|
|
||||||
function ConsoleKitManager() {
|
|
||||||
return new ConsoleKitProxy(Gio.DBus.system,
|
|
||||||
'org.freedesktop.ConsoleKit',
|
|
||||||
'/org/freedesktop/ConsoleKit/Manager');
|
|
||||||
};
|
|
@ -11,10 +11,16 @@ const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
|
|||||||
</method>
|
</method>
|
||||||
</interface>;
|
</interface>;
|
||||||
|
|
||||||
const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
|
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
|
||||||
|
|
||||||
function FprintManager() {
|
function FprintManager() {
|
||||||
return new FprintManagerProxy(Gio.DBus.system,
|
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
||||||
'net.reactivated.Fprint',
|
g_interface_name: FprintManagerInfo.name,
|
||||||
'/net/reactivated/Fprint/Manager');
|
g_interface_info: FprintManagerInfo,
|
||||||
};
|
g_name: 'net.reactivated.Fprint',
|
||||||
|
g_object_path: '/net/reactivated/Fprint/Manager',
|
||||||
|
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||||
|
|
||||||
|
self.init(null);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
@ -30,83 +30,24 @@ const Pango = imports.gi.Pango;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const GdmGreeter = imports.gi.GdmGreeter;
|
const Gdm = imports.gi.Gdm;
|
||||||
|
|
||||||
const Batch = imports.gdm.batch;
|
const Batch = imports.gdm.batch;
|
||||||
const Fprint = imports.gdm.fingerprint;
|
const Fprint = imports.gdm.fingerprint;
|
||||||
|
const GdmUtil = imports.gdm.util;
|
||||||
const Lightbox = imports.ui.lightbox;
|
const Lightbox = imports.ui.lightbox;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
const UserMenu = imports.ui.userMenu;
|
||||||
|
|
||||||
const _PASSWORD_SERVICE_NAME = 'gdm-password';
|
|
||||||
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
|
||||||
const _FADE_ANIMATION_TIME = 0.16;
|
|
||||||
const _RESIZE_ANIMATION_TIME = 0.25;
|
const _RESIZE_ANIMATION_TIME = 0.25;
|
||||||
const _SCROLL_ANIMATION_TIME = 2.0;
|
const _SCROLL_ANIMATION_TIME = 0.5;
|
||||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||||
const _LOGO_ICON_NAME_SIZE = 48;
|
const _LOGO_ICON_NAME_SIZE = 48;
|
||||||
|
|
||||||
const _LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
|
||||||
const _FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
|
||||||
|
|
||||||
const _LOGO_KEY = 'logo';
|
|
||||||
|
|
||||||
let _loginDialog = null;
|
let _loginDialog = null;
|
||||||
|
|
||||||
function _fadeInActor(actor) {
|
|
||||||
let hold = new Batch.Hold();
|
|
||||||
|
|
||||||
if (actor.opacity == 255 && actor.visible)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
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() {
|
|
||||||
actor.set_height(-1);
|
|
||||||
hold.release();
|
|
||||||
},
|
|
||||||
onCompleteScope: this
|
|
||||||
});
|
|
||||||
return hold;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _fadeOutActor(actor) {
|
|
||||||
let hold = new Batch.Hold();
|
|
||||||
|
|
||||||
if (!actor.visible) {
|
|
||||||
actor.opacity = 0;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actor.opacity == 0) {
|
|
||||||
actor.hide();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tweener.addTween(actor,
|
|
||||||
{ opacity: 0,
|
|
||||||
height: 0,
|
|
||||||
time: _FADE_ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
onComplete: function() {
|
|
||||||
actor.hide();
|
|
||||||
actor.set_height(-1);
|
|
||||||
hold.release();
|
|
||||||
},
|
|
||||||
onCompleteScope: this
|
|
||||||
});
|
|
||||||
return hold;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _smoothlyResizeActor(actor, width, height) {
|
function _smoothlyResizeActor(actor, width, height) {
|
||||||
let finalWidth;
|
let finalWidth;
|
||||||
let finalHeight;
|
let finalHeight;
|
||||||
@ -148,95 +89,60 @@ const UserListItem = new Lang.Class({
|
|||||||
this._userChangedId = this.user.connect('changed',
|
this._userChangedId = this.user.connect('changed',
|
||||||
Lang.bind(this, this._onUserChanged));
|
Lang.bind(this, this._onUserChanged));
|
||||||
|
|
||||||
this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout',
|
let layout = new St.BoxLayout({ vertical: false });
|
||||||
vertical: true });
|
|
||||||
|
|
||||||
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
|
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
child: this._verticalBox,
|
child: layout,
|
||||||
reactive: true,
|
reactive: true,
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
let layout = new St.BoxLayout({ vertical: false });
|
|
||||||
|
|
||||||
this._verticalBox.add(layout,
|
this._userAvatar = new UserMenu.UserAvatarWidget(this.user,
|
||||||
{ y_fill: true,
|
{ styleClass: 'login-dialog-user-list-item-icon' });
|
||||||
x_fill: true,
|
layout.add(this._userAvatar.actor);
|
||||||
expand: true });
|
|
||||||
|
|
||||||
this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' });
|
|
||||||
this._verticalBox.add(this._focusBin,
|
|
||||||
{ x_fill: false,
|
|
||||||
x_align: St.Align.MIDDLE,
|
|
||||||
y_fill: false,
|
|
||||||
expand: true });
|
|
||||||
|
|
||||||
this._iconBin = new St.Bin();
|
|
||||||
layout.add(this._iconBin);
|
|
||||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
layout.add(textLayout,
|
layout.add(textLayout, { expand: true });
|
||||||
|
|
||||||
|
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
|
||||||
|
this.actor.label_actor = this._nameLabel;
|
||||||
|
textLayout.add(this._nameLabel,
|
||||||
{ y_fill: false,
|
{ y_fill: false,
|
||||||
y_align: St.Align.MIDDLE,
|
y_align: St.Align.MIDDLE,
|
||||||
expand: true });
|
expand: true });
|
||||||
|
|
||||||
this._nameLabel = new St.Label({ text: this.user.get_real_name(),
|
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
|
||||||
style_class: 'login-dialog-user-list-item-name' });
|
scale_x: 0 });
|
||||||
textLayout.add(this._nameLabel);
|
textLayout.add(this._timedLoginIndicator,
|
||||||
|
{ x_fill: true,
|
||||||
this._updateIcon();
|
x_align: St.Align.MIDDLE,
|
||||||
|
y_fill: false,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
this._onUserChanged();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onUserChanged: function() {
|
_onUserChanged: function() {
|
||||||
this._nameLabel.set_text(this.user.get_real_name());
|
this._nameLabel.set_text(this.user.get_real_name());
|
||||||
this._updateIcon();
|
this._userAvatar.update();
|
||||||
|
this._updateLoggedIn();
|
||||||
},
|
},
|
||||||
|
|
||||||
_setIconFromFile: function(iconFile, styleClass) {
|
syncStyleClasses: function() {
|
||||||
if (styleClass)
|
this._updateLoggedIn();
|
||||||
this._iconBin.set_style_class_name(styleClass);
|
|
||||||
this._iconBin.set_style(null);
|
|
||||||
|
|
||||||
this._iconBin.child = null;
|
if (global.stage.get_key_focus() == this.actor)
|
||||||
if (iconFile) {
|
this.actor.add_style_pseudo_class('focus');
|
||||||
this._iconBin.show();
|
|
||||||
// We use background-image instead of, say, St.TextureCache
|
|
||||||
// so the theme writers can add a rounded frame around the image
|
|
||||||
// and so theme writers can pick the icon size.
|
|
||||||
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 icon = new St.Icon();
|
|
||||||
icon.set_icon_name(iconName)
|
|
||||||
|
|
||||||
this._iconBin.child = icon;
|
|
||||||
this._iconBin.show();
|
|
||||||
} else {
|
|
||||||
this._iconBin.child = null;
|
|
||||||
this._iconBin.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateIcon: function() {
|
|
||||||
let iconFileName = this.user.get_icon_file();
|
|
||||||
let gicon = null;
|
|
||||||
|
|
||||||
if (GLib.file_test(iconFileName, GLib.FileTest.EXISTS))
|
|
||||||
this._setIconFromFile(iconFileName, 'login-dialog-user-list-item-icon');
|
|
||||||
else
|
else
|
||||||
this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
|
this.actor.remove_style_pseudo_class('focus');
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLoggedIn: function() {
|
||||||
|
if (this.user.is_logged_in())
|
||||||
|
this.actor.add_style_pseudo_class('logged-in');
|
||||||
|
else
|
||||||
|
this.actor.remove_style_pseudo_class('logged-in');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClicked: function() {
|
_onClicked: function() {
|
||||||
@ -244,25 +150,19 @@ const UserListItem = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fadeOutName: function() {
|
fadeOutName: function() {
|
||||||
return _fadeOutActor(this._nameLabel);
|
return GdmUtil.fadeOutActor(this._nameLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
fadeInName: function() {
|
fadeInName: function() {
|
||||||
return _fadeInActor(this._nameLabel);
|
return GdmUtil.fadeInActor(this._nameLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
showFocusAnimation: function(time) {
|
showTimedLoginIndicator: function(time) {
|
||||||
let hold = new Batch.Hold();
|
let hold = new Batch.Hold();
|
||||||
|
|
||||||
let node = this.actor.get_theme_node();
|
this.hideTimedLoginIndicator();
|
||||||
let padding = node.get_horizontal_padding();
|
Tweener.addTween(this._timedLoginIndicator,
|
||||||
|
{ scale_x: 1.,
|
||||||
let box = this._verticalBox.get_allocation_box();
|
|
||||||
|
|
||||||
Tweener.removeTweens(this._focusBin);
|
|
||||||
this._focusBin.width = 0;
|
|
||||||
Tweener.addTween(this._focusBin,
|
|
||||||
{ width: (box.x2 - box.x1 - padding),
|
|
||||||
time: time,
|
time: time,
|
||||||
transition: 'linear',
|
transition: 'linear',
|
||||||
onComplete: function() {
|
onComplete: function() {
|
||||||
@ -271,6 +171,11 @@ const UserListItem = new Lang.Class({
|
|||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
return hold;
|
return hold;
|
||||||
|
},
|
||||||
|
|
||||||
|
hideTimedLoginIndicator: function() {
|
||||||
|
Tweener.removeTweens(this._timedLoginIndicator);
|
||||||
|
this._timedLoginIndicator.scale_x = 0.;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(UserListItem.prototype);
|
Signals.addSignalMethods(UserListItem.prototype);
|
||||||
@ -284,13 +189,10 @@ const UserList = new Lang.Class({
|
|||||||
Gtk.PolicyType.AUTOMATIC);
|
Gtk.PolicyType.AUTOMATIC);
|
||||||
|
|
||||||
this._box = new St.BoxLayout({ vertical: true,
|
this._box = new St.BoxLayout({ vertical: true,
|
||||||
style_class: 'login-dialog-user-list' });
|
style_class: 'login-dialog-user-list',
|
||||||
|
pseudo_class: 'expanded' });
|
||||||
|
|
||||||
this.actor.add_actor(this._box,
|
this.actor.add_actor(this._box);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
this._items = {};
|
this._items = {};
|
||||||
|
|
||||||
this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems));
|
this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems));
|
||||||
@ -310,7 +212,7 @@ const UserList = new Lang.Class({
|
|||||||
|
|
||||||
_showItem: function(item) {
|
_showItem: function(item) {
|
||||||
let tasks = [function() {
|
let tasks = [function() {
|
||||||
return _fadeInActor(item.actor);
|
return GdmUtil.fadeInActor(item.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
@ -367,17 +269,21 @@ const UserList = new Lang.Class({
|
|||||||
for (let userName in this._items) {
|
for (let userName in this._items) {
|
||||||
let item = this._items[userName];
|
let item = this._items[userName];
|
||||||
|
|
||||||
|
item.actor.set_hover(false);
|
||||||
|
item.actor.reactive = false;
|
||||||
item.actor.can_focus = false;
|
item.actor.can_focus = false;
|
||||||
item._focusBin.width = 0;
|
item.syncStyleClasses();
|
||||||
|
item._timedLoginIndicator.scale_x = 0.;
|
||||||
if (item != exception)
|
if (item != exception)
|
||||||
tasks.push(function() {
|
tasks.push(function() {
|
||||||
return _fadeOutActor(item.actor);
|
return GdmUtil.fadeOutActor(item.actor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._box.remove_style_pseudo_class('expanded');
|
||||||
let batch = new Batch.ConsecutiveBatch(this,
|
let batch = new Batch.ConsecutiveBatch(this,
|
||||||
[function() {
|
[function() {
|
||||||
return _fadeOutActor(this.actor.vscroll);
|
return GdmUtil.fadeOutActor(this.actor.vscroll);
|
||||||
},
|
},
|
||||||
|
|
||||||
new Batch.ConcurrentBatch(this, tasks)
|
new Batch.ConcurrentBatch(this, tasks)
|
||||||
@ -421,12 +327,16 @@ const UserList = new Lang.Class({
|
|||||||
|
|
||||||
for (let userName in this._items) {
|
for (let userName in this._items) {
|
||||||
let item = this._items[userName];
|
let item = this._items[userName];
|
||||||
|
item.actor.sync_hover();
|
||||||
|
item.actor.reactive = true;
|
||||||
item.actor.can_focus = true;
|
item.actor.can_focus = true;
|
||||||
|
item.syncStyleClasses();
|
||||||
tasks.push(function() {
|
tasks.push(function() {
|
||||||
return this._showItem(item);
|
return this._showItem(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._box.add_style_pseudo_class('expanded');
|
||||||
let batch = new Batch.ConsecutiveBatch(this,
|
let batch = new Batch.ConsecutiveBatch(this,
|
||||||
[function() {
|
[function() {
|
||||||
this.takeOverWhitespace();
|
this.takeOverWhitespace();
|
||||||
@ -444,7 +354,7 @@ const UserList = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
return _fadeInActor(this.actor.vscroll);
|
return GdmUtil.fadeInActor(this.actor.vscroll);
|
||||||
}]);
|
}]);
|
||||||
return batch.run();
|
return batch.run();
|
||||||
},
|
},
|
||||||
@ -459,7 +369,7 @@ const UserList = new Lang.Class({
|
|||||||
Tweener.addTween (adjustment,
|
Tweener.addTween (adjustment,
|
||||||
{ value: value,
|
{ value: value,
|
||||||
time: _SCROLL_ANIMATION_TIME,
|
time: _SCROLL_ANIMATION_TIME,
|
||||||
transition: 'linear' });
|
transition: 'easeOutQuad' });
|
||||||
},
|
},
|
||||||
|
|
||||||
jumpToItem: function(item) {
|
jumpToItem: function(item) {
|
||||||
@ -488,6 +398,9 @@ const UserList = new Lang.Class({
|
|||||||
if (user.is_system_account())
|
if (user.is_system_account())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (user.locked)
|
||||||
|
return;
|
||||||
|
|
||||||
let userName = user.get_user_name();
|
let userName = user.get_user_name();
|
||||||
|
|
||||||
if (!userName)
|
if (!userName)
|
||||||
@ -508,7 +421,6 @@ const UserList = new Lang.Class({
|
|||||||
Lang.bind(this,
|
Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
this.scrollToItem(item);
|
this.scrollToItem(item);
|
||||||
item.showFocusAnimation(0);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._moveFocusToItems();
|
this._moveFocusToItems();
|
||||||
@ -550,10 +462,7 @@ const SessionListItem = new Lang.Class({
|
|||||||
|
|
||||||
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
|
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
|
||||||
|
|
||||||
this.actor.add_actor(this._box,
|
this.actor.add_actor(this._box);
|
||||||
{ expand: true,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true });
|
|
||||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
|
||||||
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
|
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
|
||||||
@ -563,11 +472,9 @@ const SessionListItem = new Lang.Class({
|
|||||||
|
|
||||||
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
|
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
|
||||||
text: name });
|
text: name });
|
||||||
|
this.actor.label_actor = label;
|
||||||
|
|
||||||
this._box.add_actor(label,
|
this._box.add_actor(label);
|
||||||
{ expand: true,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setShowDot: function(show) {
|
setShowDot: function(show) {
|
||||||
@ -611,10 +518,7 @@ const SessionList = new Lang.Class({
|
|||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
let box = new St.BoxLayout();
|
let box = new St.BoxLayout();
|
||||||
this._button.add_actor(box,
|
this._button.add_actor(box);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
|
|
||||||
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
|
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
|
||||||
text: '\u25B8' });
|
text: '\u25B8' });
|
||||||
@ -622,30 +526,18 @@ const SessionList = new Lang.Class({
|
|||||||
|
|
||||||
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
|
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
|
||||||
text: _("Session...") });
|
text: _("Session...") });
|
||||||
box.add_actor(label,
|
box.add_actor(label);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
|
|
||||||
this._button.connect('clicked',
|
this._button.connect('clicked',
|
||||||
Lang.bind(this, this._onClicked));
|
Lang.bind(this, this._onClicked));
|
||||||
this._box.add_actor(this._button,
|
this._box.add_actor(this._button);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
|
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
|
||||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
|
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||||
Gtk.PolicyType.AUTOMATIC);
|
Gtk.PolicyType.AUTOMATIC);
|
||||||
this._box.add_actor(this._scrollView,
|
this._box.add_actor(this._scrollView);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
|
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this._scrollView.add_actor(this._itemList,
|
this._scrollView.add_actor(this._itemList);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
this._scrollView.hide();
|
this._scrollView.hide();
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
this._populate();
|
this._populate();
|
||||||
@ -698,7 +590,7 @@ const SessionList = new Lang.Class({
|
|||||||
this._activeSessionId = null;
|
this._activeSessionId = null;
|
||||||
this._items = {};
|
this._items = {};
|
||||||
|
|
||||||
let ids = GdmGreeter.get_session_ids();
|
let ids = Gdm.get_session_ids();
|
||||||
ids.sort();
|
ids.sort();
|
||||||
|
|
||||||
if (ids.length <= 1) {
|
if (ids.length <= 1) {
|
||||||
@ -710,14 +602,10 @@ const SessionList = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < ids.length; i++) {
|
for (let i = 0; i < ids.length; i++) {
|
||||||
let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]);
|
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]);
|
||||||
|
|
||||||
let item = new SessionListItem(ids[i], sessionName);
|
let item = new SessionListItem(ids[i], sessionName);
|
||||||
this._itemList.add_actor(item.actor,
|
this._itemList.add_actor(item.actor);
|
||||||
{ x_align: St.Align.START,
|
|
||||||
y_align: St.Align.START,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true });
|
|
||||||
this._items[ids[i]] = item;
|
this._items[ids[i]] = item;
|
||||||
|
|
||||||
if (!this._activeSessionId)
|
if (!this._activeSessionId)
|
||||||
@ -736,52 +624,54 @@ const LoginDialog = new Lang.Class({
|
|||||||
Name: 'LoginDialog',
|
Name: 'LoginDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init: function() {
|
_init: function(parentActor) {
|
||||||
this.parent({ shellReactive: true, styleClass: 'login-dialog' });
|
this.parent({ shellReactive: true,
|
||||||
|
styleClass: 'login-dialog',
|
||||||
|
parentActor: parentActor,
|
||||||
|
shouldFadeIn: false });
|
||||||
this.connect('destroy',
|
this.connect('destroy',
|
||||||
Lang.bind(this, this._onDestroy));
|
Lang.bind(this, this._onDestroy));
|
||||||
this.connect('opened',
|
this.connect('opened',
|
||||||
Lang.bind(this, this._onOpened));
|
Lang.bind(this, this._onOpened));
|
||||||
|
|
||||||
this._userManager = AccountsService.UserManager.get_default()
|
this._userManager = AccountsService.UserManager.get_default()
|
||||||
this._greeterClient = new GdmGreeter.Client();
|
this._greeterClient = new Gdm.Client();
|
||||||
|
|
||||||
this._greeterClient.open_connection();
|
this._greeter = this._greeterClient.get_greeter_sync(null);
|
||||||
|
|
||||||
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
|
this._greeter.connect('default-session-name-changed',
|
||||||
|
|
||||||
this._greeterClient.connect('reset',
|
|
||||||
Lang.bind(this, this._onReset));
|
|
||||||
this._greeterClient.connect('default-session-changed',
|
|
||||||
Lang.bind(this, this._onDefaultSessionChanged));
|
Lang.bind(this, this._onDefaultSessionChanged));
|
||||||
this._greeterClient.connect('info',
|
|
||||||
Lang.bind(this, this._onInfo));
|
this._greeter.connect('session-opened',
|
||||||
this._greeterClient.connect('problem',
|
|
||||||
Lang.bind(this, this._onProblem));
|
|
||||||
this._greeterClient.connect('info-query',
|
|
||||||
Lang.bind(this, this._onInfoQuery));
|
|
||||||
this._greeterClient.connect('secret-info-query',
|
|
||||||
Lang.bind(this, this._onSecretInfoQuery));
|
|
||||||
this._greeterClient.connect('session-opened',
|
|
||||||
Lang.bind(this, this._onSessionOpened));
|
Lang.bind(this, this._onSessionOpened));
|
||||||
this._greeterClient.connect('timed-login-requested',
|
this._greeter.connect('timed-login-requested',
|
||||||
Lang.bind(this, this._onTimedLoginRequested));
|
Lang.bind(this, this._onTimedLoginRequested));
|
||||||
this._greeterClient.connect('authentication-failed',
|
|
||||||
Lang.bind(this, this._onAuthenticationFailed));
|
|
||||||
this._greeterClient.connect('conversation-stopped',
|
|
||||||
Lang.bind(this, this._onConversationStopped));
|
|
||||||
|
|
||||||
this._settings = new Gio.Settings({ schema: _LOGIN_SCREEN_SCHEMA });
|
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
|
||||||
|
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
|
||||||
|
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
||||||
|
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||||
|
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
||||||
|
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
||||||
|
|
||||||
this._fprintManager = new Fprint.FprintManager();
|
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||||
this._startFingerprintConversationIfNeeded();
|
|
||||||
this._settings.connect('changed::' + _LOGO_KEY,
|
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
|
||||||
Lang.bind(this, this._updateLogo));
|
Lang.bind(this, this._updateLogo));
|
||||||
|
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
||||||
|
Lang.bind(this, this._updateBanner));
|
||||||
|
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
|
||||||
|
Lang.bind(this, this._updateBanner));
|
||||||
|
|
||||||
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
|
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
|
||||||
this.contentLayout.add(this._logoBox);
|
this.contentLayout.add(this._logoBox);
|
||||||
this._updateLogo();
|
this._updateLogo();
|
||||||
|
|
||||||
|
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||||
|
text: '' });
|
||||||
|
this.contentLayout.add(this._bannerLabel);
|
||||||
|
this._updateBanner();
|
||||||
|
|
||||||
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
|
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
|
||||||
text: C_("title", "Sign In") });
|
text: C_("title", "Sign In") });
|
||||||
|
|
||||||
@ -826,17 +716,18 @@ const LoginDialog = new Lang.Class({
|
|||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
x_align: St.Align.START });
|
x_align: St.Align.START });
|
||||||
// Translators: this message is shown below the password entry field
|
|
||||||
// to indicate the user can swipe their finger instead
|
this._promptMessage = new St.Label({ visible: false });
|
||||||
this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"),
|
this._promptBox.add(this._promptMessage, { x_fill: true });
|
||||||
style_class: 'login-dialog-prompt-fingerprint-message' });
|
|
||||||
this._promptFingerprintMessage.hide();
|
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
||||||
this._promptBox.add(this._promptFingerprintMessage);
|
this._promptLoginHint.hide();
|
||||||
|
this._promptBox.add(this._promptLoginHint);
|
||||||
|
|
||||||
this._sessionList = new SessionList();
|
this._sessionList = new SessionList();
|
||||||
this._sessionList.connect('session-activated',
|
this._sessionList.connect('session-activated',
|
||||||
Lang.bind(this, function(list, sessionId) {
|
Lang.bind(this, function(list, sessionId) {
|
||||||
this._greeterClient.call_select_session (sessionId);
|
this._greeter.call_select_session_sync (sessionId, null);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._promptBox.add(this._sessionList.actor,
|
this._promptBox.add(this._sessionList.actor,
|
||||||
@ -884,25 +775,9 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_startFingerprintConversationIfNeeded: function() {
|
|
||||||
this._haveFingerprintReader = false;
|
|
||||||
|
|
||||||
if (!this._settings.get_boolean(_FINGERPRINT_AUTHENTICATION_KEY))
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, Lang.bind(this,
|
|
||||||
function(device, error) {
|
|
||||||
if (!error && device)
|
|
||||||
this._haveFingerprintReader = true;
|
|
||||||
|
|
||||||
if (this._haveFingerprintReader)
|
|
||||||
this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME);
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateLogo: function() {
|
_updateLogo: function() {
|
||||||
this._logoBox.child = null;
|
this._logoBox.child = null;
|
||||||
let path = this._settings.get_string(_LOGO_KEY);
|
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
let file = Gio.file_new_for_path(path);
|
let file = Gio.file_new_for_path(path);
|
||||||
@ -914,9 +789,20 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateBanner: function() {
|
||||||
|
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
|
||||||
|
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
|
||||||
|
|
||||||
|
if (enabled && text) {
|
||||||
|
this._bannerLabel.set_text(text);
|
||||||
|
this._fadeInBanner();
|
||||||
|
} else {
|
||||||
|
this._fadeOutBanner();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_onReset: function(client, serviceName) {
|
_onReset: function(client, serviceName) {
|
||||||
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
|
this._promptMessage.hide();
|
||||||
this._startFingerprintConversationIfNeeded();
|
|
||||||
|
|
||||||
let tasks = [this._hidePrompt,
|
let tasks = [this._hidePrompt,
|
||||||
|
|
||||||
@ -926,7 +812,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
function() {
|
function() {
|
||||||
this._sessionList.close();
|
this._sessionList.close();
|
||||||
this._promptFingerprintMessage.hide();
|
this._promptLoginHint.hide();
|
||||||
this._userList.actor.show();
|
this._userList.actor.show();
|
||||||
this._userList.actor.opacity = 255;
|
this._userList.actor.opacity = 255;
|
||||||
return this._userList.showItems();
|
return this._userList.showItems();
|
||||||
@ -947,61 +833,51 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._sessionList.setActiveSession(sessionId);
|
this._sessionList.setActiveSession(sessionId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInfo: function(client, serviceName, info) {
|
_showMessage: function(userVerifier, message, styleClass) {
|
||||||
// We don't display fingerprint messages, because they
|
this._promptMessage.text = message;
|
||||||
// have words like UPEK in them. Instead we use the messages
|
this._promptMessage.styleClass = styleClass;
|
||||||
// as a cue to display our own message.
|
GdmUtil.fadeInActor(this._promptMessage);
|
||||||
if (serviceName == _FINGERPRINT_SERVICE_NAME &&
|
|
||||||
this._haveFingerprintReader &&
|
|
||||||
(!this._promptFingerprintMessage.visible ||
|
|
||||||
this._promptFingerprintMessage.opacity != 255)) {
|
|
||||||
|
|
||||||
_fadeInActor(this._promptFingerprintMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
|
||||||
Main.notifyError(info);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onProblem: function(client, serviceName, problem) {
|
_showLoginHint: function(verifier, message) {
|
||||||
// we don't want to show auth failed messages to
|
this._promptLoginHint.set_text(message)
|
||||||
// users who haven't enrolled their fingerprint.
|
GdmUtil.fadeInActor(this._promptLoginHint);
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
|
||||||
Main.notifyError(problem);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancel: function(client) {
|
_hideLoginHint: function() {
|
||||||
this._greeterClient.call_cancel();
|
GdmUtil.fadeOutActor(this._promptLoginHint);
|
||||||
|
this._promptLoginHint.set_text('');
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this._userVerifier.cancel();
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeInPrompt: function() {
|
_fadeInPrompt: function() {
|
||||||
let tasks = [function() {
|
let tasks = [function() {
|
||||||
return _fadeInActor(this._promptLabel);
|
return GdmUtil.fadeInActor(this._promptLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
return _fadeInActor(this._promptEntry);
|
return GdmUtil.fadeInActor(this._promptEntry);
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
// Show it with 0 opacity so we preallocate space for it
|
// Show it with 0 opacity so we preallocate space for it
|
||||||
// in the event we need to fade in the message
|
// in the event we need to fade in the message
|
||||||
this._promptFingerprintMessage.opacity = 0;
|
this._promptLoginHint.opacity = 0;
|
||||||
this._promptFingerprintMessage.show();
|
this._promptLoginHint.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
return _fadeInActor(this._promptBox);
|
return GdmUtil.fadeInActor(this._promptBox);
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
if (this._user && this._user.is_logged_in())
|
if (this._user && this._user.is_logged_in())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return _fadeInActor(this._sessionList.actor);
|
return GdmUtil.fadeInActor(this._sessionList.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
@ -1016,13 +892,14 @@ const LoginDialog = new Lang.Class({
|
|||||||
_showPrompt: function() {
|
_showPrompt: function() {
|
||||||
let hold = new Batch.Hold();
|
let hold = new Batch.Hold();
|
||||||
|
|
||||||
let buttons = [{ action: Lang.bind(this, this._onCancel),
|
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||||
label: _("Cancel"),
|
label: _("Cancel"),
|
||||||
key: Clutter.Escape },
|
key: Clutter.Escape },
|
||||||
{ action: Lang.bind(this, function() {
|
{ action: Lang.bind(this, function() {
|
||||||
hold.release();
|
hold.release();
|
||||||
}),
|
}),
|
||||||
label: C_("button", "Sign In") }];
|
label: C_("button", "Sign In"),
|
||||||
|
default: true }];
|
||||||
|
|
||||||
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
|
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
@ -1057,13 +934,12 @@ const LoginDialog = new Lang.Class({
|
|||||||
this.setButtons([]);
|
this.setButtons([]);
|
||||||
|
|
||||||
let tasks = [function() {
|
let tasks = [function() {
|
||||||
return _fadeOutActor(this._promptBox);
|
return GdmUtil.fadeOutActor(this._promptBox);
|
||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
this._promptFingerprintMessage.hide();
|
this._promptLoginHint.hide();
|
||||||
this._promptEntry.reactive = true;
|
this._promptEntry.reactive = true;
|
||||||
this._promptEntry.remove_style_pseudo_class('insensitive');
|
|
||||||
this._promptEntry.set_text('');
|
this._promptEntry.set_text('');
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -1072,43 +948,26 @@ const LoginDialog = new Lang.Class({
|
|||||||
return batch.run();
|
return batch.run();
|
||||||
},
|
},
|
||||||
|
|
||||||
_askQuestion: function(serviceName, question) {
|
_askQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||||
this._promptLabel.set_text(question);
|
this._promptLabel.set_text(question);
|
||||||
|
|
||||||
|
this._promptEntry.set_text('');
|
||||||
|
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
||||||
|
|
||||||
let tasks = [this._showPrompt,
|
let tasks = [this._showPrompt,
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
let _text = this._promptEntry.get_text();
|
let _text = this._promptEntry.get_text();
|
||||||
this._promptEntry.reactive = false;
|
this._promptEntry.reactive = false;
|
||||||
this._promptEntry.add_style_pseudo_class('insensitive');
|
this._userVerifier.answerQuery(serviceName, _text);
|
||||||
this._greeterClient.call_answer_query(serviceName, _text);
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||||
return batch.run();
|
return batch.run();
|
||||||
},
|
},
|
||||||
_onInfoQuery: function(client, serviceName, question) {
|
|
||||||
// We only expect questions to come from the main auth service
|
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._promptEntry.set_text('');
|
|
||||||
this._promptEntry.clutter_text.set_password_char('');
|
|
||||||
this._askQuestion(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._promptEntry.set_text('');
|
|
||||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
|
||||||
this._askQuestion(serviceName, secretQuestion);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onSessionOpened: function(client, serviceName) {
|
_onSessionOpened: function(client, serviceName) {
|
||||||
this._greeterClient.call_start_session_when_ready(serviceName, true);
|
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_waitForItemForUser: function(userName) {
|
_waitForItemForUser: function(userName) {
|
||||||
@ -1135,7 +994,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
_showTimedLoginAnimation: function() {
|
_showTimedLoginAnimation: function() {
|
||||||
this._timedLoginItem.actor.grab_key_focus();
|
this._timedLoginItem.actor.grab_key_focus();
|
||||||
return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime);
|
return this._timedLoginItem.showTimedLoginIndicator(this._timedLoginAnimationTime);
|
||||||
},
|
},
|
||||||
|
|
||||||
_blockTimedLoginUntilIdle: function() {
|
_blockTimedLoginUntilIdle: function() {
|
||||||
@ -1176,7 +1035,6 @@ const LoginDialog = new Lang.Class({
|
|||||||
// item.
|
// item.
|
||||||
if (!this.is_loaded) {
|
if (!this.is_loaded) {
|
||||||
this._userList.jumpToItem(this._timedLoginItem);
|
this._userList.jumpToItem(this._timedLoginItem);
|
||||||
this._timedLoginItem.showFocusAnimation(0);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1190,7 +1048,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
function() {
|
function() {
|
||||||
this._timedLoginBatch = null;
|
this._timedLoginBatch = null;
|
||||||
this._greeterClient.call_begin_auto_login(userName);
|
this._greeter.call_begin_auto_login_sync(userName, null);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
|
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
|
||||||
@ -1204,6 +1062,9 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._timedLoginBatch = null;
|
this._timedLoginBatch = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._timedLoginItem)
|
||||||
|
this._timedLoginItem.hideTimedLoginIndicator();
|
||||||
|
|
||||||
let userName = this._timedLoginItem.user.get_user_name();
|
let userName = this._timedLoginItem.user.get_user_name();
|
||||||
|
|
||||||
if (userName)
|
if (userName)
|
||||||
@ -1233,21 +1094,6 @@ const LoginDialog = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAuthenticationFailed: function(client) {
|
|
||||||
this._greeterClient.call_cancel();
|
|
||||||
},
|
|
||||||
|
|
||||||
_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._greeterClient.call_cancel();
|
|
||||||
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
|
|
||||||
_fadeOutActor(this._promptFingerprintMessage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onNotListedClicked: function(user) {
|
_onNotListedClicked: function(user) {
|
||||||
let tasks = [function() {
|
let tasks = [function() {
|
||||||
return this._userList.hideItems();
|
return this._userList.hideItems();
|
||||||
@ -1266,7 +1112,10 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._fadeOutLogo]),
|
this._fadeOutLogo]),
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME);
|
let hold = new Batch.Hold();
|
||||||
|
|
||||||
|
this._userVerifier.begin(null, hold);
|
||||||
|
return hold;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||||
@ -1274,30 +1123,47 @@ const LoginDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_fadeInLogo: function() {
|
_fadeInLogo: function() {
|
||||||
return _fadeInActor(this._logoBox);
|
return GdmUtil.fadeInActor(this._logoBox);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeOutLogo: function() {
|
_fadeOutLogo: function() {
|
||||||
return _fadeOutActor(this._logoBox);
|
return GdmUtil.fadeOutActor(this._logoBox);
|
||||||
|
},
|
||||||
|
|
||||||
|
_fadeInBanner: function() {
|
||||||
|
return GdmUtil.fadeInActor(this._bannerLabel);
|
||||||
|
},
|
||||||
|
|
||||||
|
_fadeOutBanner: function() {
|
||||||
|
return GdmUtil.fadeOutActor(this._bannerLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeInTitleLabel: function() {
|
_fadeInTitleLabel: function() {
|
||||||
return _fadeInActor(this._titleLabel);
|
return GdmUtil.fadeInActor(this._titleLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeOutTitleLabel: function() {
|
_fadeOutTitleLabel: function() {
|
||||||
return _fadeOutActor(this._titleLabel);
|
return GdmUtil.fadeOutActor(this._titleLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeInNotListedButton: function() {
|
_fadeInNotListedButton: function() {
|
||||||
return _fadeInActor(this._notListedButton);
|
return GdmUtil.fadeInActor(this._notListedButton);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeOutNotListedButton: function() {
|
_fadeOutNotListedButton: function() {
|
||||||
return _fadeOutActor(this._notListedButton);
|
return GdmUtil.fadeOutActor(this._notListedButton);
|
||||||
|
},
|
||||||
|
|
||||||
|
_beginVerificationForUser: function(userName) {
|
||||||
|
let hold = new Batch.Hold();
|
||||||
|
|
||||||
|
this._userVerifier.begin(userName, hold);
|
||||||
|
return hold;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onUserListActivated: function(activatedItem) {
|
_onUserListActivated: function(activatedItem) {
|
||||||
|
let userName;
|
||||||
|
|
||||||
let tasks = [function() {
|
let tasks = [function() {
|
||||||
this._userList.actor.reactive = false;
|
this._userList.actor.reactive = false;
|
||||||
return this._userList.pinInPlace();
|
return this._userList.pinInPlace();
|
||||||
@ -1324,12 +1190,9 @@ const LoginDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
let userName = activatedItem.user.get_user_name();
|
userName = activatedItem.user.get_user_name();
|
||||||
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME,
|
|
||||||
userName);
|
|
||||||
|
|
||||||
if (this._haveFingerprintReader)
|
return this._beginVerificationForUser(userName);
|
||||||
this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName);
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
this._user = activatedItem.user;
|
this._user = activatedItem.user;
|
||||||
|
@ -21,8 +21,7 @@
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const UPowerGlib = imports.gi.UPowerGlib;
|
const UPowerGlib = imports.gi.UPowerGlib;
|
||||||
|
|
||||||
const ConsoleKit = imports.gdm.consoleKit;
|
const LoginManager = imports.misc.loginManager;
|
||||||
const Systemd = imports.gdm.systemd;
|
|
||||||
|
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
@ -32,13 +31,11 @@ const PowerMenuButton = new Lang.Class({
|
|||||||
Extends: PanelMenu.SystemStatusButton,
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent('system-shutdown', null);
|
/* Translators: accessible name of the power menu in the login screen */
|
||||||
|
this.parent('system-shutdown-symbolic', _("Power"));
|
||||||
this._upClient = new UPowerGlib.Client();
|
this._upClient = new UPowerGlib.Client();
|
||||||
|
|
||||||
if (Systemd.haveSystemd())
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
this._systemdLoginManager = new Systemd.SystemdLoginManager();
|
|
||||||
else
|
|
||||||
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
|
|
||||||
|
|
||||||
this._createSubMenu();
|
this._createSubMenu();
|
||||||
|
|
||||||
@ -60,92 +57,29 @@ const PowerMenuButton = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
|
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||||
this.actor.hide();
|
this.actor.visible = shouldBeVisible;
|
||||||
else
|
|
||||||
this.actor.show();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateHaveShutdown: function() {
|
_updateHaveShutdown: function() {
|
||||||
|
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
|
||||||
if (Systemd.haveSystemd()) {
|
|
||||||
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error)
|
|
||||||
this._haveShutdown = result != 'no';
|
|
||||||
else
|
|
||||||
this._haveShutdown = false;
|
|
||||||
|
|
||||||
if (this._haveShutdown)
|
|
||||||
this._powerOffItem.actor.show();
|
|
||||||
else
|
|
||||||
this._powerOffItem.actor.hide();
|
|
||||||
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._consoleKitManager.CanStopRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error)
|
|
||||||
this._haveShutdown = result;
|
this._haveShutdown = result;
|
||||||
else
|
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||||
this._haveShutdown = false;
|
|
||||||
|
|
||||||
if (this._haveShutdown) {
|
|
||||||
this._powerOffItem.actor.show();
|
|
||||||
} else {
|
|
||||||
this._powerOffItem.actor.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateHaveRestart: function() {
|
_updateHaveRestart: function() {
|
||||||
|
this._loginManager.canReboot(Lang.bind(this, function(result) {
|
||||||
if (Systemd.haveSystemd()) {
|
|
||||||
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error)
|
|
||||||
this._haveRestart = result != 'no';
|
|
||||||
else
|
|
||||||
this._haveRestart = false;
|
|
||||||
|
|
||||||
if (this._haveRestart)
|
|
||||||
this._restartItem.actor.show();
|
|
||||||
else
|
|
||||||
this._restartItem.actor.hide();
|
|
||||||
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error)
|
|
||||||
this._haveRestart = result;
|
this._haveRestart = result;
|
||||||
else
|
this._restartItem.actor.visible = this._haveRestart;
|
||||||
this._haveRestart = false;
|
|
||||||
|
|
||||||
if (this._haveRestart) {
|
|
||||||
this._restartItem.actor.show();
|
|
||||||
} else {
|
|
||||||
this._restartItem.actor.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateHaveSuspend: function() {
|
_updateHaveSuspend: function() {
|
||||||
this._haveSuspend = this._upClient.get_can_suspend();
|
this._haveSuspend = this._upClient.get_can_suspend();
|
||||||
|
this._suspendItem.actor.visible = this._haveSuspend;
|
||||||
if (this._haveSuspend)
|
|
||||||
this._suspendItem.actor.show();
|
|
||||||
else
|
|
||||||
this._suspendItem.actor.hide();
|
|
||||||
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -177,19 +111,13 @@ const PowerMenuButton = new Lang.Class({
|
|||||||
if (!this._haveRestart)
|
if (!this._haveRestart)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Systemd.haveSystemd())
|
this._loginManager.reboot();
|
||||||
this._systemdLoginManager.RebootRemote(true);
|
|
||||||
else
|
|
||||||
this._consoleKitManager.RestartRemote();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onActivatePowerOff: function() {
|
_onActivatePowerOff: function() {
|
||||||
if (!this._haveShutdown)
|
if (!this._haveShutdown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Systemd.haveSystemd())
|
this._loginManager.powerOff();
|
||||||
this._systemdLoginManager.PowerOffRemote(true);
|
|
||||||
else
|
|
||||||
this._consoleKitManager.StopRemote();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
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;
|
|
||||||
}
|
|
369
js/gdm/util.js
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
// -*- 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';
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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.relase();
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
let canRetry = retry && this._userName &&
|
||||||
|
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||||
|
|
||||||
|
if (canRetry) {
|
||||||
|
this._failCounter++;
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
this.begin(this._userName, new Batch.Hold());
|
||||||
|
} else {
|
||||||
|
// Allow some time to see the message, then reset everything
|
||||||
|
Mainloop.timeout_add(3000, Lang.bind(this, function() {
|
||||||
|
this.cancel();
|
||||||
|
|
||||||
|
this._onReset();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('verification-failed');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onConversationStopped: function(client, serviceName) {
|
||||||
|
// if the password service fails, then cancel everything.
|
||||||
|
// But if, e.g., fingerprint fails, still give
|
||||||
|
// password authentication a chance to succeed
|
||||||
|
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||||
|
this._verificationFailed(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('hide-login-hint');
|
||||||
|
|
||||||
|
if (this._realmLoginHintSignalId) {
|
||||||
|
this._realmManager.disconnect(this._realmLoginHintSignalId);
|
||||||
|
this._realmLoginHintSignalId = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
@ -4,12 +4,8 @@
|
|||||||
const PACKAGE_NAME = '@PACKAGE_NAME@';
|
const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||||
/* The version of this package */
|
/* The version of this package */
|
||||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||||
/* The version of GJS we're linking to */
|
|
||||||
const GJS_VERSION = '@GJS_VERSION@';
|
|
||||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||||
/* The system TLS CA list */
|
|
||||||
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
|
|
||||||
/* gettext package */
|
/* gettext package */
|
||||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
||||||
/* locale dir */
|
/* locale dir */
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
// Common utils for the extension system and the extension
|
// Common utils for the extension system and the extension
|
||||||
// preferences tool
|
// preferences tool
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const ShellJS = imports.gi.ShellJS;
|
const ShellJS = imports.gi.ShellJS;
|
||||||
@ -14,9 +17,6 @@ const ExtensionType = {
|
|||||||
PER_USER: 2
|
PER_USER: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
// GFile for user extensions
|
|
||||||
var userExtensionsDir = null;
|
|
||||||
|
|
||||||
// Maps uuid -> metadata object
|
// Maps uuid -> metadata object
|
||||||
const extensions = {};
|
const extensions = {};
|
||||||
|
|
||||||
@ -40,13 +40,18 @@ function getCurrentExtension() {
|
|||||||
throw new Error('Could not find current extension');
|
throw new Error('Could not find current extension');
|
||||||
|
|
||||||
let path = match[1];
|
let path = match[1];
|
||||||
let uuid = GLib.path_get_basename(GLib.path_get_dirname(path));
|
let file = Gio.File.new_for_path(path);
|
||||||
|
|
||||||
let extension = extensions[uuid];
|
|
||||||
if (extension === undefined)
|
|
||||||
throw new Error('Could not find current extension');
|
|
||||||
|
|
||||||
|
// Walk up the directory tree, looking for an extesion with
|
||||||
|
// the same UUID as a directory name.
|
||||||
|
while (file != null) {
|
||||||
|
let extension = extensions[file.get_basename()];
|
||||||
|
if (extension !== undefined)
|
||||||
return extension;
|
return extension;
|
||||||
|
file = file.get_parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Could not find current extension');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,9 +88,6 @@ function isOutOfDate(extension) {
|
|||||||
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
|
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ function createExtensionObject(uuid, dir, type) {
|
|||||||
|
|
||||||
// Encourage people to add this
|
// Encourage people to add this
|
||||||
if (!meta.url) {
|
if (!meta.url) {
|
||||||
global.log('Warning: Missing "url" property in metadata.json');
|
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uuid != meta.uuid) {
|
if (uuid != meta.uuid) {
|
||||||
@ -150,24 +152,16 @@ function installImporter(extension) {
|
|||||||
_extension = null;
|
_extension = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
const ExtensionFinder = new Lang.Class({
|
||||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
Name: 'ExtensionFinder',
|
||||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
|
||||||
try {
|
|
||||||
if (!userExtensionsDir.query_exists(null))
|
|
||||||
userExtensionsDir.make_directory_with_parents(null);
|
|
||||||
} catch (e) {
|
|
||||||
global.logError('' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanExtensionsInDirectory(callback, dir, type) {
|
_scanExtensionsInDirectory: function(dir, type) {
|
||||||
let fileEnum;
|
let fileEnum;
|
||||||
let file, info;
|
let file, info;
|
||||||
try {
|
try {
|
||||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
global.logError('' + e);
|
logError(e, 'Could not enumerate extensions directory');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,18 +171,36 @@ function scanExtensionsInDirectory(callback, dir, type) {
|
|||||||
continue;
|
continue;
|
||||||
let uuid = info.get_name();
|
let uuid = info.get_name();
|
||||||
let extensionDir = dir.get_child(uuid);
|
let extensionDir = dir.get_child(uuid);
|
||||||
callback(uuid, extensionDir, type);
|
|
||||||
|
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;
|
||||||
|
try {
|
||||||
|
extension = createExtensionObject(uuid, extensionDir, type);
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, 'Could not load extension %s'.format(uuid));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.emit('extension-found', extension);
|
||||||
}
|
}
|
||||||
fileEnum.close(null);
|
fileEnum.close(null);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
scanExtensions: function() {
|
||||||
|
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
|
||||||
|
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
|
||||||
|
|
||||||
function scanExtensions(callback) {
|
|
||||||
let systemDataDirs = GLib.get_system_data_dirs();
|
let systemDataDirs = GLib.get_system_data_dirs();
|
||||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||||
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
|
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
|
||||||
let dir = Gio.file_new_for_path(dirPath);
|
let dir = Gio.file_new_for_path(dirPath);
|
||||||
if (dir.query_exists(null))
|
if (dir.query_exists(null))
|
||||||
scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
|
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
|
||||||
}
|
}
|
||||||
scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
|
}
|
||||||
}
|
});
|
||||||
|
Signals.addSignalMethods(ExtensionFinder.prototype);
|
||||||
|
@ -28,7 +28,7 @@ function deleteGFile(file) {
|
|||||||
return file['delete'](null);
|
return file['delete'](null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function recursivelyDeleteDir(dir) {
|
function recursivelyDeleteDir(dir, deleteParent) {
|
||||||
let children = dir.enumerate_children('standard::name,standard::type',
|
let children = dir.enumerate_children('standard::name,standard::type',
|
||||||
Gio.FileQueryInfoFlags.NONE, null);
|
Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
|
||||||
@ -39,8 +39,29 @@ function recursivelyDeleteDir(dir) {
|
|||||||
if (type == Gio.FileType.REGULAR)
|
if (type == Gio.FileType.REGULAR)
|
||||||
deleteGFile(child);
|
deleteGFile(child);
|
||||||
else if (type == Gio.FileType.DIRECTORY)
|
else if (type == Gio.FileType.DIRECTORY)
|
||||||
recursivelyDeleteDir(child);
|
recursivelyDeleteDir(child, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deleteParent)
|
||||||
deleteGFile(dir);
|
deleteGFile(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recursivelyMoveDir(srcDir, destDir) {
|
||||||
|
let children = srcDir.enumerate_children('standard::name,standard::type',
|
||||||
|
Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
|
||||||
|
if (!destDir.query_exists(null))
|
||||||
|
destDir.make_directory_with_parents(null);
|
||||||
|
|
||||||
|
let info, child;
|
||||||
|
while ((info = children.next_file(null)) != null) {
|
||||||
|
let type = info.get_file_type();
|
||||||
|
let srcChild = srcDir.get_child(info.get_name());
|
||||||
|
let destChild = destDir.get_child(info.get_name());
|
||||||
|
log([srcChild.get_path(), destChild.get_path()]);
|
||||||
|
if (type == Gio.FileType.REGULAR)
|
||||||
|
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
|
||||||
|
else if (type == Gio.FileType.DIRECTORY)
|
||||||
|
recursivelyMoveDir(srcChild, destChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This function is intended to extend the String object and provide
|
|
||||||
* an String.format API for string formatting.
|
|
||||||
* It has to be set up using String.prototype.format = Format.format;
|
|
||||||
* Usage:
|
|
||||||
* "somestring %s %d".format('hello', 5);
|
|
||||||
* It supports %s, %d, %x and %f, for %f it also support precisions like
|
|
||||||
* "%.2f".format(1.526). All specifiers can be prefixed with a minimum
|
|
||||||
* field width, e.g. "%5s".format("foo"). Unless the width is prefixed
|
|
||||||
* with '0', the formatted string will be padded with spaces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function format() {
|
|
||||||
let str = this;
|
|
||||||
let i = 0;
|
|
||||||
let args = arguments;
|
|
||||||
|
|
||||||
return str.replace(/%([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, widthGroup, precisionGroup, genericGroup) {
|
|
||||||
|
|
||||||
if (precisionGroup != '' && genericGroup != 'f')
|
|
||||||
throw new Error("Precision can only be specified for 'f'");
|
|
||||||
|
|
||||||
let fillChar = (widthGroup[0] == '0') ? '0' : ' ';
|
|
||||||
let width = parseInt(widthGroup, 10) || 0;
|
|
||||||
|
|
||||||
function fillWidth(s, c, w) {
|
|
||||||
let fill = '';
|
|
||||||
for (let i = 0; i < w; i++)
|
|
||||||
fill += c;
|
|
||||||
return fill.substr(s.length) + s;
|
|
||||||
}
|
|
||||||
|
|
||||||
let s = '';
|
|
||||||
switch (genericGroup) {
|
|
||||||
case '%':
|
|
||||||
return '%';
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
s = args[i++].toString();
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
s = parseInt(args[i++]).toString();
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
s = parseInt(args[i++]).toString(16);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
if (precisionGroup == '')
|
|
||||||
s = parseFloat(args[i++]).toString();
|
|
||||||
else
|
|
||||||
s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error('Unsupported conversion character %' + genericGroup);
|
|
||||||
}
|
|
||||||
return fillWidth(s, fillChar, width);
|
|
||||||
});
|
|
||||||
}
|
|
@ -50,9 +50,20 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
|||||||
<arg type="u" direction="in" />
|
<arg type="u" direction="in" />
|
||||||
</method>
|
</method>
|
||||||
<method name="Shutdown" />
|
<method name="Shutdown" />
|
||||||
|
<method name="Reboot" />
|
||||||
<method name="CanShutdown">
|
<method name="CanShutdown">
|
||||||
<arg type="b" direction="out" />
|
<arg type="b" direction="out" />
|
||||||
</method>
|
</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>;
|
</interface>;
|
||||||
|
|
||||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
||||||
|
@ -41,24 +41,26 @@ const HistoryManager = new Lang.Class({
|
|||||||
this._historyIndex = this._history.length;
|
this._historyIndex = this._history.length;
|
||||||
},
|
},
|
||||||
|
|
||||||
prevItem: function(text) {
|
_setPrevItem: function(text) {
|
||||||
if (this._historyIndex <= 0)
|
if (this._historyIndex <= 0)
|
||||||
return text;
|
return false;
|
||||||
|
|
||||||
if (text)
|
if (text)
|
||||||
this._history[this._historyIndex] = text;
|
this._history[this._historyIndex] = text;
|
||||||
this._historyIndex--;
|
this._historyIndex--;
|
||||||
return this._indexChanged();
|
this._indexChanged();
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
nextItem: function(text) {
|
_setNextItem: function(text) {
|
||||||
if (this._historyIndex >= this._history.length)
|
if (this._historyIndex >= this._history.length)
|
||||||
return text;
|
return false;
|
||||||
|
|
||||||
if (text)
|
if (text)
|
||||||
this._history[this._historyIndex] = text;
|
this._history[this._historyIndex] = text;
|
||||||
this._historyIndex++;
|
this._historyIndex++;
|
||||||
return this._indexChanged();
|
this._indexChanged();
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
lastItem: function() {
|
lastItem: function() {
|
||||||
@ -83,11 +85,9 @@ const HistoryManager = new Lang.Class({
|
|||||||
_onEntryKeyPress: function(entry, event) {
|
_onEntryKeyPress: function(entry, event) {
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Up) {
|
if (symbol == Clutter.KEY_Up) {
|
||||||
this.prevItem(entry.get_text());
|
return this._setPrevItem(entry.get_text());
|
||||||
return true;
|
|
||||||
} else if (symbol == Clutter.KEY_Down) {
|
} else if (symbol == Clutter.KEY_Down) {
|
||||||
this.nextItem(entry.get_text());
|
return this._setNextItem(entry.get_text());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -98,8 +98,6 @@ const HistoryManager = new Lang.Class({
|
|||||||
|
|
||||||
if (this._entry)
|
if (this._entry)
|
||||||
this._entry.set_text(current);
|
this._entry.set_text(current);
|
||||||
|
|
||||||
return current;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_save: function() {
|
_save: function() {
|
||||||
|
197
js/misc/loginManager.js
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// -*- 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 Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
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 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');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
powerOff: function() {
|
||||||
|
this._proxy.PowerOffRemote(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
reboot: function() {
|
||||||
|
this._proxy.RebootRemote(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const LoginManagerConsoleKit = new Lang.Class({
|
||||||
|
Name: 'LoginManagerConsoleKit',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||||
|
'org.freedesktop.ConsoleKit',
|
||||||
|
'/org/freedesktop/ConsoleKit/Manager');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||||
|
// session objects have different interfaces - but in both cases there are
|
||||||
|
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||||
|
getCurrentSessionProxy: function() {
|
||||||
|
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]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
powerOff: function() {
|
||||||
|
this._proxy.StopRemote();
|
||||||
|
},
|
||||||
|
|
||||||
|
reboot: function() {
|
||||||
|
this._proxy.RestartRemote();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -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 -*-
|
// -*- 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 GLib = imports.gi.GLib;
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
@ -83,24 +80,33 @@ function spawnCommandLine(command_line) {
|
|||||||
// this will throw an error.
|
// this will throw an error.
|
||||||
function trySpawn(argv)
|
function trySpawn(argv)
|
||||||
{
|
{
|
||||||
|
var success, pid;
|
||||||
try {
|
try {
|
||||||
GLib.spawn_async(null, argv, null,
|
[success, pid] = GLib.spawn_async(null, argv, null,
|
||||||
GLib.SpawnFlags.SEARCH_PATH,
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||||
null, null);
|
null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
|
/* Rewrite the error in case of ENOENT */
|
||||||
err.message = _("Command not found");
|
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
|
||||||
} else {
|
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
|
||||||
|
message: _("Command not found") });
|
||||||
|
} else if (err instanceof GLib.Error) {
|
||||||
// The exception from gjs contains an error string like:
|
// The exception from gjs contains an error string like:
|
||||||
// Error invoking GLib.spawn_command_line_async: Failed to
|
// Error invoking GLib.spawn_command_line_async: Failed to
|
||||||
// execute child process "foo" (No such file or directory)
|
// execute child process "foo" (No such file or directory)
|
||||||
// We are only interested in the part in the parentheses. (And
|
// We are only interested in the part in the parentheses. (And
|
||||||
// we can't pattern match the text, since it gets localized.)
|
// we can't pattern match the text, since it gets localized.)
|
||||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
let message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||||
}
|
throw new (err.constructor)({ code: err.code,
|
||||||
|
message: message });
|
||||||
|
} else {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Dummy child watch; we don't want to double-fork internally
|
||||||
|
// because then we lose the parent-child relationship, which
|
||||||
|
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
|
||||||
|
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// trySpawnCommandLine:
|
// trySpawnCommandLine:
|
||||||
@ -144,7 +150,7 @@ function killall(processName) {
|
|||||||
// whatever...
|
// whatever...
|
||||||
|
|
||||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
|
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||||
// It might be useful to return success/failure, but we'd need
|
// It might be useful to return success/failure, but we'd need
|
||||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||||
// the current callers care, we don't bother.
|
// the current callers care, we don't bother.
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const System = imports.system;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Scripting = imports.ui.scripting;
|
const Scripting = imports.ui.scripting;
|
||||||
|
|
||||||
@ -99,7 +101,7 @@ function run() {
|
|||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
|
|
||||||
global.gc();
|
System.gc();
|
||||||
yield Scripting.sleep(1000);
|
yield Scripting.sleep(1000);
|
||||||
Scripting.collectStatistics();
|
Scripting.collectStatistics();
|
||||||
Scripting.scriptEvent('afterShowHide');
|
Scripting.scriptEvent('afterShowHide');
|
||||||
@ -113,10 +115,10 @@ function run() {
|
|||||||
|
|
||||||
for (let i = 0; i < 2; i++) {
|
for (let i = 0; i < 2; i++) {
|
||||||
Scripting.scriptEvent('applicationsShowStart');
|
Scripting.scriptEvent('applicationsShowStart');
|
||||||
Main.overview._viewSelector.switchTab('applications');
|
Main.overview._dash.showAppsButton.checked = true;
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
Scripting.scriptEvent('applicationsShowDone');
|
Scripting.scriptEvent('applicationsShowDone');
|
||||||
Main.overview._viewSelector.switchTab('windows');
|
Main.overview._dash.showAppsButton.checked = false;
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ const Search = imports.ui.search;
|
|||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Workspace = imports.ui.workspace;
|
const Workspace = imports.ui.workspace;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const MAX_APPLICATION_WORK_MILLIS = 75;
|
const MAX_APPLICATION_WORK_MILLIS = 75;
|
||||||
const MENU_POPUP_TIMEOUT = 600;
|
const MENU_POPUP_TIMEOUT = 600;
|
||||||
@ -36,6 +37,7 @@ const AlphabeticalView = new Lang.Class({
|
|||||||
|
|
||||||
this._pendingAppLaterId = 0;
|
this._pendingAppLaterId = 0;
|
||||||
this._appIcons = {}; // desktop file id
|
this._appIcons = {}; // desktop file id
|
||||||
|
this._allApps = [];
|
||||||
|
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||||
@ -60,16 +62,22 @@ const AlphabeticalView = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_removeAll: function() {
|
removeAll: function() {
|
||||||
this._grid.removeAll();
|
this._grid.removeAll();
|
||||||
this._appIcons = {};
|
this._appIcons = {};
|
||||||
|
this._allApps = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
_addApp: function(app) {
|
addApp: function(app) {
|
||||||
var id = app.get_id();
|
var id = app.get_id();
|
||||||
let appIcon = new AppWellIcon(app);
|
if (this._appIcons[id] !== undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
this._grid.addItem(appIcon.actor);
|
let appIcon = new AppWellIcon(app);
|
||||||
|
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
|
||||||
|
return a.compare_by_name(b);
|
||||||
|
});
|
||||||
|
this._grid.addItem(appIcon.actor, pos);
|
||||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||||
|
|
||||||
this._appIcons[id] = appIcon;
|
this._appIcons[id] = appIcon;
|
||||||
@ -120,14 +128,6 @@ const AlphabeticalView = new Lang.Class({
|
|||||||
icon.actor.visible = true;
|
icon.actor.visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
setAppList: function(apps) {
|
|
||||||
this._removeAll();
|
|
||||||
for (var i = 0; i < apps.length; i++) {
|
|
||||||
var app = apps[i];
|
|
||||||
this._addApp(app);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -147,7 +147,6 @@ const ViewByCategories = new Lang.Class({
|
|||||||
// (used only before the actor is mapped the first time)
|
// (used only before the actor is mapped the first time)
|
||||||
this._currentCategory = -2;
|
this._currentCategory = -2;
|
||||||
this._categories = [];
|
this._categories = [];
|
||||||
this._apps = null;
|
|
||||||
|
|
||||||
this._categoryBox = new St.BoxLayout({ vertical: true,
|
this._categoryBox = new St.BoxLayout({ vertical: true,
|
||||||
reactive: true,
|
reactive: true,
|
||||||
@ -204,16 +203,19 @@ const ViewByCategories = new Lang.Class({
|
|||||||
if (nextType == GMenu.TreeItemType.ENTRY) {
|
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||||
var entry = iter.get_entry();
|
var entry = iter.get_entry();
|
||||||
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
||||||
if (!entry.get_app_info().get_nodisplay())
|
if (!entry.get_app_info().get_nodisplay()) {
|
||||||
|
this._view.addApp(app);
|
||||||
appList.push(app);
|
appList.push(app);
|
||||||
|
}
|
||||||
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||||
if (!dir.get_is_nodisplay())
|
var itemDir = iter.get_directory();
|
||||||
this._loadCategory(iter.get_directory(), appList);
|
if (!itemDir.get_is_nodisplay())
|
||||||
|
this._loadCategory(itemDir, appList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_addCategory: function(name, index, dir, allApps) {
|
_addCategory: function(name, index, dir) {
|
||||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||||
style_class: 'app-filter',
|
style_class: 'app-filter',
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
@ -225,7 +227,6 @@ const ViewByCategories = new Lang.Class({
|
|||||||
|
|
||||||
var apps;
|
var apps;
|
||||||
if (dir == null) {
|
if (dir == null) {
|
||||||
apps = allApps;
|
|
||||||
this._allCategoryButton = button;
|
this._allCategoryButton = button;
|
||||||
} else {
|
} else {
|
||||||
apps = [];
|
apps = [];
|
||||||
@ -239,6 +240,7 @@ const ViewByCategories = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_removeAll: function() {
|
_removeAll: function() {
|
||||||
|
this._view.removeAll();
|
||||||
this._categories = [];
|
this._categories = [];
|
||||||
this._categoryBox.destroy_all_children();
|
this._categoryBox.destroy_all_children();
|
||||||
},
|
},
|
||||||
@ -246,13 +248,8 @@ const ViewByCategories = new Lang.Class({
|
|||||||
refresh: function() {
|
refresh: function() {
|
||||||
this._removeAll();
|
this._removeAll();
|
||||||
|
|
||||||
var allApps = Shell.AppSystem.get_default().get_all();
|
|
||||||
allApps.sort(function(a, b) {
|
|
||||||
return a.compare_by_name(b);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Translators: Filter to display all applications */
|
/* Translators: Filter to display all applications */
|
||||||
this._addCategory(_("All"), -1, null, allApps);
|
this._addCategory(_("All"), -1, null);
|
||||||
|
|
||||||
var tree = this._appSystem.get_tree();
|
var tree = this._appSystem.get_tree();
|
||||||
var root = tree.get_root_directory();
|
var root = tree.get_root_directory();
|
||||||
@ -270,7 +267,6 @@ const ViewByCategories = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._view.setAppList(allApps);
|
|
||||||
this._selectCategory(-1);
|
this._selectCategory(-1);
|
||||||
|
|
||||||
if (this._focusDummy) {
|
if (this._focusDummy) {
|
||||||
@ -312,11 +308,10 @@ const AppSearchProvider = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent(_("APPLICATIONS"));
|
this.parent(_("APPLICATIONS"));
|
||||||
|
|
||||||
this._appSys = Shell.AppSystem.get_default();
|
this._appSys = Shell.AppSystem.get_default();
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultMetas: function(apps) {
|
getResultMetas: function(apps, callback) {
|
||||||
let metas = [];
|
let metas = [];
|
||||||
for (let i = 0; i < apps.length; i++) {
|
for (let i = 0; i < apps.length; i++) {
|
||||||
let app = apps[i];
|
let app = apps[i];
|
||||||
@ -327,15 +322,15 @@ const AppSearchProvider = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return metas;
|
callback(metas);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
return this._appSys.initial_search(terms);
|
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
return this._appSys.subsearch(previousResults, terms);
|
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
|
||||||
},
|
},
|
||||||
|
|
||||||
activateResult: function(app, params) {
|
activateResult: function(app, params) {
|
||||||
@ -378,7 +373,7 @@ const SettingsSearchProvider = new Lang.Class({
|
|||||||
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultMetas: function(prefs) {
|
getResultMetas: function(prefs, callback) {
|
||||||
let metas = [];
|
let metas = [];
|
||||||
for (let i = 0; i < prefs.length; i++) {
|
for (let i = 0; i < prefs.length; i++) {
|
||||||
let pref = prefs[i];
|
let pref = prefs[i];
|
||||||
@ -389,15 +384,15 @@ const SettingsSearchProvider = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return metas;
|
callback(metas);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
return this._appSys.search_settings(terms);
|
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
return this._appSys.search_settings(terms);
|
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||||
},
|
},
|
||||||
|
|
||||||
activateResult: function(pref, params) {
|
activateResult: function(pref, params) {
|
||||||
|
@ -1,290 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const GLib = imports.gi.GLib;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Params = imports.misc.params;
|
|
||||||
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
|
||||||
const ScreenSaver = imports.misc.screenSaver;
|
|
||||||
|
|
||||||
// GSettings keys
|
|
||||||
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
|
||||||
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 = [];
|
|
||||||
|
|
||||||
if (!haveSystemd())
|
|
||||||
this.ckListener = new ConsoleKitManager();
|
|
||||||
|
|
||||||
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
|
|
||||||
this._ssProxy.connectSignal('ActiveChanged',
|
|
||||||
Lang.bind(this, this._screenSaverActiveChanged));
|
|
||||||
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
|
||||||
|
|
||||||
this._volumeMonitor.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);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the queue anyway
|
|
||||||
this._volumeQueue = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
_startupMountAll: function() {
|
|
||||||
let volumes = this._volumeMonitor.get_volumes();
|
|
||||||
volumes.forEach(Lang.bind(this, function(volume) {
|
|
||||||
this._checkAndMountVolume(volume, { checkSession: false,
|
|
||||||
useMountOp: false });
|
|
||||||
}));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
isSessionActive: function() {
|
|
||||||
// Return whether the current session is active, using the
|
|
||||||
// right mechanism: either systemd if available or ConsoleKit
|
|
||||||
// as fallback.
|
|
||||||
|
|
||||||
if (haveSystemd())
|
|
||||||
return Shell.session_is_active_for_systemd();
|
|
||||||
|
|
||||||
return this.ckListener.sessionActive;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDriveConnected: function() {
|
|
||||||
// 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)
|
|
||||||
return;
|
|
||||||
|
|
||||||
global.play_theme_sound(0, 'device-added-media');
|
|
||||||
},
|
|
||||||
|
|
||||||
_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)
|
|
||||||
return;
|
|
||||||
|
|
||||||
global.play_theme_sound(0, 'device-removed-media');
|
|
||||||
},
|
|
||||||
|
|
||||||
_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())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// we force stop/eject in this case, so we don't have to pass a
|
|
||||||
// mount operation object
|
|
||||||
if (drive.can_stop()) {
|
|
||||||
drive.stop
|
|
||||||
(Gio.MountUnmountFlags.FORCE, null, null,
|
|
||||||
Lang.bind(this, function(drive, res) {
|
|
||||||
try {
|
|
||||||
drive.stop_finish(res);
|
|
||||||
} catch (e) {
|
|
||||||
log("Unable to stop the drive after drive-eject-button " + e.toString());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else if (drive.can_eject()) {
|
|
||||||
drive.eject_with_operation
|
|
||||||
(Gio.MountUnmountFlags.FORCE, null, null,
|
|
||||||
Lang.bind(this, function(drive, res) {
|
|
||||||
try {
|
|
||||||
drive.eject_with_operation_finish(res);
|
|
||||||
} catch (e) {
|
|
||||||
log("Unable to eject the drive after drive-eject-button " + e.toString());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onVolumeAdded: function(monitor, volume) {
|
|
||||||
this._checkAndMountVolume(volume);
|
|
||||||
},
|
|
||||||
|
|
||||||
_checkAndMountVolume: function(volume, params) {
|
|
||||||
params = Params.parse(params, { checkSession: true,
|
|
||||||
useMountOp: true });
|
|
||||||
|
|
||||||
if (params.checkSession) {
|
|
||||||
// if we're not in the current ConsoleKit session,
|
|
||||||
// don't attempt automount
|
|
||||||
if (!this.isSessionActive())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._ssProxy.screenSaverActive) {
|
|
||||||
if (this._volumeQueue.indexOf(volume) == -1)
|
|
||||||
this._volumeQueue.push(volume);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume is already mounted, don't bother.
|
|
||||||
if (volume.get_mount())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
|
|
||||||
!volume.should_automount() ||
|
|
||||||
!volume.can_mount()) {
|
|
||||||
// allow the autorun to run anyway; this can happen if the
|
|
||||||
// mount gets added programmatically later, even if
|
|
||||||
// should_automount() or can_mount() are false, like for
|
|
||||||
// blank optical media.
|
|
||||||
this._allowAutorun(volume);
|
|
||||||
this._allowAutorunExpire(volume);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.useMountOp) {
|
|
||||||
let operation = new ShellMountOperation.ShellMountOperation(volume);
|
|
||||||
this._mountVolume(volume, operation.mountOp);
|
|
||||||
} else {
|
|
||||||
this._mountVolume(volume, null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_mountVolume: function(volume, operation) {
|
|
||||||
this._allowAutorun(volume);
|
|
||||||
volume.mount(0, operation, null,
|
|
||||||
Lang.bind(this, this._onVolumeMounted));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onVolumeMounted: function(volume, res) {
|
|
||||||
this._allowAutorunExpire(volume);
|
|
||||||
|
|
||||||
try {
|
|
||||||
volume.mount_finish(res);
|
|
||||||
} catch (e) {
|
|
||||||
let string = e.toString();
|
|
||||||
|
|
||||||
// FIXME: needs proper error code handling instead of this
|
|
||||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
|
||||||
if (string.indexOf('No key available with this passphrase') != -1)
|
|
||||||
this._reaskPassword(volume);
|
|
||||||
else
|
|
||||||
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onVolumeRemoved: function(monitor, volume) {
|
|
||||||
this._volumeQueue =
|
|
||||||
this._volumeQueue.filter(function(element) {
|
|
||||||
return (element != volume);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_reaskPassword: function(volume) {
|
|
||||||
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
|
|
||||||
this._mountVolume(volume, operation.mountOp);
|
|
||||||
},
|
|
||||||
|
|
||||||
_allowAutorun: function(volume) {
|
|
||||||
volume.allowAutorun = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_allowAutorunExpire: function(volume) {
|
|
||||||
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
|
||||||
volume.allowAutorun = false;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
@ -9,6 +9,13 @@ const Shell = imports.gi.Shell;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const PopupAnimation = {
|
||||||
|
NONE: 0,
|
||||||
|
SLIDE: 1 << 0,
|
||||||
|
FADE: 1 << 1,
|
||||||
|
FULL: ~0,
|
||||||
|
};
|
||||||
|
|
||||||
const POPUP_ANIMATION_TIME = 0.15;
|
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
|
* An actor which displays a triangle "arrow" pointing to a given
|
||||||
* side. The .bin property is a container in which content can be
|
* 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({
|
const BoxPointer = new Lang.Class({
|
||||||
@ -26,6 +36,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(arrowSide, binProperties) {
|
_init: function(arrowSide, binProperties) {
|
||||||
this._arrowSide = arrowSide;
|
this._arrowSide = arrowSide;
|
||||||
|
this._userArrowSide = arrowSide;
|
||||||
this._arrowOrigin = 0;
|
this._arrowOrigin = 0;
|
||||||
this.actor = new St.Bin({ x_fill: true,
|
this.actor = new St.Bin({ x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
@ -65,11 +76,16 @@ const BoxPointer = new Lang.Class({
|
|||||||
show: function(animate, onComplete) {
|
show: function(animate, onComplete) {
|
||||||
let themeNode = this.actor.get_theme_node();
|
let themeNode = this.actor.get_theme_node();
|
||||||
let rise = themeNode.get_length('-arrow-rise');
|
let rise = themeNode.get_length('-arrow-rise');
|
||||||
|
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
||||||
|
|
||||||
|
if (animate & PopupAnimation.FADE)
|
||||||
this.opacity = 0;
|
this.opacity = 0;
|
||||||
|
else
|
||||||
|
this.opacity = 255;
|
||||||
|
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
|
|
||||||
if (animate) {
|
if (animate & PopupAnimation.SLIDE) {
|
||||||
switch (this._arrowSide) {
|
switch (this._arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
this.yOffset = -rise;
|
this.yOffset = -rise;
|
||||||
@ -95,7 +111,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
if (onComplete)
|
if (onComplete)
|
||||||
onComplete();
|
onComplete();
|
||||||
}),
|
}),
|
||||||
time: POPUP_ANIMATION_TIME });
|
time: animationTime });
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function(animate, onComplete) {
|
hide: function(animate, onComplete) {
|
||||||
@ -103,8 +119,10 @@ const BoxPointer = new Lang.Class({
|
|||||||
let yOffset = 0;
|
let yOffset = 0;
|
||||||
let themeNode = this.actor.get_theme_node();
|
let themeNode = this.actor.get_theme_node();
|
||||||
let rise = themeNode.get_length('-arrow-rise');
|
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) {
|
switch (this._arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
yOffset = rise;
|
yOffset = rise;
|
||||||
@ -123,13 +141,14 @@ const BoxPointer = new Lang.Class({
|
|||||||
|
|
||||||
this._muteInput();
|
this._muteInput();
|
||||||
|
|
||||||
Tweener.addTween(this, { opacity: 0,
|
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
||||||
xOffset: xOffset,
|
xOffset: xOffset,
|
||||||
yOffset: yOffset,
|
yOffset: yOffset,
|
||||||
transition: 'linear',
|
transition: 'linear',
|
||||||
time: POPUP_ANIMATION_TIME,
|
time: animationTime,
|
||||||
onComplete: Lang.bind(this, function () {
|
onComplete: Lang.bind(this, function () {
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
this.opacity = 0;
|
||||||
this.xOffset = 0;
|
this.xOffset = 0;
|
||||||
this.yOffset = 0;
|
this.yOffset = 0;
|
||||||
if (onComplete)
|
if (onComplete)
|
||||||
@ -199,8 +218,27 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this.bin.allocate(childBox, flags);
|
this.bin.allocate(childBox, flags);
|
||||||
|
|
||||||
if (this._sourceActor && this._sourceActor.mapped)
|
if (this._sourceActor && this._sourceActor.mapped) {
|
||||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
this._reposition(this._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) {
|
_drawBorder: function(area) {
|
||||||
@ -214,7 +252,6 @@ const BoxPointer = new Lang.Class({
|
|||||||
let halfBorder = borderWidth / 2;
|
let halfBorder = borderWidth / 2;
|
||||||
let halfBase = Math.floor(base/2);
|
let halfBase = Math.floor(base/2);
|
||||||
|
|
||||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
|
||||||
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
||||||
|
|
||||||
let [width, height] = area.get_surface_size();
|
let [width, height] = area.get_surface_size();
|
||||||
@ -225,7 +262,6 @@ const BoxPointer = new Lang.Class({
|
|||||||
boxWidth -= rise;
|
boxWidth -= rise;
|
||||||
}
|
}
|
||||||
let cr = area.get_context();
|
let cr = area.get_context();
|
||||||
Clutter.cairo_set_source_color(cr, borderColor);
|
|
||||||
|
|
||||||
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
||||||
// with the arrow poking out of that
|
// with the arrow poking out of that
|
||||||
@ -238,14 +274,51 @@ const BoxPointer = new Lang.Class({
|
|||||||
let [x1, y1] = [halfBorder, halfBorder];
|
let [x1, y1] = [halfBorder, halfBorder];
|
||||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
||||||
|
|
||||||
|
let skipTopLeft = false;
|
||||||
|
let skipTopRight = false;
|
||||||
|
let skipBottomLeft = false;
|
||||||
|
let skipBottomRight = false;
|
||||||
|
|
||||||
|
switch (this._arrowSide) {
|
||||||
|
case St.Side.TOP:
|
||||||
|
if (this._arrowOrigin == x1)
|
||||||
|
skipTopLeft = true;
|
||||||
|
else if (this._arrowOrigin == x2)
|
||||||
|
skipTopRight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case St.Side.RIGHT:
|
||||||
|
if (this._arrowOrigin == y1)
|
||||||
|
skipTopRight = true;
|
||||||
|
else if (this._arrowOrigin == y2)
|
||||||
|
skipBottomRight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case St.Side.BOTTOM:
|
||||||
|
if (this._arrowOrigin == x1)
|
||||||
|
skipBottomLeft = true;
|
||||||
|
else if (this._arrowOrigin == x2)
|
||||||
|
skipBottomRight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case St.Side.LEFT:
|
||||||
|
if (this._arrowOrigin == y1)
|
||||||
|
skipTopLeft = true;
|
||||||
|
else if (this._arrowOrigin == y2)
|
||||||
|
skipBottomLeft = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
cr.moveTo(x1 + borderRadius, y1);
|
cr.moveTo(x1 + borderRadius, y1);
|
||||||
if (this._arrowSide == St.Side.TOP) {
|
if (this._arrowSide == St.Side.TOP) {
|
||||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.moveTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
cr.lineTo(x1, y1 - rise);
|
||||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1 + halfBase, y1);
|
||||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
} else if (skipTopRight) {
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.lineTo(x2 - halfBase, y1);
|
||||||
|
cr.lineTo(x2, y1 - rise);
|
||||||
|
cr.lineTo(x2, y1 + borderRadius);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||||
@ -253,19 +326,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipTopRight) {
|
||||||
cr.lineTo(x2 - borderRadius, y1);
|
cr.lineTo(x2 - borderRadius, y1);
|
||||||
|
|
||||||
// top-right corner
|
|
||||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||||
3*Math.PI/2, Math.PI*2);
|
3*Math.PI/2, Math.PI*2);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.RIGHT) {
|
if (this._arrowSide == St.Side.RIGHT) {
|
||||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
if (skipTopRight) {
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, y1);
|
||||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
} else if (skipBottomRight) {
|
||||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
cr.lineTo(x2, y2 - halfBase);
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, y2);
|
||||||
|
cr.lineTo(x2 - borderRadius, y2);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||||
@ -273,19 +347,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipBottomRight) {
|
||||||
cr.lineTo(x2, y2 - borderRadius);
|
cr.lineTo(x2, y2 - borderRadius);
|
||||||
|
|
||||||
// bottom-right corner
|
|
||||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||||
0, Math.PI/2);
|
0, Math.PI/2);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.BOTTOM) {
|
if (this._arrowSide == St.Side.BOTTOM) {
|
||||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
if (skipBottomLeft) {
|
||||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
cr.lineTo(x1 + halfBase, y2);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
cr.lineTo(x1, y2 + rise);
|
||||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
} else if (skipBottomRight) {
|
||||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
cr.lineTo(x2, y2 + rise);
|
||||||
|
cr.lineTo(x2 - halfBase, y2);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||||
@ -293,19 +368,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipBottomLeft) {
|
||||||
cr.lineTo(x1 + borderRadius, y2);
|
cr.lineTo(x1 + borderRadius, y2);
|
||||||
|
|
||||||
// bottom-left corner
|
|
||||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||||
Math.PI/2, Math.PI);
|
Math.PI/2, Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.LEFT) {
|
if (this._arrowSide == St.Side.LEFT) {
|
||||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
cr.lineTo(x1, y1 + halfBase);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
cr.lineTo(x1 - rise, y1);
|
||||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1 + borderRadius, y1);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
} else if (skipBottomLeft) {
|
||||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
cr.lineTo(x1 - rise, y2)
|
||||||
|
cr.lineTo(x1 - rise, y2 - halfBase);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||||
@ -313,20 +389,26 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!skipTopLeft) {
|
||||||
cr.lineTo(x1, y1 + borderRadius);
|
cr.lineTo(x1, y1 + borderRadius);
|
||||||
|
|
||||||
// top-left corner
|
|
||||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||||
Math.PI, 3*Math.PI/2);
|
Math.PI, 3*Math.PI/2);
|
||||||
|
}
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||||
cr.fillPreserve();
|
cr.fillPreserve();
|
||||||
|
|
||||||
|
if (borderWidth > 0) {
|
||||||
|
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||||
Clutter.cairo_set_source_color(cr, borderColor);
|
Clutter.cairo_set_source_color(cr, borderColor);
|
||||||
cr.setLineWidth(borderWidth);
|
cr.setLineWidth(borderWidth);
|
||||||
cr.stroke();
|
cr.stroke();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setPosition: function(sourceActor, alignment) {
|
setPosition: function(sourceActor, alignment) {
|
||||||
|
this._arrowSide = this._userArrowSide;
|
||||||
|
|
||||||
// We need to show it now to force an allocation,
|
// We need to show it now to force an allocation,
|
||||||
// so that we can query the correct size.
|
// so that we can query the correct size.
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
@ -343,11 +425,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
if (!this._sourceActor)
|
if (!this._sourceActor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We need to show it now to force an allocation,
|
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||||
// so that we can query the correct size.
|
|
||||||
this.actor.show();
|
|
||||||
|
|
||||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_reposition: function(sourceActor, alignment) {
|
_reposition: function(sourceActor, alignment) {
|
||||||
@ -368,10 +446,9 @@ const BoxPointer = new Lang.Class({
|
|||||||
let arrowBase = themeNode.get_length('-arrow-base');
|
let arrowBase = themeNode.get_length('-arrow-base');
|
||||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||||
let halfMargin = margin / 2;
|
|
||||||
|
|
||||||
let themeNode = this.actor.get_theme_node();
|
|
||||||
let gap = themeNode.get_length('-boxpointer-gap');
|
let gap = themeNode.get_length('-boxpointer-gap');
|
||||||
|
let padding = themeNode.get_length('-arrow-rise');
|
||||||
|
|
||||||
let resX, resY;
|
let resX, resY;
|
||||||
|
|
||||||
@ -390,29 +467,66 @@ const BoxPointer = new Lang.Class({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now align and position the pointing axis, making sure
|
// Now align and position the pointing axis, making sure it fits on
|
||||||
// it fits on screen
|
// screen. If the arrowOrigin is so close to the edge that the arrow
|
||||||
|
// will not be isosceles, we try to compensate as follows:
|
||||||
|
// - We skip the rounded corner and settle for a right angled arrow
|
||||||
|
// as shown below. See _drawBorder for further details.
|
||||||
|
// |\_____
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// - If the arrow was going to be acute angled, we move the position
|
||||||
|
// of the box to maintain the arrow's accuracy.
|
||||||
|
|
||||||
|
let arrowOrigin;
|
||||||
|
let halfBase = Math.floor(arrowBase/2);
|
||||||
|
let halfBorder = borderWidth / 2;
|
||||||
|
let halfMargin = margin / 2;
|
||||||
|
let [x1, y1] = [halfBorder, halfBorder];
|
||||||
|
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
|
||||||
|
|
||||||
switch (this._arrowSide) {
|
switch (this._arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
case St.Side.BOTTOM:
|
case St.Side.BOTTOM:
|
||||||
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
||||||
|
|
||||||
resX = Math.max(resX, monitor.x + 10);
|
resX = Math.max(resX, monitor.x + padding);
|
||||||
resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth));
|
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
|
||||||
this.setArrowOrigin(sourceCenterX - resX);
|
|
||||||
|
arrowOrigin = sourceCenterX - resX;
|
||||||
|
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin > x1)
|
||||||
|
resX += (arrowOrigin - x1);
|
||||||
|
arrowOrigin = x1;
|
||||||
|
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin < x2)
|
||||||
|
resX -= (x2 - arrowOrigin);
|
||||||
|
arrowOrigin = x2;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case St.Side.LEFT:
|
case St.Side.LEFT:
|
||||||
case St.Side.RIGHT:
|
case St.Side.RIGHT:
|
||||||
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
||||||
|
|
||||||
resY = Math.max(resY, monitor.y + 10);
|
resY = Math.max(resY, monitor.y + padding);
|
||||||
resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight));
|
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
|
||||||
|
|
||||||
this.setArrowOrigin(sourceCenterY - resY);
|
arrowOrigin = sourceCenterY - resY;
|
||||||
|
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin > y1)
|
||||||
|
resY += (arrowOrigin - y1);
|
||||||
|
arrowOrigin = y1;
|
||||||
|
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
|
||||||
|
if (arrowOrigin < y2)
|
||||||
|
resX -= (y2 - arrowOrigin);
|
||||||
|
arrowOrigin = y2;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setArrowOrigin(arrowOrigin);
|
||||||
|
|
||||||
let parent = this.actor.get_parent();
|
let parent = this.actor.get_parent();
|
||||||
let success, x, y;
|
let success, x, y;
|
||||||
while (!success) {
|
while (!success) {
|
||||||
@ -446,6 +560,39 @@ const BoxPointer = new Lang.Class({
|
|||||||
-(this._yPosition + this._yOffset));
|
-(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) {
|
set xOffset(offset) {
|
||||||
this._xOffset = offset;
|
this._xOffset = offset;
|
||||||
this._shiftActor();
|
this._shiftActor();
|
||||||
|
@ -208,8 +208,7 @@ function CalendarServer() {
|
|||||||
g_interface_info: CalendarServerInfo,
|
g_interface_info: CalendarServerInfo,
|
||||||
g_name: 'org.gnome.Shell.CalendarServer',
|
g_name: 'org.gnome.Shell.CalendarServer',
|
||||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
g_object_path: '/org/gnome/Shell/CalendarServer',
|
||||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
|
||||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
|
||||||
|
|
||||||
self.init(null);
|
self.init(null);
|
||||||
return self;
|
return self;
|
||||||
@ -340,22 +339,10 @@ const DBusEventSource = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(DBusEventSource.prototype);
|
Signals.addSignalMethods(DBusEventSource.prototype);
|
||||||
|
|
||||||
// Calendar:
|
|
||||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
|
||||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
|
||||||
const Calendar = new Lang.Class({
|
const Calendar = new Lang.Class({
|
||||||
Name: 'Calendar',
|
Name: 'Calendar',
|
||||||
|
|
||||||
_init: function(eventSource) {
|
_init: function() {
|
||||||
if (eventSource) {
|
|
||||||
this._eventSource = eventSource;
|
|
||||||
|
|
||||||
this._eventSource.connect('changed', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._update(false);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._weekStart = Shell.util_get_week_start();
|
this._weekStart = Shell.util_get_week_start();
|
||||||
this._weekdate = NaN;
|
this._weekdate = NaN;
|
||||||
this._digitWidth = NaN;
|
this._digitWidth = NaN;
|
||||||
@ -392,6 +379,24 @@ const Calendar = new Lang.Class({
|
|||||||
this._buildHeader ();
|
this._buildHeader ();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||||
|
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||||
|
setEventSource: function(eventSource) {
|
||||||
|
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
|
// Sets the calendar to show a specific date
|
||||||
setDate: function(date, forceReload) {
|
setDate: function(date, forceReload) {
|
||||||
if (!_sameDay(date, this._selectedDate)) {
|
if (!_sameDay(date, this._selectedDate)) {
|
||||||
@ -448,7 +453,7 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All the children after this are days, and get removed when we update the calendar
|
// All the children after this are days, and get removed when we update the calendar
|
||||||
this._firstDayIndex = this.actor.get_children().length;
|
this._firstDayIndex = this.actor.get_n_children();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onStyleChange: function(actor, event) {
|
_onStyleChange: function(actor, event) {
|
||||||
@ -551,6 +556,7 @@ const Calendar = new Lang.Class({
|
|||||||
let row = 2;
|
let row = 2;
|
||||||
while (true) {
|
while (true) {
|
||||||
let button = new St.Button({ label: iter.getDate().toString() });
|
let button = new St.Button({ label: iter.getDate().toString() });
|
||||||
|
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||||
|
|
||||||
if (!this._eventSource)
|
if (!this._eventSource)
|
||||||
button.reactive = false;
|
button.reactive = false;
|
||||||
@ -571,7 +577,10 @@ const Calendar = new Lang.Class({
|
|||||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||||
if (row == 2)
|
if (row == 2)
|
||||||
styleClass = 'calendar-day-top ' + styleClass;
|
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;
|
styleClass = 'calendar-day-left ' + styleClass;
|
||||||
|
|
||||||
if (_sameDay(now, iter))
|
if (_sameDay(now, iter))
|
||||||
@ -618,16 +627,25 @@ Signals.addSignalMethods(Calendar.prototype);
|
|||||||
const EventsList = new Lang.Class({
|
const EventsList = new Lang.Class({
|
||||||
Name: 'EventsList',
|
Name: 'EventsList',
|
||||||
|
|
||||||
_init: function(eventSource) {
|
_init: function() {
|
||||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
||||||
this._date = new Date();
|
this._date = new Date();
|
||||||
this._eventSource = eventSource;
|
|
||||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
|
||||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||||
this._weekStart = Shell.util_get_week_start();
|
this._weekStart = Shell.util_get_week_start();
|
||||||
|
},
|
||||||
|
|
||||||
this._update();
|
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) {
|
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||||
@ -713,13 +731,15 @@ const EventsList = new Lang.Class({
|
|||||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
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
|
/* If now is within the first 5 days we show "This week" and
|
||||||
* include events up until and including Saturday/Sunday
|
* include events up until and including Saturday/Sunday
|
||||||
* (depending on whether a week starts on Sunday/Monday).
|
* (depending on whether a week starts on Sunday/Monday).
|
||||||
*/
|
*/
|
||||||
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
||||||
} else {
|
} else {
|
||||||
/* otherwise it's one of the two last days of the week ... show
|
/* otherwise it's one of the two last days of the week ... show
|
||||||
@ -727,7 +747,7 @@ const EventsList = new Lang.Class({
|
|||||||
* Saturday/Sunday
|
* Saturday/Sunday
|
||||||
*/
|
*/
|
||||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
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();
|
||||||
|
}
|
||||||
|
});
|
241
js/ui/components/automountManager.js
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
|
const LoginManager = imports.misc.loginManager;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
|
||||||
|
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
||||||
|
|
||||||
|
// GSettings keys
|
||||||
|
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
||||||
|
const SETTING_ENABLE_AUTOMOUNT = 'automount';
|
||||||
|
|
||||||
|
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
|
||||||
|
|
||||||
|
const AutomountManager = new Lang.Class({
|
||||||
|
Name: 'AutomountManager',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||||
|
this._volumeQueue = [];
|
||||||
|
this._session = new GnomeSession.SessionManager();
|
||||||
|
this._session.connectSignal('InhibitorAdded',
|
||||||
|
Lang.bind(this, this._InhibitorsChanged));
|
||||||
|
this._session.connectSignal('InhibitorRemoved',
|
||||||
|
Lang.bind(this, this._InhibitorsChanged));
|
||||||
|
this._inhibited = false;
|
||||||
|
|
||||||
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
},
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._volumeMonitor.disconnect(this._volumeAddedId);
|
||||||
|
this._volumeMonitor.disconnect(this._volumeRemovedId);
|
||||||
|
this._volumeMonitor.disconnect(this._driveConnectedId);
|
||||||
|
this._volumeMonitor.disconnect(this._driveDisconnectedId);
|
||||||
|
this._volumeMonitor.disconnect(this._driveEjectButtonId);
|
||||||
|
|
||||||
|
if (this._mountAllId > 0) {
|
||||||
|
Mainloop.source_remove(this._mountAllId);
|
||||||
|
this._mountAllId = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
|
||||||
|
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT,
|
||||||
|
Lang.bind(this,
|
||||||
|
function(result, error) {
|
||||||
|
if (!error) {
|
||||||
|
this._inhibited = result[0];
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_startupMountAll: function() {
|
||||||
|
let volumes = this._volumeMonitor.get_volumes();
|
||||||
|
volumes.forEach(Lang.bind(this, function(volume) {
|
||||||
|
this._checkAndMountVolume(volume, { checkSession: false,
|
||||||
|
useMountOp: false,
|
||||||
|
allowAutorun: false });
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._mountAllId = 0;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDriveConnected: function() {
|
||||||
|
// if we're not in the current ConsoleKit session,
|
||||||
|
// or screensaver is active, don't play sounds
|
||||||
|
if (!this._loginManager.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
global.play_theme_sound(0, 'device-added-media');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDriveDisconnected: function() {
|
||||||
|
// if we're not in the current ConsoleKit session,
|
||||||
|
// or screensaver is active, don't play sounds
|
||||||
|
if (!this._loginManager.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
global.play_theme_sound(0, 'device-removed-media');
|
||||||
|
},
|
||||||
|
|
||||||
|
_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._loginManager.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we force stop/eject in this case, so we don't have to pass a
|
||||||
|
// mount operation object
|
||||||
|
if (drive.can_stop()) {
|
||||||
|
drive.stop
|
||||||
|
(Gio.MountUnmountFlags.FORCE, null, null,
|
||||||
|
Lang.bind(this, function(drive, res) {
|
||||||
|
try {
|
||||||
|
drive.stop_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
log("Unable to stop the drive after drive-eject-button " + e.toString());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else if (drive.can_eject()) {
|
||||||
|
drive.eject_with_operation
|
||||||
|
(Gio.MountUnmountFlags.FORCE, null, null,
|
||||||
|
Lang.bind(this, function(drive, res) {
|
||||||
|
try {
|
||||||
|
drive.eject_with_operation_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
log("Unable to eject the drive after drive-eject-button " + e.toString());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVolumeAdded: function(monitor, volume) {
|
||||||
|
this._checkAndMountVolume(volume);
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkAndMountVolume: function(volume, params) {
|
||||||
|
params = Params.parse(params, { checkSession: true,
|
||||||
|
useMountOp: true,
|
||||||
|
allowAutorun: true });
|
||||||
|
|
||||||
|
if (params.checkSession) {
|
||||||
|
// if we're not in the current ConsoleKit session,
|
||||||
|
// don't attempt automount
|
||||||
|
if (!this._loginManager.sessionActive)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._inhibited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Volume is already mounted, don't bother.
|
||||||
|
if (volume.get_mount())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
|
||||||
|
!volume.should_automount() ||
|
||||||
|
!volume.can_mount()) {
|
||||||
|
// allow the autorun to run anyway; this can happen if the
|
||||||
|
// mount gets added programmatically later, even if
|
||||||
|
// should_automount() or can_mount() are false, like for
|
||||||
|
// blank optical media.
|
||||||
|
this._allowAutorun(volume);
|
||||||
|
this._allowAutorunExpire(volume);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.useMountOp) {
|
||||||
|
let operation = new ShellMountOperation.ShellMountOperation(volume);
|
||||||
|
this._mountVolume(volume, operation, params.allowAutorun);
|
||||||
|
} else {
|
||||||
|
this._mountVolume(volume, null, params.allowAutorun);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_mountVolume: function(volume, operation, allowAutorun) {
|
||||||
|
if (allowAutorun)
|
||||||
|
this._allowAutorun(volume);
|
||||||
|
|
||||||
|
let mountOp = operation ? operation.mountOp : null;
|
||||||
|
volume._operation = operation;
|
||||||
|
|
||||||
|
volume.mount(0, mountOp, null,
|
||||||
|
Lang.bind(this, this._onVolumeMounted));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVolumeMounted: function(volume, res) {
|
||||||
|
this._allowAutorunExpire(volume);
|
||||||
|
|
||||||
|
try {
|
||||||
|
volume.mount_finish(res);
|
||||||
|
this._closeOperation(volume);
|
||||||
|
} catch (e) {
|
||||||
|
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
|
||||||
|
// backend in this case, see
|
||||||
|
// https://bugs.freedesktop.org/show_bug.cgi?id=51271
|
||||||
|
if (e.message.indexOf('No key available with this passphrase') != -1) {
|
||||||
|
this._reaskPassword(volume);
|
||||||
|
} else {
|
||||||
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||||
|
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
|
||||||
|
|
||||||
|
this._closeOperation(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVolumeRemoved: function(monitor, volume) {
|
||||||
|
this._volumeQueue =
|
||||||
|
this._volumeQueue.filter(function(element) {
|
||||||
|
return (element != volume);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_reaskPassword: function(volume) {
|
||||||
|
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null;
|
||||||
|
let operation =
|
||||||
|
new ShellMountOperation.ShellMountOperation(volume,
|
||||||
|
{ existingDialog: existingDialog });
|
||||||
|
this._mountVolume(volume, operation);
|
||||||
|
},
|
||||||
|
|
||||||
|
_closeOperation: function(volume) {
|
||||||
|
if (volume._operation)
|
||||||
|
volume._operation.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
_allowAutorun: function(volume) {
|
||||||
|
volume.allowAutorun = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allowAutorunExpire: function(volume) {
|
||||||
|
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
||||||
|
volume.allowAutorun = false;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const Component = AutomountManager;
|
@ -4,6 +4,7 @@ const Lang = imports.lang;
|
|||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const LoginManager = imports.misc.loginManager;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
@ -23,12 +24,14 @@ const AutorunSetting = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// misc utils
|
// misc utils
|
||||||
function ignoreAutorunForMount(mount) {
|
function shouldAutorunMount(mount, forTransient) {
|
||||||
let root = mount.get_root();
|
let root = mount.get_root();
|
||||||
let volume = mount.get_volume();
|
let volume = mount.get_volume();
|
||||||
|
|
||||||
if ((root.is_native() && !isMountRootHidden(root)) ||
|
if (!volume || (!volume.allowAutorun && forTransient))
|
||||||
(volume && volume.allowAutorun && volume.should_automount()))
|
return false;
|
||||||
|
|
||||||
|
if (!root.is_native() || isMountRootHidden(root))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -140,23 +143,44 @@ const AutorunManager = new Lang.Class({
|
|||||||
Name: 'AutorunManager',
|
Name: 'AutorunManager',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
this._volumeMonitor.connect('mount-added',
|
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||||
Lang.bind(this,
|
},
|
||||||
this._onMountAdded));
|
|
||||||
this._volumeMonitor.connect('mount-removed',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._onMountRemoved));
|
|
||||||
|
|
||||||
this._transDispatcher = new AutorunTransientDispatcher();
|
_ensureResidentSource: function() {
|
||||||
this._createResidentSource();
|
if (this._residentSource)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._residentSource = new AutorunResidentSource(this);
|
||||||
|
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
|
||||||
|
this._residentSource.disconnect(destroyId);
|
||||||
|
this._residentSource = null;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
|
||||||
|
_scanMounts: function() {
|
||||||
let mounts = this._volumeMonitor.get_mounts();
|
let mounts = this._volumeMonitor.get_mounts();
|
||||||
|
|
||||||
mounts.forEach(Lang.bind(this, function (mount) {
|
mounts.forEach(Lang.bind(this, function (mount) {
|
||||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||||
function (mount, apps) {
|
function (mount, apps) {
|
||||||
|
this._ensureResidentSource();
|
||||||
this._residentSource.addMount(mount, apps);
|
this._residentSource.addMount(mount, apps);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -164,22 +188,16 @@ const AutorunManager = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_createResidentSource: function() {
|
|
||||||
this._residentSource = new AutorunResidentSource();
|
|
||||||
this._residentSource.connect('destroy',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._createResidentSource));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMountAdded: function(monitor, mount) {
|
_onMountAdded: function(monitor, mount) {
|
||||||
// don't do anything if our session is not the currently
|
// don't do anything if our session is not the currently
|
||||||
// active one
|
// active one
|
||||||
if (!Main.automountManager.isSessionActive())
|
if (!this._loginManager.sessionActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||||
function (mount, apps, contentTypes) {
|
function (mount, apps, contentTypes) {
|
||||||
this._transDispatcher.addMount(mount, apps, contentTypes);
|
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||||
|
this._ensureResidentSource();
|
||||||
this._residentSource.addMount(mount, apps);
|
this._residentSource.addMount(mount, apps);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -188,6 +206,7 @@ const AutorunManager = new Lang.Class({
|
|||||||
|
|
||||||
_onMountRemoved: function(monitor, mount) {
|
_onMountRemoved: function(monitor, mount) {
|
||||||
this._transDispatcher.removeMount(mount);
|
this._transDispatcher.removeMount(mount);
|
||||||
|
if (this._residentSource)
|
||||||
this._residentSource.removeMount(mount);
|
this._residentSource.removeMount(mount);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -224,9 +243,7 @@ const AutorunManager = new Lang.Class({
|
|||||||
try {
|
try {
|
||||||
mount.unmount_with_operation_finish(res);
|
mount.unmount_with_operation_finish(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||||
// but we can't access the error code from JS.
|
|
||||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
|
||||||
log('Unable to eject the mount ' + mount.get_name()
|
log('Unable to eject the mount ' + mount.get_name()
|
||||||
+ ': ' + e.toString());
|
+ ': ' + e.toString());
|
||||||
}
|
}
|
||||||
@ -236,9 +253,7 @@ const AutorunManager = new Lang.Class({
|
|||||||
try {
|
try {
|
||||||
source.eject_with_operation_finish(res);
|
source.eject_with_operation_finish(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||||
// but we can't access the error code from JS.
|
|
||||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
|
||||||
log('Unable to eject the drive ' + source.get_name()
|
log('Unable to eject the drive ' + source.get_name()
|
||||||
+ ': ' + e.toString());
|
+ ': ' + e.toString());
|
||||||
}
|
}
|
||||||
@ -248,9 +263,7 @@ const AutorunManager = new Lang.Class({
|
|||||||
try {
|
try {
|
||||||
drive.stop_finish(res);
|
drive.stop_finish(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||||
// but we can't access the error code from JS.
|
|
||||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
|
||||||
log('Unable to stop the drive ' + drive.get_name()
|
log('Unable to stop the drive ' + drive.get_name()
|
||||||
+ ': ' + e.toString());
|
+ ': ' + e.toString());
|
||||||
}
|
}
|
||||||
@ -261,17 +274,22 @@ const AutorunResidentSource = new Lang.Class({
|
|||||||
Name: 'AutorunResidentSource',
|
Name: 'AutorunResidentSource',
|
||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function() {
|
_init: function(manager) {
|
||||||
this.parent(_("Removable Devices"));
|
this.parent(_("Removable Devices"), 'media-removable');
|
||||||
|
this.showInLockScreen = false;
|
||||||
|
|
||||||
this._mounts = [];
|
this._mounts = [];
|
||||||
|
|
||||||
this._notification = new AutorunResidentNotification(this);
|
this._manager = manager;
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
buildRightClickMenu: function() {
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
addMount: function(mount, apps) {
|
addMount: function(mount, apps) {
|
||||||
if (ignoreAutorunForMount(mount))
|
if (!shouldAutorunMount(mount, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let filtered = this._mounts.filter(function (element) {
|
let filtered = this._mounts.filter(function (element) {
|
||||||
@ -310,12 +328,6 @@ const AutorunResidentSource = new Lang.Class({
|
|||||||
Main.messageTray.add(this);
|
Main.messageTray.add(this);
|
||||||
this.pushNotification(this._notification);
|
this.pushNotification(this._notification);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
|
||||||
return new St.Icon ({ icon_name: 'media-removable',
|
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -323,7 +335,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
Name: 'AutorunResidentNotification',
|
Name: 'AutorunResidentNotification',
|
||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(manager, source) {
|
||||||
this.parent(source, source.title, null, { customContent: true });
|
this.parent(source, source.title, null, { customContent: true });
|
||||||
|
|
||||||
// set the notification as resident
|
// set the notification as resident
|
||||||
@ -331,6 +343,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
|
|
||||||
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
|
this._manager = manager;
|
||||||
|
|
||||||
this.addActor(this._layout,
|
this.addActor(this._layout,
|
||||||
{ x_expand: true,
|
{ x_expand: true,
|
||||||
@ -393,7 +406,7 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
ejectButton.connect('clicked', Lang.bind(this, function() {
|
ejectButton.connect('clicked', Lang.bind(this, function() {
|
||||||
Main.autorunManager.ejectMount(mount);
|
this._manager.ejectMount(mount);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@ -403,7 +416,8 @@ const AutorunResidentNotification = new Lang.Class({
|
|||||||
const AutorunTransientDispatcher = new Lang.Class({
|
const AutorunTransientDispatcher = new Lang.Class({
|
||||||
Name: 'AutorunTransientDispatcher',
|
Name: 'AutorunTransientDispatcher',
|
||||||
|
|
||||||
_init: function() {
|
_init: function(manager) {
|
||||||
|
this._manager = manager;
|
||||||
this._sources = [];
|
this._sources = [];
|
||||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||||
},
|
},
|
||||||
@ -446,7 +460,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// add a new source
|
// add a new source
|
||||||
this._sources.push(new AutorunTransientSource(mount, apps));
|
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
|
||||||
},
|
},
|
||||||
|
|
||||||
addMount: function(mount, apps, contentTypes) {
|
addMount: function(mount, apps, contentTypes) {
|
||||||
@ -455,7 +469,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// if the mount doesn't want to be autorun, return
|
// if the mount doesn't want to be autorun, return
|
||||||
if (ignoreAutorunForMount(mount))
|
if (!shouldAutorunMount(mount, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let setting = this._getAutorunSettingForType(contentTypes[0]);
|
let setting = this._getAutorunSettingForType(contentTypes[0]);
|
||||||
@ -499,23 +513,23 @@ const AutorunTransientSource = new Lang.Class({
|
|||||||
Name: 'AutorunTransientSource',
|
Name: 'AutorunTransientSource',
|
||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(mount, apps) {
|
_init: function(manager, mount, apps) {
|
||||||
this.parent(mount.get_name());
|
this._manager = manager;
|
||||||
|
|
||||||
this.mount = mount;
|
this.mount = mount;
|
||||||
this.apps = apps;
|
this.apps = apps;
|
||||||
|
|
||||||
this._notification = new AutorunTransientNotification(this);
|
this.parent(mount.get_name());
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
|
||||||
|
this._notification = new AutorunTransientNotification(this._manager, this);
|
||||||
|
|
||||||
// add ourselves as a source, and popup the notification
|
// add ourselves as a source, and popup the notification
|
||||||
Main.messageTray.add(this);
|
Main.messageTray.add(this);
|
||||||
this.notify(this._notification);
|
this.notify(this._notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
createIcon: function(size) {
|
||||||
return new St.Icon({ gicon: this.mount.get_icon(),
|
return new St.Icon({ gicon: this.mount.get_icon(),
|
||||||
icon_size: this.ICON_SIZE });
|
icon_size: size });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -523,9 +537,10 @@ const AutorunTransientNotification = new Lang.Class({
|
|||||||
Name: 'AutorunTransientNotification',
|
Name: 'AutorunTransientNotification',
|
||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(manager, source) {
|
||||||
this.parent(source, source.title, null, { customContent: true });
|
this.parent(source, source.title, null, { customContent: true });
|
||||||
|
|
||||||
|
this._manager = manager;
|
||||||
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this.addActor(this._box);
|
this.addActor(this._box);
|
||||||
@ -594,10 +609,11 @@ const AutorunTransientNotification = new Lang.Class({
|
|||||||
style_class: 'hotplug-notification-item' });
|
style_class: 'hotplug-notification-item' });
|
||||||
|
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
Main.autorunManager.ejectMount(this._mount);
|
this._manager.ejectMount(this._mount);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Component = AutorunManager;
|
@ -65,7 +65,8 @@ const KeyringDialog = new Lang.Class({
|
|||||||
key: Clutter.Escape
|
key: Clutter.Escape
|
||||||
},
|
},
|
||||||
{ label: '',
|
{ label: '',
|
||||||
action: Lang.bind(this, this._onContinueButton)
|
action: Lang.bind(this, this._onContinueButton),
|
||||||
|
default: true
|
||||||
}]
|
}]
|
||||||
|
|
||||||
this.setButtons(buttons);
|
this.setButtons(buttons);
|
||||||
@ -83,7 +84,10 @@ const KeyringDialog = new Lang.Class({
|
|||||||
if (this.prompt.password_visible) {
|
if (this.prompt.password_visible) {
|
||||||
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
label.set_text(_("Password:"));
|
label.set_text(_("Password:"));
|
||||||
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
|
table.add(label, { row: row, col: 0,
|
||||||
|
x_expand: false, x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: '',
|
text: '',
|
||||||
can_focus: true});
|
can_focus: true});
|
||||||
@ -99,7 +103,10 @@ const KeyringDialog = new Lang.Class({
|
|||||||
if (this.prompt.confirm_visible) {
|
if (this.prompt.confirm_visible) {
|
||||||
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
label.set_text(_("Type again:"));
|
label.set_text(_("Type again:"));
|
||||||
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
|
table.add(label, { row: row, col: 0,
|
||||||
|
x_expand: false, x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: '',
|
text: '',
|
||||||
can_focus: true});
|
can_focus: true});
|
||||||
@ -185,23 +192,36 @@ const KeyringDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onContinueButton: function() {
|
_onContinueButton: function() {
|
||||||
this.prompt.complete()
|
this.prompt.complete();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancelButton: function() {
|
_onCancelButton: function() {
|
||||||
this.prompt.cancel()
|
this.prompt.cancel();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function init() {
|
const KeyringPrompter = new Lang.Class({
|
||||||
prompter = new Gcr.SystemPrompter();
|
Name: 'KeyringPrompter',
|
||||||
prompter.connect('new-prompt', function(prompter) {
|
|
||||||
|
_init: function() {
|
||||||
|
this._prompter = new Gcr.SystemPrompter();
|
||||||
|
this._prompter.connect('new-prompt', function(prompter) {
|
||||||
let dialog = new KeyringDialog();
|
let dialog = new KeyringDialog();
|
||||||
return dialog.prompt;
|
return dialog.prompt;
|
||||||
});
|
});
|
||||||
|
this._dbusId = null;
|
||||||
|
},
|
||||||
|
|
||||||
let connection = Gio.DBus.session;
|
enable: function() {
|
||||||
prompter.register(connection);
|
this._prompter.register(Gio.DBus.session);
|
||||||
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
|
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
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 -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
/*
|
|
||||||
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
@ -135,7 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
} else
|
} else
|
||||||
secret.valid = true;
|
secret.valid = true;
|
||||||
|
|
||||||
secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START });
|
secretTable.add(label, { row: pos, col: 0,
|
||||||
|
x_expand: false, x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
||||||
pos++;
|
pos++;
|
||||||
|
|
||||||
@ -147,7 +131,7 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._okButton = { label: _("Connect"),
|
this._okButton = { label: _("Connect"),
|
||||||
action: Lang.bind(this, this._onOk),
|
action: Lang.bind(this, this._onOk),
|
||||||
key: Clutter.KEY_Return,
|
default: true
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setButtons([{ label: _("Cancel"),
|
this.setButtons([{ label: _("Cancel"),
|
||||||
@ -166,10 +150,6 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._okButton.button.reactive = valid;
|
this._okButton.button.reactive = valid;
|
||||||
this._okButton.button.can_focus = valid;
|
this._okButton.button.can_focus = valid;
|
||||||
if (valid)
|
|
||||||
this._okButton.button.remove_style_pseudo_class('disabled');
|
|
||||||
else
|
|
||||||
this._okButton.button.add_style_pseudo_class('disabled');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOk: function() {
|
_onOk: function() {
|
||||||
@ -604,7 +584,7 @@ const NetworkAgent = new Lang.Class({
|
|||||||
Name: 'NetworkAgent',
|
Name: 'NetworkAgent',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._native = new Shell.NetworkAgent({ auto_register: true,
|
this._native = new Shell.NetworkAgent({ auto_register: false,
|
||||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||||
|
|
||||||
this._dialogs = { };
|
this._dialogs = { };
|
||||||
@ -614,6 +594,14 @@ const NetworkAgent = new Lang.Class({
|
|||||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
this._native.register();
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._native.unregister();
|
||||||
|
},
|
||||||
|
|
||||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||||
if (settingName == 'vpn') {
|
if (settingName == 'vpn') {
|
||||||
this._vpnRequest(requestId, connection, hints, flags);
|
this._vpnRequest(requestId, connection, hints, flags);
|
||||||
@ -683,7 +671,10 @@ const NetworkAgent = new Lang.Class({
|
|||||||
try {
|
try {
|
||||||
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
|
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
|
||||||
} catch(e) { } // ignore errors if key does not exist
|
} 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))
|
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
|
||||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
|
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
|
||||||
@ -700,3 +691,4 @@ const NetworkAgent = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const Component = NetworkAgent;
|
@ -1,24 +1,4 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
/*
|
|
||||||
* Copyright 2010 Red Hat, Inc
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* Author: David Zeuthen <davidz@redhat.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
@ -33,8 +13,12 @@ const Mainloop = imports.mainloop;
|
|||||||
const Polkit = imports.gi.Polkit;
|
const Polkit = imports.gi.Polkit;
|
||||||
const PolkitAgent = imports.gi.PolkitAgent;
|
const PolkitAgent = imports.gi.PolkitAgent;
|
||||||
|
|
||||||
|
const Components = imports.ui.components;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const ShellEntry = imports.ui.shellEntry;
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
const UserMenu = imports.ui.userMenu;
|
||||||
|
|
||||||
|
const DIALOG_ICON_SIZE = 48;
|
||||||
|
|
||||||
const AuthenticationDialog = new Lang.Class({
|
const AuthenticationDialog = new Lang.Class({
|
||||||
Name: 'AuthenticationDialog',
|
Name: 'AuthenticationDialog',
|
||||||
@ -117,9 +101,11 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
messageBox.add(userBox);
|
messageBox.add(userBox);
|
||||||
this._userIcon = new St.Icon();
|
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
|
||||||
this._userIcon.hide();
|
{ iconSize: DIALOG_ICON_SIZE,
|
||||||
userBox.add(this._userIcon,
|
styleClass: 'polkit-dialog-user-icon' });
|
||||||
|
this._userAvatar.actor.hide();
|
||||||
|
userBox.add(this._userAvatar.actor,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
x_align: St.Align.END,
|
x_align: St.Align.END,
|
||||||
@ -135,17 +121,17 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._onUserChanged();
|
this._onUserChanged();
|
||||||
|
|
||||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
|
||||||
messageBox.add(this._passwordBox);
|
messageBox.add(this._passwordBox);
|
||||||
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
this._passwordBox.add(this._passwordLabel);
|
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: "",
|
text: "",
|
||||||
can_focus: true});
|
can_focus: true});
|
||||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||||
this._passwordBox.add(this._passwordEntry,
|
this._passwordBox.add(this._passwordEntry,
|
||||||
{expand: true });
|
{ expand: true });
|
||||||
this.setInitialKeyFocus(this._passwordEntry);
|
this.setInitialKeyFocus(this._passwordEntry);
|
||||||
this._passwordBox.hide();
|
this._passwordBox.hide();
|
||||||
|
|
||||||
@ -167,6 +153,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
|
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
|
||||||
text: 'abc'});
|
text: 'abc'});
|
||||||
|
this._nullMessageLabel.add_style_class_name('hidden');
|
||||||
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
this._nullMessageLabel.clutter_text.line_wrap = true;
|
this._nullMessageLabel.clutter_text.line_wrap = true;
|
||||||
messageBox.add(this._nullMessageLabel);
|
messageBox.add(this._nullMessageLabel);
|
||||||
@ -177,7 +164,8 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
key: Clutter.Escape
|
key: Clutter.Escape
|
||||||
},
|
},
|
||||||
{ label: _("Authenticate"),
|
{ label: _("Authenticate"),
|
||||||
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||||
|
default: true
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
this._doneEmitted = false;
|
this._doneEmitted = false;
|
||||||
@ -268,7 +256,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
|
|
||||||
_onSessionRequest: function(session, request, echo_on) {
|
_onSessionRequest: function(session, request, echo_on) {
|
||||||
// Cheap localization trick
|
// Cheap localization trick
|
||||||
if (request == 'Password:')
|
if (request == 'Password:' || request == 'Password: ')
|
||||||
this._passwordLabel.set_text(_("Password:"));
|
this._passwordLabel.set_text(_("Password:"));
|
||||||
else
|
else
|
||||||
this._passwordLabel.set_text(request);
|
this._passwordLabel.set_text(request);
|
||||||
@ -311,19 +299,9 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onUserChanged: function() {
|
_onUserChanged: function() {
|
||||||
if (this._user.is_loaded) {
|
if (this._user.is_loaded && this._userAvatar) {
|
||||||
if (this._userIcon) {
|
this._userAvatar.update();
|
||||||
let iconFileName = this._user.get_icon_file();
|
this._userAvatar.actor.show();
|
||||||
let iconFile = Gio.file_new_for_path(iconFileName);
|
|
||||||
let icon;
|
|
||||||
if (iconFile.query_exists(null)) {
|
|
||||||
icon = new Gio.FileIcon({file: iconFile});
|
|
||||||
} else {
|
|
||||||
icon = new Gio.ThemedIcon({name: 'avatar-default'});
|
|
||||||
}
|
|
||||||
this._userIcon.set_gicon (icon);
|
|
||||||
this._userIcon.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -339,11 +317,20 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
Name: 'AuthenticationAgent',
|
Name: 'AuthenticationAgent',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this._currentDialog = null;
|
||||||
|
this._isCompleting = false;
|
||||||
|
this._handle = null;
|
||||||
this._native = new Shell.PolkitAuthenticationAgent();
|
this._native = new Shell.PolkitAuthenticationAgent();
|
||||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||||
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||||
this._currentDialog = null;
|
},
|
||||||
this._isCompleting = false;
|
|
||||||
|
enable: function() {
|
||||||
|
this._native.register();
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._native.unregister();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||||
@ -401,6 +388,4 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function init() {
|
const Component = AuthenticationAgent;
|
||||||
let agent = new AuthenticationAgent();
|
|
||||||
}
|
|
58
js/ui/components/recorder.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
global.display.add_keybinding('toggle-recording',
|
||||||
|
this._bindingSettings,
|
||||||
|
Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder));
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
global.display.remove_keybinding('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_filename(_("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 Tp = imports.gi.TelepathyGLib;
|
||||||
|
|
||||||
const History = imports.misc.history;
|
const History = imports.misc.history;
|
||||||
const Params = imports.misc.params;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
// See Notification.appendMessage
|
// See Notification.appendMessage
|
||||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
||||||
@ -63,10 +63,10 @@ function makeMessageFromTplEvent(event) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Client = new Lang.Class({
|
const TelepathyClient = new Lang.Class({
|
||||||
Name: 'Client',
|
Name: 'TelepathyClient',
|
||||||
|
|
||||||
_init : function() {
|
_init: function() {
|
||||||
// channel path -> ChatSource
|
// channel path -> ChatSource
|
||||||
this._chatSources = {};
|
this._chatSources = {};
|
||||||
this._chatState = Tp.ChannelChatState.ACTIVE;
|
this._chatState = Tp.ChannelChatState.ACTIVE;
|
||||||
@ -89,9 +89,9 @@ const Client = new Lang.Class({
|
|||||||
// channel matching its filters is detected.
|
// channel matching its filters is detected.
|
||||||
// The second argument, recover, means _observeChannels will be run
|
// The second argument, recover, means _observeChannels will be run
|
||||||
// for any existing channel as well.
|
// for any existing channel as well.
|
||||||
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
|
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
|
||||||
'name': 'GnomeShell',
|
account_manager: this._accountManager,
|
||||||
'uniquify-name': true })
|
uniquify_name: true });
|
||||||
this._tpClient.set_observe_channels_func(
|
this._tpClient.set_observe_channels_func(
|
||||||
Lang.bind(this, this._observeChannels));
|
Lang.bind(this, this._observeChannels));
|
||||||
this._tpClient.set_approve_channels_func(
|
this._tpClient.set_approve_channels_func(
|
||||||
@ -99,6 +99,10 @@ const Client = new Lang.Class({
|
|||||||
this._tpClient.set_handle_channels_func(
|
this._tpClient.set_handle_channels_func(
|
||||||
Lang.bind(this, this._handleChannels));
|
Lang.bind(this, this._handleChannels));
|
||||||
|
|
||||||
|
// Watch subscription requests and connection errors
|
||||||
|
this._subscriptionSource = null;
|
||||||
|
this._accountSource = null;
|
||||||
|
|
||||||
// Workaround for gjs not supporting GPtrArray in signals.
|
// Workaround for gjs not supporting GPtrArray in signals.
|
||||||
// See BGO bug #653941 for context.
|
// See BGO bug #653941 for context.
|
||||||
this._tpClient.set_contact_list_changed_func(
|
this._tpClient.set_contact_list_changed_func(
|
||||||
@ -108,23 +112,28 @@ const Client = new Lang.Class({
|
|||||||
// needed
|
// needed
|
||||||
this._tpClient.set_delegated_channels_callback(
|
this._tpClient.set_delegated_channels_callback(
|
||||||
Lang.bind(this, this._delegatedChannelsCb));
|
Lang.bind(this, this._delegatedChannelsCb));
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
try {
|
try {
|
||||||
this._tpClient.register();
|
this._tpClient.register();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch subscription requests and connection errors
|
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
|
||||||
this._subscriptionSource = null;
|
|
||||||
this._accountSource = null;
|
|
||||||
|
|
||||||
this._accountManager.connect('account-validity-changed',
|
|
||||||
Lang.bind(this, this._accountValidityChanged));
|
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,
|
_observeChannels: function(observer, account, conn, channels,
|
||||||
dispatchOp, requests, context) {
|
dispatchOp, requests, context) {
|
||||||
let len = channels.length;
|
let len = channels.length;
|
||||||
@ -132,6 +141,9 @@ const Client = new Lang.Class({
|
|||||||
let channel = channels[i];
|
let channel = channels[i];
|
||||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||||
|
|
||||||
|
if (channel.get_invalidated())
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Only observe contact text channels */
|
/* Only observe contact text channels */
|
||||||
if ((!(channel instanceof Tp.TextChannel)) ||
|
if ((!(channel instanceof Tp.TextChannel)) ||
|
||||||
targetHandleType != Tp.HandleType.CONTACT)
|
targetHandleType != Tp.HandleType.CONTACT)
|
||||||
@ -181,6 +193,9 @@ const Client = new Lang.Class({
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (channel.get_invalidated())
|
||||||
|
continue;
|
||||||
|
|
||||||
// 'notify' will be true when coming from an actual HandleChannels
|
// 'notify' will be true when coming from an actual HandleChannels
|
||||||
// call, and not when from a successful Claim call. The point is
|
// call, and not when from a successful Claim call. The point is
|
||||||
// we don't want to notify for a channel we just claimed which
|
// we don't want to notify for a channel we just claimed which
|
||||||
@ -205,13 +220,15 @@ const Client = new Lang.Class({
|
|||||||
// We can only approve the rooms if we have been invited to it
|
// We can only approve the rooms if we have been invited to it
|
||||||
let selfContact = channel.group_get_self_contact();
|
let selfContact = channel.group_get_self_contact();
|
||||||
if (selfContact == null) {
|
if (selfContact == null) {
|
||||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Not invited to the room' }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
|
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
|
||||||
if (!invited) {
|
if (!invited) {
|
||||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Not invited to the room' }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,12 +248,21 @@ const Client = new Lang.Class({
|
|||||||
let channel = channels[0];
|
let channel = channels[0];
|
||||||
let chanType = channel.get_channel_type();
|
let chanType = channel.get_channel_type();
|
||||||
|
|
||||||
|
if (channel.get_invalidated()) {
|
||||||
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Channel is invalidated' }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
|
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
|
||||||
this._approveTextChannel(account, conn, channel, dispatchOp, context);
|
this._approveTextChannel(account, conn, channel, dispatchOp, context);
|
||||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
|
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
|
||||||
this._approveCall(account, conn, channel, dispatchOp, context);
|
this._approveCall(account, conn, channel, dispatchOp, context);
|
||||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
||||||
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
||||||
|
else
|
||||||
|
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
|
||||||
|
message: 'Unsupported channel type' }));
|
||||||
},
|
},
|
||||||
|
|
||||||
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
||||||
@ -365,8 +391,8 @@ const Client = new Lang.Class({
|
|||||||
|
|
||||||
_ensureSubscriptionSource: function() {
|
_ensureSubscriptionSource: function() {
|
||||||
if (this._subscriptionSource == null) {
|
if (this._subscriptionSource == null) {
|
||||||
this._subscriptionSource = new MultiNotificationSource(
|
this._subscriptionSource = new MessageTray.Source(_("Subscription request"),
|
||||||
_("Subscription request"), 'gtk-dialog-question');
|
'gtk-dialog-question');
|
||||||
Main.messageTray.add(this._subscriptionSource);
|
Main.messageTray.add(this._subscriptionSource);
|
||||||
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
||||||
this._subscriptionSource = null;
|
this._subscriptionSource = null;
|
||||||
@ -401,8 +427,8 @@ const Client = new Lang.Class({
|
|||||||
|
|
||||||
_ensureAccountSource: function() {
|
_ensureAccountSource: function() {
|
||||||
if (this._accountSource == null) {
|
if (this._accountSource == null) {
|
||||||
this._accountSource = new MultiNotificationSource(
|
this._accountSource = new MessageTray.Source(_("Connection error"),
|
||||||
_("Connection error"), 'gtk-dialog-error');
|
'gtk-dialog-error');
|
||||||
Main.messageTray.add(this._accountSource);
|
Main.messageTray.add(this._accountSource);
|
||||||
this._accountSource.connect('destroy', Lang.bind(this, function () {
|
this._accountSource.connect('destroy', Lang.bind(this, function () {
|
||||||
this._accountSource = null;
|
this._accountSource = null;
|
||||||
@ -418,14 +444,13 @@ const ChatSource = new Lang.Class({
|
|||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(account, conn, channel, contact, client) {
|
_init: function(account, conn, channel, contact, client) {
|
||||||
this.parent(contact.get_alias());
|
|
||||||
|
|
||||||
this.isChat = true;
|
|
||||||
|
|
||||||
this._account = account;
|
this._account = account;
|
||||||
this._contact = contact;
|
this._contact = contact;
|
||||||
this._client = client;
|
this._client = client;
|
||||||
|
|
||||||
|
this.parent(contact.get_alias());
|
||||||
|
|
||||||
|
this.isChat = true;
|
||||||
this._pendingMessages = [];
|
this._pendingMessages = [];
|
||||||
|
|
||||||
this._conn = conn;
|
this._conn = conn;
|
||||||
@ -446,8 +471,6 @@ const ChatSource = new Lang.Class({
|
|||||||
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
|
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
|
||||||
this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved));
|
this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved));
|
||||||
|
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
|
||||||
|
|
||||||
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
|
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
|
||||||
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
|
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
|
||||||
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
|
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
|
||||||
@ -459,6 +482,22 @@ const ChatSource = new Lang.Class({
|
|||||||
this._getLogMessages();
|
this._getLogMessages();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildRightClickMenu: function() {
|
||||||
|
let item;
|
||||||
|
|
||||||
|
let rightClickMenu = this.parent();
|
||||||
|
item = new PopupMenu.PopupMenuItem('');
|
||||||
|
item.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||||
|
item.label.set_text(this.isMuted ? _("Unmute") : _("Mute"));
|
||||||
|
}));
|
||||||
|
item.connect('activate', Lang.bind(this, function() {
|
||||||
|
this.setMuted(!this.isMuted);
|
||||||
|
this.emit('done-displaying-content');
|
||||||
|
}));
|
||||||
|
rightClickMenu.add(item.actor);
|
||||||
|
return rightClickMenu;
|
||||||
|
},
|
||||||
|
|
||||||
_updateAlias: function() {
|
_updateAlias: function() {
|
||||||
let oldAlias = this.title;
|
let oldAlias = this.title;
|
||||||
let newAlias = this._contact.get_alias();
|
let newAlias = this._contact.get_alias();
|
||||||
@ -470,9 +509,9 @@ const ChatSource = new Lang.Class({
|
|||||||
this._notification.appendAliasChange(oldAlias, newAlias);
|
this._notification.appendAliasChange(oldAlias, newAlias);
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
createIcon: function(size) {
|
||||||
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||||
this._iconBox._size = this.ICON_SIZE;
|
this._iconBox._size = size;
|
||||||
let textureCache = St.TextureCache.get_default();
|
let textureCache = St.TextureCache.get_default();
|
||||||
let file = this._contact.get_avatar_file();
|
let file = this._contact.get_avatar_file();
|
||||||
|
|
||||||
@ -481,16 +520,45 @@ const ChatSource = new Lang.Class({
|
|||||||
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
|
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
|
||||||
} else {
|
} else {
|
||||||
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: this._iconBox._size });
|
icon_size: this._iconBox._size });
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._iconBox;
|
return this._iconBox;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createSecondaryIcon: function() {
|
||||||
|
let iconBox = new St.Bin();
|
||||||
|
iconBox.child = new St.Icon({ style_class: 'secondary-icon' });
|
||||||
|
let presenceType = this._contact.get_presence_type();
|
||||||
|
|
||||||
|
switch (presenceType) {
|
||||||
|
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||||
|
iconBox.child.icon_name = 'user-available';
|
||||||
|
break;
|
||||||
|
case Tp.ConnectionPresenceType.BUSY:
|
||||||
|
iconBox.child.icon_name = 'user-busy';
|
||||||
|
break;
|
||||||
|
case Tp.ConnectionPresenceType.OFFLINE:
|
||||||
|
iconBox.child.icon_name = 'user-offline';
|
||||||
|
break;
|
||||||
|
case Tp.ConnectionPresenceType.HIDDEN:
|
||||||
|
iconBox.child.icon_name = 'user-invisible';
|
||||||
|
break;
|
||||||
|
case Tp.ConnectionPresenceType.AWAY:
|
||||||
|
iconBox.child.icon_name = 'user-away';
|
||||||
|
break;
|
||||||
|
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||||
|
iconBox.child.icon_name = 'user-idle';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iconBox.child.icon_name = 'user-offline';
|
||||||
|
}
|
||||||
|
return iconBox;
|
||||||
|
},
|
||||||
|
|
||||||
_updateAvatarIcon: function() {
|
_updateAvatarIcon: function() {
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
this.iconUpdated();
|
||||||
this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
|
this._notification.update(this._notification.title, null, { customContent: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function(notification) {
|
||||||
@ -510,10 +578,10 @@ const ChatSource = new Lang.Class({
|
|||||||
_getLogMessages: function() {
|
_getLogMessages: function() {
|
||||||
let logManager = Tpl.LogManager.dup_singleton();
|
let logManager = Tpl.LogManager.dup_singleton();
|
||||||
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
|
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
|
||||||
Shell.get_contact_events(logManager,
|
|
||||||
this._account, entity,
|
logManager.get_filtered_events_async(this._account, entity,
|
||||||
SCROLLBACK_HISTORY_LINES,
|
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
|
||||||
Lang.bind(this, this._displayPendingMessages));
|
null, Lang.bind(this, this._displayPendingMessages));
|
||||||
},
|
},
|
||||||
|
|
||||||
_displayPendingMessages: function(logManager, result) {
|
_displayPendingMessages: function(logManager, result) {
|
||||||
@ -535,7 +603,7 @@ const ChatSource = new Lang.Class({
|
|||||||
this._pendingMessages.push(message);
|
this._pendingMessages.push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateCount();
|
this.countUpdated();
|
||||||
|
|
||||||
let showTimestamp = false;
|
let showTimestamp = false;
|
||||||
|
|
||||||
@ -581,8 +649,17 @@ const ChatSource = new Lang.Class({
|
|||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateCount: function() {
|
/* All messages are new messages for Telepathy sources */
|
||||||
this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0);
|
get count() {
|
||||||
|
return this._pendingMessages.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
get unseenCount() {
|
||||||
|
return this.count;
|
||||||
|
},
|
||||||
|
|
||||||
|
get countVisible() {
|
||||||
|
return this.count > 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_messageReceived: function(channel, message) {
|
_messageReceived: function(channel, message) {
|
||||||
@ -590,7 +667,7 @@ const ChatSource = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._pendingMessages.push(message);
|
this._pendingMessages.push(message);
|
||||||
this._updateCount();
|
this.countUpdated();
|
||||||
|
|
||||||
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
||||||
this._notification.appendMessage(message);
|
this._notification.appendMessage(message);
|
||||||
@ -652,38 +729,14 @@ const ChatSource = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_presenceChanged: function (contact, presence, status, message) {
|
_presenceChanged: function (contact, presence, status, message) {
|
||||||
let msg, shouldNotify, title;
|
let msg, title;
|
||||||
|
|
||||||
if (this._presence == presence)
|
|
||||||
return;
|
|
||||||
|
|
||||||
title = GLib.markup_escape_text(this.title, -1);
|
title = GLib.markup_escape_text(this.title, -1);
|
||||||
|
|
||||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE) {
|
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() });
|
||||||
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;
|
|
||||||
|
|
||||||
if (message)
|
if (message)
|
||||||
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
||||||
|
|
||||||
this._notification.appendPresence(msg, shouldNotify);
|
|
||||||
if (shouldNotify)
|
|
||||||
this.notify();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_pendingRemoved: function(channel, message) {
|
_pendingRemoved: function(channel, message) {
|
||||||
@ -691,10 +744,8 @@ const ChatSource = new Lang.Class({
|
|||||||
|
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
this._pendingMessages.splice(idx, 1);
|
this._pendingMessages.splice(idx, 1);
|
||||||
this._updateCount();
|
this.countUpdated();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
throw new Error('Message not in our pending list: ' + message);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ackMessages: function() {
|
_ackMessages: function() {
|
||||||
@ -710,7 +761,7 @@ const ChatNotification = new Lang.Class({
|
|||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(source) {
|
||||||
this.parent(source, source.title, null, { customContent: true });
|
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() });
|
||||||
this.setResident(true);
|
this.setResident(true);
|
||||||
|
|
||||||
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
||||||
@ -719,6 +770,14 @@ const ChatNotification = new Lang.Class({
|
|||||||
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
|
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
|
||||||
this.setActionArea(this._responseEntry);
|
this.setActionArea(this._responseEntry);
|
||||||
|
|
||||||
|
this._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._oldMaxScrollAdjustment = 0;
|
this._oldMaxScrollAdjustment = 0;
|
||||||
this._createScrollArea();
|
this._createScrollArea();
|
||||||
this._lastGroup = null;
|
this._lastGroup = null;
|
||||||
@ -803,7 +862,7 @@ const ChatNotification = new Lang.Class({
|
|||||||
let groups = this._contentArea.get_children();
|
let groups = this._contentArea.get_children();
|
||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < groups.length; i++) {
|
||||||
let group = groups[i];
|
let group = groups[i];
|
||||||
if (group.get_children().length == 0)
|
if (group.get_n_children() == 0)
|
||||||
group.destroy();
|
group.destroy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -853,6 +912,8 @@ const ChatNotification = new Lang.Class({
|
|||||||
|
|
||||||
this._lastGroupActor.add(body, props.childProps);
|
this._lastGroupActor.add(body, props.childProps);
|
||||||
|
|
||||||
|
this.updated();
|
||||||
|
|
||||||
let timestamp = props.timestamp;
|
let timestamp = props.timestamp;
|
||||||
this._history.unshift({ actor: body, time: timestamp,
|
this._history.unshift({ actor: body, time: timestamp,
|
||||||
realMessage: group != 'meta' });
|
realMessage: group != 'meta' });
|
||||||
@ -918,19 +979,6 @@ const ChatNotification = new Lang.Class({
|
|||||||
return false;
|
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) {
|
appendAliasChange: function(oldAlias, newAlias) {
|
||||||
oldAlias = GLib.markup_escape_text(oldAlias, -1);
|
oldAlias = GLib.markup_escape_text(oldAlias, -1);
|
||||||
newAlias = GLib.markup_escape_text(newAlias, -1);
|
newAlias = GLib.markup_escape_text(newAlias, -1);
|
||||||
@ -1000,10 +1048,9 @@ const ApproverSource = new Lang.Class({
|
|||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(dispatchOp, text, gicon) {
|
_init: function(dispatchOp, text, gicon) {
|
||||||
this.parent(text);
|
|
||||||
|
|
||||||
this._gicon = gicon;
|
this._gicon = gicon;
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
|
||||||
|
this.parent(text);
|
||||||
|
|
||||||
this._dispatchOp = dispatchOp;
|
this._dispatchOp = dispatchOp;
|
||||||
|
|
||||||
@ -1024,10 +1071,9 @@ const ApproverSource = new Lang.Class({
|
|||||||
this.parent();
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
createIcon: function(size) {
|
||||||
return new St.Icon({ gicon: this._gicon,
|
return new St.Icon({ gicon: this._gicon,
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
icon_size: size });
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1149,40 +1195,6 @@ const FileTransferNotification = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// A notification source that can embed multiple notifications
|
|
||||||
const MultiNotificationSource = new Lang.Class({
|
|
||||||
Name: 'MultiNotificationSource',
|
|
||||||
Extends: MessageTray.Source,
|
|
||||||
|
|
||||||
_init: function(title, icon) {
|
|
||||||
this.parent(title);
|
|
||||||
|
|
||||||
this._icon = icon;
|
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
|
||||||
this._nbNotifications = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
notify: function(notification) {
|
|
||||||
this.parent(notification);
|
|
||||||
|
|
||||||
this._nbNotifications += 1;
|
|
||||||
|
|
||||||
// Display the source while there is at least one notification
|
|
||||||
notification.connect('destroy', Lang.bind(this, function () {
|
|
||||||
this._nbNotifications -= 1;
|
|
||||||
|
|
||||||
if (this._nbNotifications == 0)
|
|
||||||
this.destroy();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
|
||||||
return new St.Icon({ gicon: Gio.icon_new_for_string(this._icon),
|
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Subscription request
|
// Subscription request
|
||||||
const SubscriptionRequestNotification = new Lang.Class({
|
const SubscriptionRequestNotification = new Lang.Class({
|
||||||
Name: 'SubscriptionRequestNotification',
|
Name: 'SubscriptionRequestNotification',
|
||||||
@ -1212,7 +1224,6 @@ const SubscriptionRequestNotification = new Lang.Class({
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: iconBox._size });
|
icon_size: iconBox._size });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1357,15 +1368,14 @@ const AccountNotification = new Lang.Class({
|
|||||||
case 'reconnect':
|
case 'reconnect':
|
||||||
// If it fails again, a new notification should pop up with the
|
// If it fails again, a new notification should pop up with the
|
||||||
// new error.
|
// new error.
|
||||||
account.reconnect_async(null, null);
|
account.reconnect_async(null);
|
||||||
break;
|
break;
|
||||||
case 'edit':
|
case 'edit':
|
||||||
let cmd = '/usr/bin/empathy-accounts'
|
let cmd = '/usr/bin/empathy-accounts'
|
||||||
+ ' --select-account=%s'
|
+ ' --select-account=%s'
|
||||||
.format(account.get_path_suffix());
|
.format(account.get_path_suffix());
|
||||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
|
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||||
null);
|
app_info.launch([], global.create_app_launch_context());
|
||||||
app_info.launch([], null, null);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.destroy();
|
this.destroy();
|
||||||
@ -1420,3 +1430,4 @@ const AccountNotification = new Lang.Class({
|
|||||||
this.parent();
|
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 ||
|
|
||||||
_("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) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return metas;
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
|
||||||
return this._contactSys.initial_search(terms);
|
|
||||||
},
|
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
|
||||||
return this._contactSys.subsearch(previousResults, terms);
|
|
||||||
},
|
|
||||||
|
|
||||||
createResultActor: function(resultMeta, terms) {
|
|
||||||
let contact = new Contact(resultMeta.id);
|
|
||||||
return contact.actor;
|
|
||||||
},
|
|
||||||
|
|
||||||
createResultContainerActor: function() {
|
|
||||||
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
|
||||||
xAlign: St.Align.START });
|
|
||||||
grid.actor.style_class = 'contact-grid';
|
|
||||||
|
|
||||||
let actor = new SearchDisplay.GridSearchResults(this, grid);
|
|
||||||
return actor;
|
|
||||||
},
|
|
||||||
|
|
||||||
activateResult: function(id, params) {
|
|
||||||
launchContact(id);
|
|
||||||
}
|
|
||||||
});
|
|
@ -27,7 +27,6 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._items = [];
|
this._items = [];
|
||||||
this._focusManager = St.FocusManager.get_for_stage(global.stage);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addGroup: function(root, name, icon, params) {
|
addGroup: function(root, name, icon, params) {
|
||||||
@ -41,11 +40,11 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
|
|
||||||
this._items.push(item);
|
this._items.push(item);
|
||||||
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
||||||
this._focusManager.add_group(root);
|
global.focus_manager.add_group(root);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeGroup: function(root) {
|
removeGroup: function(root) {
|
||||||
this._focusManager.remove_group(root);
|
global.focus_manager.remove_group(root);
|
||||||
for (let i = 0; i < this._items.length; i++) {
|
for (let i = 0; i < this._items.length; i++) {
|
||||||
if (this._items[i].root == root) {
|
if (this._items[i].root == root) {
|
||||||
this._items.splice(i, 1);
|
this._items.splice(i, 1);
|
||||||
@ -319,7 +318,6 @@ const CtrlAltTabSwitcher = new Lang.Class({
|
|||||||
let icon = item.iconActor;
|
let icon = item.iconActor;
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
icon = new St.Icon({ icon_name: item.iconName,
|
icon = new St.Icon({ icon_name: item.iconName,
|
||||||
icon_type: St.IconType.SYMBOLIC,
|
|
||||||
icon_size: POPUP_APPICON_SIZE });
|
icon_size: POPUP_APPICON_SIZE });
|
||||||
}
|
}
|
||||||
box.add(icon, { x_fill: false, y_fill: false } );
|
box.add(icon, { x_fill: false, y_fill: false } );
|
||||||
|
171
js/ui/dash.js
@ -124,16 +124,19 @@ const DashItemContainer = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setLabelText: function(text) {
|
setLabelText: function(text) {
|
||||||
if (this.label == null)
|
if (this.label == null) {
|
||||||
this.label = new St.Label({ style_class: 'dash-label'});
|
this.label = new St.Label({ style_class: 'dash-label'});
|
||||||
|
|
||||||
this.label.set_text(text);
|
|
||||||
Main.layoutManager.addChrome(this.label);
|
Main.layoutManager.addChrome(this.label);
|
||||||
this.label.hide();
|
this.label.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.label.set_text(text);
|
||||||
},
|
},
|
||||||
|
|
||||||
hideLabel: function () {
|
hideLabel: function () {
|
||||||
this.label.opacity = 255;
|
if (this.label == null)
|
||||||
|
return;
|
||||||
|
|
||||||
Tweener.addTween(this.label,
|
Tweener.addTween(this.label,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: DASH_ITEM_LABEL_HIDE_TIME,
|
time: DASH_ITEM_LABEL_HIDE_TIME,
|
||||||
@ -168,7 +171,17 @@ const DashItemContainer = new Lang.Class({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this.label)
|
||||||
|
this.label.destroy();
|
||||||
|
|
||||||
|
this.actor.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
animateOutAndDestroy: function() {
|
animateOutAndDestroy: function() {
|
||||||
|
if (this.label)
|
||||||
|
this.label.destroy();
|
||||||
|
|
||||||
if (this.child == null) {
|
if (this.child == null) {
|
||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
return;
|
return;
|
||||||
@ -217,36 +230,47 @@ const DashItemContainer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const RemoveFavoriteIcon = new Lang.Class({
|
const ShowAppsIcon = new Lang.Class({
|
||||||
Name: 'RemoveFavoriteIcon',
|
Name: 'ShowAppsIcon',
|
||||||
Extends: DashItemContainer,
|
Extends: DashItemContainer,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent();
|
this.parent();
|
||||||
|
|
||||||
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
|
this.toggleButton = new St.Button({ style_class: 'show-apps',
|
||||||
|
track_hover: true,
|
||||||
|
can_focus: true,
|
||||||
|
toggle_mode: true });
|
||||||
this._iconActor = null;
|
this._iconActor = null;
|
||||||
this.icon = new IconGrid.BaseIcon(_("Remove"),
|
this.icon = new IconGrid.BaseIcon(_("Show Applications"),
|
||||||
{ setSizeManually: true,
|
{ setSizeManually: true,
|
||||||
showLabel: false,
|
showLabel: false,
|
||||||
createIcon: Lang.bind(this, this._createIcon) });
|
createIcon: Lang.bind(this, this._createIcon) });
|
||||||
this._iconBin.set_child(this.icon.actor);
|
this.toggleButton.add_actor(this.icon.actor);
|
||||||
this._iconBin._delegate = this;
|
this.toggleButton._delegate = this;
|
||||||
|
|
||||||
this.setChild(this._iconBin);
|
this.setChild(this.toggleButton);
|
||||||
|
this.setHover(false);
|
||||||
|
this.toggleButton.label_actor = this.label;
|
||||||
},
|
},
|
||||||
|
|
||||||
_createIcon: function(size) {
|
_createIcon: function(size) {
|
||||||
this._iconActor = new St.Icon({ icon_name: 'user-trash',
|
this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
|
||||||
style_class: 'remove-favorite-icon',
|
icon_size: size,
|
||||||
icon_size: size });
|
style_class: 'show-apps-icon',
|
||||||
|
track_hover: true });
|
||||||
return this._iconActor;
|
return this._iconActor;
|
||||||
},
|
},
|
||||||
|
|
||||||
setHover: function(hovered) {
|
setHover: function(hovered) {
|
||||||
this._iconBin.set_hover(hovered);
|
this.toggleButton.set_hover(hovered);
|
||||||
if (this._iconActor)
|
if (this._iconActor)
|
||||||
this._iconActor.set_hover(hovered);
|
this._iconActor.set_hover(hovered);
|
||||||
|
|
||||||
|
if (hovered)
|
||||||
|
this.setLabelText(_("Remove from Favorites"));
|
||||||
|
else
|
||||||
|
this.setLabelText(_("Show Applications"));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Rely on the dragged item being a favorite
|
// Rely on the dragged item being a favorite
|
||||||
@ -286,6 +310,40 @@ 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;
|
||||||
|
let availHeight = contentBox.y2 - contentBox.y1;
|
||||||
|
|
||||||
|
this.set_allocation(box, flags);
|
||||||
|
|
||||||
|
let [appIcons, showAppsButton] = this.get_children();
|
||||||
|
let [minHeight, natHeight] = showAppsButton.get_preferred_height(availWidth);
|
||||||
|
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
childBox.x1 = 0;
|
||||||
|
childBox.x2 = availWidth;
|
||||||
|
childBox.y1 = 0;
|
||||||
|
childBox.y2 = availHeight - natHeight;
|
||||||
|
appIcons.allocate(childBox, flags);
|
||||||
|
|
||||||
|
childBox.y1 = availHeight - natHeight;
|
||||||
|
childBox.y2 = availHeight;
|
||||||
|
showAppsButton.allocate(childBox, flags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const Dash = new Lang.Class({
|
const Dash = new Lang.Class({
|
||||||
Name: 'Dash',
|
Name: 'Dash',
|
||||||
|
|
||||||
@ -297,17 +355,25 @@ const Dash = new Lang.Class({
|
|||||||
this._dragPlaceholder = null;
|
this._dragPlaceholder = null;
|
||||||
this._dragPlaceholderPos = -1;
|
this._dragPlaceholderPos = -1;
|
||||||
this._animatingPlaceholdersCount = 0;
|
this._animatingPlaceholdersCount = 0;
|
||||||
this._favRemoveTarget = null;
|
|
||||||
this._showLabelTimeoutId = 0;
|
this._showLabelTimeoutId = 0;
|
||||||
this._resetHoverTimeoutId = 0;
|
this._resetHoverTimeoutId = 0;
|
||||||
this._labelShowing = false;
|
this._labelShowing = false;
|
||||||
|
|
||||||
this._box = new St.BoxLayout({ name: 'dash',
|
this._container = new DashActor();
|
||||||
vertical: true,
|
this._box = new St.BoxLayout({ vertical: true,
|
||||||
clip_to_allocation: true });
|
clip_to_allocation: true });
|
||||||
this._box._delegate = this;
|
this._box._delegate = this;
|
||||||
|
this._container.add_actor(this._box);
|
||||||
|
|
||||||
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
|
this._showAppsIcon = new ShowAppsIcon();
|
||||||
|
this._showAppsIcon.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,
|
this.actor.connect('notify::height', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
if (this._maxHeight != this.actor.height)
|
if (this._maxHeight != this.actor.height)
|
||||||
@ -360,14 +426,6 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
_endDrag: function() {
|
_endDrag: function() {
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
if (this._favRemoveTarget) {
|
|
||||||
this._favRemoveTarget.animateOutAndDestroy();
|
|
||||||
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._favRemoveTarget = null;
|
|
||||||
}));
|
|
||||||
this._adjustIconSize();
|
|
||||||
}
|
|
||||||
DND.removeDragMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -386,28 +444,13 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
let srcIsFavorite = (id in favorites);
|
let srcIsFavorite = (id in favorites);
|
||||||
|
|
||||||
if (srcIsFavorite &&
|
let showAppsHovered =
|
||||||
app.get_state() != Shell.AppState.RUNNING &&
|
this._showAppsIcon.actor.contains(dragEvent.targetActor);
|
||||||
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._box.contains(dragEvent.targetActor) || showAppsHovered)
|
||||||
if (this._favRemoveTarget)
|
|
||||||
favRemoveHovered =
|
|
||||||
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
|
|
||||||
|
|
||||||
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
|
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
|
|
||||||
if (this._favRemoveTarget)
|
this._showAppsIcon.setHover(showAppsHovered);
|
||||||
this._favRemoveTarget.setHover(favRemoveHovered);
|
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
@ -423,6 +466,17 @@ const Dash = new Lang.Class({
|
|||||||
Main.queueDeferredWork(this._workId);
|
Main.queueDeferredWork(this._workId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_hookUpLabel: function(item) {
|
||||||
|
item.child.connect('notify::hover', Lang.bind(this, function() {
|
||||||
|
this._onHover(item);
|
||||||
|
}));
|
||||||
|
|
||||||
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||||
|
this._labelShowing = false;
|
||||||
|
item.hideLabel();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
_createAppItem: function(app) {
|
_createAppItem: function(app) {
|
||||||
let display = new AppDisplay.AppWellIcon(app,
|
let display = new AppDisplay.AppWellIcon(app,
|
||||||
{ setSizeManually: true,
|
{ setSizeManually: true,
|
||||||
@ -442,18 +496,14 @@ const Dash = new Lang.Class({
|
|||||||
item.setLabelText(app.get_name());
|
item.setLabelText(app.get_name());
|
||||||
// Override default AppWellIcon label_actor
|
// Override default AppWellIcon label_actor
|
||||||
display.actor.label_actor = item.label;
|
display.actor.label_actor = item.label;
|
||||||
|
|
||||||
|
|
||||||
display.icon.setIconSize(this.iconSize);
|
display.icon.setIconSize(this.iconSize);
|
||||||
display.actor.connect('notify::hover',
|
this._hookUpLabel(item);
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._onHover(item, display)
|
|
||||||
}));
|
|
||||||
return item;
|
return item;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onHover: function (item, display) {
|
_onHover: function (item) {
|
||||||
if (display.actor.get_hover() && !display.isMenuUp) {
|
if (item.child.get_hover() && !item.child._delegate.isMenuUp) {
|
||||||
if (this._showLabelTimeoutId == 0) {
|
if (this._showLabelTimeoutId == 0) {
|
||||||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||||||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||||
@ -494,18 +544,12 @@ const Dash = new Lang.Class({
|
|||||||
!actor._delegate.animatingOut;
|
!actor._delegate.animatingOut;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (iconChildren.length == 0) {
|
iconChildren.push(this._showAppsIcon.actor);
|
||||||
this._box.add_style_pseudo_class('empty');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._box.remove_style_pseudo_class('empty');
|
|
||||||
|
|
||||||
if (this._maxHeight == -1)
|
if (this._maxHeight == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
let themeNode = this._container.get_theme_node();
|
||||||
let themeNode = this._box.get_theme_node();
|
|
||||||
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
|
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
|
||||||
x2: 42 /* whatever */,
|
x2: 42 /* whatever */,
|
||||||
y2: this._maxHeight });
|
y2: this._maxHeight });
|
||||||
@ -531,7 +575,6 @@ const Dash = new Lang.Class({
|
|||||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Subtract icon padding and box spacing from the available height
|
// Subtract icon padding and box spacing from the available height
|
||||||
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
||||||
(iconChildren.length - 1) * spacing;
|
(iconChildren.length - 1) * spacing;
|
||||||
@ -691,7 +734,7 @@ const Dash = new Lang.Class({
|
|||||||
if (Main.overview.visible)
|
if (Main.overview.visible)
|
||||||
item.animateOutAndDestroy();
|
item.animateOutAndDestroy();
|
||||||
else
|
else
|
||||||
item.actor.destroy();
|
item.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._adjustIconSize();
|
this._adjustIconSize();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Cairo = imports.cairo;
|
const Cairo = imports.cairo;
|
||||||
@ -16,14 +17,6 @@ const Main = imports.ui.main;
|
|||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Calendar = imports.ui.calendar;
|
const Calendar = imports.ui.calendar;
|
||||||
const UPowerGlib = imports.gi.UPowerGlib;
|
|
||||||
|
|
||||||
// in org.gnome.desktop.interface
|
|
||||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
|
||||||
|
|
||||||
// in org.gnome.shell.clock
|
|
||||||
const CLOCK_SHOW_DATE_KEY = 'show-date';
|
|
||||||
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
|
|
||||||
|
|
||||||
function _onVertSepRepaint (area)
|
function _onVertSepRepaint (area)
|
||||||
{
|
{
|
||||||
@ -45,9 +38,7 @@ const DateMenuButton = new Lang.Class({
|
|||||||
Name: 'DateMenuButton',
|
Name: 'DateMenuButton',
|
||||||
Extends: PanelMenu.Button,
|
Extends: PanelMenu.Button,
|
||||||
|
|
||||||
_init: function(params) {
|
_init: function() {
|
||||||
params = Params.parse(params, { showEvents: true });
|
|
||||||
|
|
||||||
let item;
|
let item;
|
||||||
let hbox;
|
let hbox;
|
||||||
let vbox;
|
let vbox;
|
||||||
@ -62,8 +53,8 @@ const DateMenuButton = new Lang.Class({
|
|||||||
// role ATK_ROLE_MENU like other elements of the panel.
|
// role ATK_ROLE_MENU like other elements of the panel.
|
||||||
this.actor.accessible_role = Atk.Role.LABEL;
|
this.actor.accessible_role = Atk.Role.LABEL;
|
||||||
|
|
||||||
this._clock = new St.Label();
|
this._clockDisplay = new St.Label();
|
||||||
this.actor.add_actor(this._clock);
|
this.actor.add_actor(this._clockDisplay);
|
||||||
|
|
||||||
hbox = new St.BoxLayout({name: 'calendarArea' });
|
hbox = new St.BoxLayout({name: 'calendarArea' });
|
||||||
this.menu.addActor(hbox);
|
this.menu.addActor(hbox);
|
||||||
@ -75,20 +66,12 @@ const DateMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
// Date
|
// Date
|
||||||
this._date = new St.Label();
|
this._date = new St.Label();
|
||||||
this.actor.label_actor = this._date;
|
this.actor.label_actor = this._clockDisplay;
|
||||||
this._date.style_class = 'datemenu-date-label';
|
this._date.style_class = 'datemenu-date-label';
|
||||||
vbox.add(this._date);
|
vbox.add(this._date);
|
||||||
|
|
||||||
if (params.showEvents) {
|
this._eventList = new Calendar.EventsList();
|
||||||
this._eventSource = new Calendar.DBusEventSource();
|
this._calendar = new Calendar.Calendar();
|
||||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
|
||||||
} else {
|
|
||||||
this._eventSource = null;
|
|
||||||
this._eventList = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calendar
|
|
||||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
|
||||||
|
|
||||||
this._calendar.connect('selected-date-changed',
|
this._calendar.connect('selected-date-changed',
|
||||||
Lang.bind(this, function(calendar, date) {
|
Lang.bind(this, function(calendar, date) {
|
||||||
@ -110,27 +93,23 @@ const DateMenuButton = new Lang.Class({
|
|||||||
item.actor.reparent(vbox);
|
item.actor.reparent(vbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.showEvents) {
|
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||||
// Add vertical separator
|
|
||||||
|
|
||||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
|
||||||
pseudo_class: 'highlighted' });
|
pseudo_class: 'highlighted' });
|
||||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||||
hbox.add(item);
|
hbox.add(this._separator);
|
||||||
|
|
||||||
// Fill up the second column
|
// Fill up the second column
|
||||||
vbox = new St.BoxLayout({name: 'calendarEventsArea',
|
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
|
||||||
vertical: true});
|
vertical: true });
|
||||||
hbox.add(vbox, { expand: true });
|
hbox.add(vbox, { expand: true });
|
||||||
|
|
||||||
// Event list
|
// Event list
|
||||||
vbox.add(this._eventList.actor, { expand: true });
|
vbox.add(this._eventList.actor, { expand: true });
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||||
item.actor.can_focus = false;
|
this._openCalendarItem.actor.can_focus = false;
|
||||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
}
|
|
||||||
|
|
||||||
// Whenever the menu is opened, select today
|
// Whenever the menu is opened, select today
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||||
@ -157,77 +136,55 @@ const DateMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
// Done with hbox for calendar and event list
|
// Done with hbox for calendar and event list
|
||||||
|
|
||||||
// Track changes to clock settings
|
this._clock = new GnomeDesktop.WallClock();
|
||||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
||||||
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
|
||||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
|
||||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
|
||||||
|
|
||||||
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
|
||||||
this._upClient = new UPowerGlib.Client();
|
|
||||||
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
|
|
||||||
|
|
||||||
// Start the clock
|
|
||||||
this._updateClockAndDate();
|
this._updateClockAndDate();
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
this._sessionUpdated();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setEventsVisibility: function(visible) {
|
||||||
|
this._separator.visible = visible;
|
||||||
|
this._eventList.visible = visible;
|
||||||
|
this._openCalendarItem.visible = visible;
|
||||||
|
},
|
||||||
|
|
||||||
|
_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);
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateClockAndDate: function() {
|
_updateClockAndDate: function() {
|
||||||
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
this._clockDisplay.set_text(this._clock.clock);
|
||||||
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
|
|
||||||
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
|
|
||||||
|
|
||||||
let clockFormat;
|
|
||||||
let dateFormat;
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case '24h':
|
|
||||||
if (showDate)
|
|
||||||
/* Translators: This is the time format with date used
|
|
||||||
in 24-hour mode. */
|
|
||||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
|
||||||
: _("%a %b %e, %R");
|
|
||||||
else
|
|
||||||
/* Translators: This is the time format without date used
|
|
||||||
in 24-hour mode. */
|
|
||||||
clockFormat = showSeconds ? _("%a %R:%S")
|
|
||||||
: _("%a %R");
|
|
||||||
break;
|
|
||||||
case '12h':
|
|
||||||
default:
|
|
||||||
if (showDate)
|
|
||||||
/* Translators: This is a time format with date used
|
|
||||||
for AM/PM. */
|
|
||||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
|
||||||
: _("%a %b %e, %l:%M %p");
|
|
||||||
else
|
|
||||||
/* Translators: This is a time format without date used
|
|
||||||
for AM/PM. */
|
|
||||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
|
||||||
: _("%a %l:%M %p");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let displayDate = new Date();
|
|
||||||
|
|
||||||
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
|
|
||||||
|
|
||||||
/* Translators: This is the date format to use when the calendar popup is
|
/* 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").
|
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||||
*/
|
*/
|
||||||
dateFormat = _("%A %B %e, %Y");
|
let dateFormat = _("%A %B %e, %Y");
|
||||||
|
let displayDate = new Date();
|
||||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||||
|
|
||||||
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOpenCalendarActivate: function() {
|
_onOpenCalendarActivate: function() {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||||
let tool = calendarSettings.get_string('exec');
|
let tool = calendarSettings.get_string('exec');
|
||||||
if (tool.length == 0 || tool == 'evolution') {
|
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||||
// TODO: pass the selected day
|
// TODO: pass the selected day
|
||||||
Util.spawn(['evolution', '-c', 'calendar']);
|
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||||
|
app.activate();
|
||||||
} else {
|
} else {
|
||||||
let needTerm = calendarSettings.get_boolean('needs-term');
|
let needTerm = calendarSettings.get_boolean('needs-term');
|
||||||
if (needTerm) {
|
if (needTerm) {
|
||||||
|
@ -31,10 +31,10 @@ const St = imports.gi.St;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const Lightbox = imports.ui.lightbox;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
const UserMenu = imports.ui.userMenu;
|
||||||
|
|
||||||
let _endSessionDialog = null;
|
let _endSessionDialog = null;
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ const shutdownDialogContent = {
|
|||||||
label: C_("button", "Restart") },
|
label: C_("button", "Restart") },
|
||||||
{ signal: 'ConfirmedShutdown',
|
{ signal: 'ConfirmedShutdown',
|
||||||
label: C_("button", "Power Off") }],
|
label: C_("button", "Power Off") }],
|
||||||
iconName: 'system-shutdown',
|
iconName: 'system-shutdown-symbolic',
|
||||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ const restartDialogContent = {
|
|||||||
endDescription: _("Restarting the system."),
|
endDescription: _("Restarting the system."),
|
||||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||||
label: C_("button", "Restart") }],
|
label: C_("button", "Restart") }],
|
||||||
iconName: 'system-shutdown',
|
iconName: 'system-shutdown-symbolic',
|
||||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -161,6 +161,7 @@ const ListItem = new Lang.Class({
|
|||||||
|
|
||||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
this._descriptionLabel = new St.Label({ text: this._reason,
|
||||||
style_class: 'end-session-dialog-app-list-item-description' });
|
style_class: 'end-session-dialog-app-list-item-description' });
|
||||||
|
this.actor.label_actor = this._nameLabel;
|
||||||
textLayout.add(this._descriptionLabel,
|
textLayout.add(this._descriptionLabel,
|
||||||
{ expand: true,
|
{ expand: true,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
@ -280,21 +281,17 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
scrollView.hide();
|
scrollView.hide();
|
||||||
|
|
||||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||||
scrollView.add_actor(this._applicationList,
|
scrollView.add_actor(this._applicationList);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
this._applicationList.connect('actor-added',
|
this._applicationList.connect('actor-added',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
if (this._applicationList.get_children().length == 1)
|
if (this._applicationList.get_n_children() == 1)
|
||||||
scrollView.show();
|
scrollView.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._applicationList.connect('actor-removed',
|
this._applicationList.connect('actor-removed',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
if (this._applicationList.get_children().length == 0)
|
if (this._applicationList.get_n_children() == 0)
|
||||||
scrollView.hide();
|
scrollView.hide();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -307,42 +304,7 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._user.disconnect(this._userChangedId);
|
this._user.disconnect(this._userChangedId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_setIconFromFile: function(iconFile, styleClass) {
|
_updateDescription: function() {
|
||||||
if (styleClass)
|
|
||||||
this._iconBin.set_style_class_name(styleClass);
|
|
||||||
this._iconBin.set_style(null);
|
|
||||||
|
|
||||||
this._iconBin.child = null;
|
|
||||||
if (iconFile) {
|
|
||||||
this._iconBin.show();
|
|
||||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
|
||||||
'background-size: contain;');
|
|
||||||
} else {
|
|
||||||
this._iconBin.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_setIconFromName: function(iconName, styleClass) {
|
|
||||||
if (styleClass)
|
|
||||||
this._iconBin.set_style_class_name(styleClass);
|
|
||||||
this._iconBin.set_style(null);
|
|
||||||
|
|
||||||
if (iconName != null) {
|
|
||||||
let textureCache = St.TextureCache.get_default();
|
|
||||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
|
||||||
iconName,
|
|
||||||
St.IconType.SYMBOLIC,
|
|
||||||
_DIALOG_ICON_SIZE);
|
|
||||||
|
|
||||||
this._iconBin.child = icon;
|
|
||||||
this._iconBin.show();
|
|
||||||
} else {
|
|
||||||
this._iconBin.child = null;
|
|
||||||
this._iconBin.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateContent: function() {
|
|
||||||
if (this.state != ModalDialog.State.OPENING &&
|
if (this.state != ModalDialog.State.OPENING &&
|
||||||
this.state != ModalDialog.State.OPENED)
|
this.state != ModalDialog.State.OPENED)
|
||||||
return;
|
return;
|
||||||
@ -352,17 +314,6 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
let subject = dialogContent.subject;
|
let subject = dialogContent.subject;
|
||||||
let description;
|
let description;
|
||||||
|
|
||||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
|
||||||
let iconFile = this._user.get_icon_file();
|
|
||||||
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
|
||||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
|
||||||
else
|
|
||||||
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
|
|
||||||
} else if (dialogContent.iconName) {
|
|
||||||
this._setIconFromName(dialogContent.iconName,
|
|
||||||
dialogContent.iconStyleClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._inhibitors.length > 0) {
|
if (this._inhibitors.length > 0) {
|
||||||
this._stopTimer();
|
this._stopTimer();
|
||||||
description = dialogContent.inhibitedDescription;
|
description = dialogContent.inhibitedDescription;
|
||||||
@ -395,6 +346,27 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
_setLabelText(this._descriptionLabel, description);
|
_setLabelText(this._descriptionLabel, description);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateContent: function() {
|
||||||
|
if (this.state != ModalDialog.State.OPENING &&
|
||||||
|
this.state != ModalDialog.State.OPENED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let dialogContent = DialogContent[this._type];
|
||||||
|
if (dialogContent.iconName) {
|
||||||
|
this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName,
|
||||||
|
icon_size: _DIALOG_ICON_SIZE,
|
||||||
|
style_class: dialogContent.iconStyleClass });
|
||||||
|
} else {
|
||||||
|
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
|
||||||
|
{ iconSize: _DIALOG_ICON_SIZE,
|
||||||
|
styleClass: dialogContent.iconStyleClass });
|
||||||
|
this._iconBin.child = avatarWidget.actor;
|
||||||
|
avatarWidget.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateDescription();
|
||||||
|
},
|
||||||
|
|
||||||
_updateButtons: function() {
|
_updateButtons: function() {
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
let buttons = [{ action: Lang.bind(this, this.cancel),
|
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||||
@ -441,7 +413,7 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
{ _secondsLeft: 0,
|
{ _secondsLeft: 0,
|
||||||
time: this._secondsLeft,
|
time: this._secondsLeft,
|
||||||
transition: 'linear',
|
transition: 'linear',
|
||||||
onUpdate: Lang.bind(this, this._updateContent),
|
onUpdate: Lang.bind(this, this._updateDescription),
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||||
|
@ -39,11 +39,19 @@ function _patchContainerClass(containerClass) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _makeLoggingFunc(func) {
|
||||||
|
return function() {
|
||||||
|
return func([].join.call(arguments, ', '));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// Add some bindings to the global JS namespace; (gjs keeps the web
|
// Add some bindings to the global JS namespace; (gjs keeps the web
|
||||||
// browser convention of having that namespace be called 'window'.)
|
// browser convention of having that namespace be called 'window'.)
|
||||||
window.global = Shell.Global.get();
|
window.global = Shell.Global.get();
|
||||||
|
|
||||||
|
window.log = _makeLoggingFunc(window.log);
|
||||||
|
|
||||||
window._ = Gettext.gettext;
|
window._ = Gettext.gettext;
|
||||||
window.C_ = Gettext.pgettext;
|
window.C_ = Gettext.pgettext;
|
||||||
window.ngettext = Gettext.ngettext;
|
window.ngettext = Gettext.ngettext;
|
||||||
@ -82,7 +90,7 @@ function init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OK, now things are initialized enough that we can import shell JS
|
// OK, now things are initialized enough that we can import shell JS
|
||||||
const Format = imports.misc.format;
|
const Format = imports.format;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
Tweener.init();
|
Tweener.init();
|
||||||
|
270
js/ui/extensionDownloader.js
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Soup = imports.gi.Soup;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Config = imports.misc.config;
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
|
const FileUtils = imports.misc.fileUtils;
|
||||||
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
|
|
||||||
|
const _signals = ExtensionSystem._signals;
|
||||||
|
|
||||||
|
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
||||||
|
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
||||||
|
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||||
|
const REPOSITORY_URL_UPDATE = REPOSITORY_URL_BASE + '/update-info/';
|
||||||
|
|
||||||
|
let _httpSession;
|
||||||
|
|
||||||
|
function installExtension(uuid, invocation) {
|
||||||
|
let params = { uuid: uuid,
|
||||||
|
shell_version: Config.PACKAGE_VERSION };
|
||||||
|
|
||||||
|
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
|
||||||
|
|
||||||
|
_httpSession.queue_message(message, function(session, message) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||||
|
ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code);
|
||||||
|
invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let info;
|
||||||
|
try {
|
||||||
|
info = JSON.parse(message.response_body.data);
|
||||||
|
} catch (e) {
|
||||||
|
ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e);
|
||||||
|
invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dialog = new InstallExtensionDialog(uuid, info, invocation);
|
||||||
|
dialog.open(global.get_current_time());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstallExtension(uuid) {
|
||||||
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
|
if (!extension)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Don't try to uninstall system extensions
|
||||||
|
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ExtensionSystem.unloadExtension(extension))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FileUtils.recursivelyDeleteDir(extension.dir, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||||
|
errback('DownloadExtensionError', message.status_code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!dir.query_exists(null))
|
||||||
|
dir.make_directory_with_parents(null);
|
||||||
|
} catch (e) {
|
||||||
|
errback('CreateExtensionDirectoryError', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
|
||||||
|
let contents = message.response_body.flatten().get_as_bytes();
|
||||||
|
stream.output_stream.write_bytes(contents, null);
|
||||||
|
stream.close(null);
|
||||||
|
let [success, pid] = GLib.spawn_async(null,
|
||||||
|
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
|
||||||
|
null,
|
||||||
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||||
|
null);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
errback('ExtractExtensionError');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||||
|
GLib.spawn_close_pid(pid);
|
||||||
|
|
||||||
|
if (status != 0)
|
||||||
|
errback('ExtractExtensionError');
|
||||||
|
else
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateExtension(uuid) {
|
||||||
|
// This gets a bit tricky. We want the update to be seamless -
|
||||||
|
// if we have any error during downloading or extracting, we
|
||||||
|
// want to not unload the current version.
|
||||||
|
|
||||||
|
let oldExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
|
||||||
|
let newExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
|
||||||
|
|
||||||
|
let params = { shell_version: Config.PACKAGE_VERSION };
|
||||||
|
|
||||||
|
let url = REPOSITORY_URL_DOWNLOAD.format(uuid);
|
||||||
|
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||||
|
|
||||||
|
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||||
|
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, function() {
|
||||||
|
let oldExtension = ExtensionUtils.extensions[uuid];
|
||||||
|
let extensionDir = oldExtension.dir;
|
||||||
|
|
||||||
|
if (!ExtensionSystem.unloadExtension(oldExtension))
|
||||||
|
return;
|
||||||
|
|
||||||
|
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
|
||||||
|
FileUtils.recursivelyMoveDir(newExtensionTmpDir, extensionDir);
|
||||||
|
|
||||||
|
let extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ExtensionSystem.loadExtension(extension);
|
||||||
|
} catch(e) {
|
||||||
|
ExtensionSystem.unloadExtension(extension);
|
||||||
|
|
||||||
|
logError(e, 'Error loading extension %s'.format(uuid));
|
||||||
|
|
||||||
|
FileUtils.recursivelyDeleteDir(extensionDir, false);
|
||||||
|
FileUtils.recursivelyMoveDir(oldExtensionTmpDir, extensionDir);
|
||||||
|
|
||||||
|
// Restore what was there before. We can't do much if we
|
||||||
|
// fail here.
|
||||||
|
ExtensionSystem.loadExtension(oldExtension);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true);
|
||||||
|
}, function(code, message) {
|
||||||
|
log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : ''));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForUpdates() {
|
||||||
|
let metadatas = {};
|
||||||
|
for (let uuid in ExtensionUtils.extensions) {
|
||||||
|
metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = { shell_version: Config.PACKAGE_VERSION,
|
||||||
|
installed: JSON.stringify(metadatas) };
|
||||||
|
|
||||||
|
let url = REPOSITORY_URL_UPDATE;
|
||||||
|
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||||
|
_httpSession.queue_message(message, function(session, message) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let operations = JSON.parse(message.response_body.data);
|
||||||
|
for (let uuid in operations) {
|
||||||
|
let operation = operations[uuid];
|
||||||
|
if (operation == 'blacklist')
|
||||||
|
uninstallExtension(uuid);
|
||||||
|
else if (operation == 'upgrade' || operation == 'downgrade')
|
||||||
|
updateExtension(uuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const InstallExtensionDialog = new Lang.Class({
|
||||||
|
Name: 'InstallExtensionDialog',
|
||||||
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
|
_init: function(uuid, info, invocation) {
|
||||||
|
this.parent({ styleClass: 'extension-dialog' });
|
||||||
|
|
||||||
|
this._uuid = uuid;
|
||||||
|
this._info = info;
|
||||||
|
this._invocation = invocation;
|
||||||
|
|
||||||
|
this.setButtons([{ label: _("Cancel"),
|
||||||
|
action: Lang.bind(this, this._onCancelButtonPressed),
|
||||||
|
key: Clutter.Escape
|
||||||
|
},
|
||||||
|
{ label: _("Install"),
|
||||||
|
action: Lang.bind(this, this._onInstallButtonPressed),
|
||||||
|
default: true
|
||||||
|
}]);
|
||||||
|
|
||||||
|
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
|
||||||
|
|
||||||
|
let box = new St.BoxLayout();
|
||||||
|
this.contentLayout.add(box);
|
||||||
|
|
||||||
|
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
|
||||||
|
let icon = new St.Icon({ gicon: gicon });
|
||||||
|
box.add(icon);
|
||||||
|
|
||||||
|
let label = new St.Label({ text: message });
|
||||||
|
box.add(label);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCancelButtonPressed: function(button, event) {
|
||||||
|
this.close(global.get_current_time());
|
||||||
|
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInstallButtonPressed: function(button, event) {
|
||||||
|
let params = { shell_version: Config.PACKAGE_VERSION };
|
||||||
|
|
||||||
|
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
|
||||||
|
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||||
|
|
||||||
|
let uuid = this._uuid;
|
||||||
|
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
|
||||||
|
let invocation = this._invocation;
|
||||||
|
function errback(code, message) {
|
||||||
|
invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function callback() {
|
||||||
|
// Add extension to 'enabled-extensions' for the user, always...
|
||||||
|
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||||
|
if (enabledExtensions.indexOf(uuid) == -1) {
|
||||||
|
enabledExtensions.push(uuid);
|
||||||
|
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ExtensionSystem.loadExtension(extension);
|
||||||
|
} catch(e) {
|
||||||
|
uninstallExtension(uuid);
|
||||||
|
errback('LoadExtensionError', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('(s)', 'successful'));
|
||||||
|
}
|
||||||
|
|
||||||
|
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||||
|
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.close(global.get_current_time());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
_httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
|
||||||
|
|
||||||
|
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||||
|
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
|
||||||
|
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||||
|
}
|
@ -3,19 +3,12 @@
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Soup = imports.gi.Soup;
|
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const FileUtils = imports.misc.fileUtils;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
|
||||||
|
|
||||||
const API_VERSION = 1;
|
|
||||||
|
|
||||||
const ExtensionState = {
|
const ExtensionState = {
|
||||||
ENABLED: 1,
|
ENABLED: 1,
|
||||||
@ -30,29 +23,6 @@ const ExtensionState = {
|
|||||||
UNINSTALLED: 99
|
UNINSTALLED: 99
|
||||||
};
|
};
|
||||||
|
|
||||||
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
|
||||||
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
|
||||||
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
|
||||||
|
|
||||||
const _httpSession = new Soup.SessionAsync();
|
|
||||||
|
|
||||||
// The unfortunate state of gjs, gobject-introspection and libsoup
|
|
||||||
// means that I have to do a hack to add a feature.
|
|
||||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
|
||||||
|
|
||||||
if (Soup.Session.prototype.add_feature != null)
|
|
||||||
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
|
||||||
|
|
||||||
function _getCertFile() {
|
|
||||||
let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']);
|
|
||||||
if (GLib.file_test(localCert, GLib.FileTest.EXISTS))
|
|
||||||
return localCert;
|
|
||||||
else
|
|
||||||
return Config.SHELL_SYSTEM_CA_FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
_httpSession.ssl_ca_file = _getCertFile();
|
|
||||||
|
|
||||||
// Arrays of uuids
|
// Arrays of uuids
|
||||||
var enabledExtensions;
|
var enabledExtensions;
|
||||||
// Contains the order that extensions were enabled in.
|
// Contains the order that extensions were enabled in.
|
||||||
@ -69,89 +39,8 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
|
|||||||
|
|
||||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||||
|
|
||||||
function installExtensionFromUUID(uuid, version_tag) {
|
var initted = false;
|
||||||
let params = { uuid: uuid,
|
var enabled;
|
||||||
version_tag: version_tag,
|
|
||||||
shell_version: Config.PACKAGE_VERSION,
|
|
||||||
api_version: API_VERSION.toString() };
|
|
||||||
|
|
||||||
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
|
|
||||||
|
|
||||||
_httpSession.queue_message(message,
|
|
||||||
function(session, message) {
|
|
||||||
let info = JSON.parse(message.response_body.data);
|
|
||||||
let dialog = new InstallExtensionDialog(uuid, version_tag, info.name);
|
|
||||||
dialog.open(global.get_current_time());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function uninstallExtensionFromUUID(uuid) {
|
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
|
||||||
if (!extension)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
|
||||||
// but it will be removed on next reboot, and hopefully nothing
|
|
||||||
// broke too much.
|
|
||||||
disableExtension(uuid);
|
|
||||||
|
|
||||||
// Don't try to uninstall system extensions
|
|
||||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
extension.state = ExtensionState.UNINSTALLED;
|
|
||||||
_signals.emit('extension-state-changed', extension);
|
|
||||||
|
|
||||||
delete ExtensionUtils.extensions[uuid];
|
|
||||||
|
|
||||||
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function gotExtensionZipFile(session, message, uuid) {
|
|
||||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
|
||||||
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: use a GFile mkstemp-type method once one exists
|
|
||||||
let fd, tmpzip;
|
|
||||||
try {
|
|
||||||
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
|
|
||||||
} catch (e) {
|
|
||||||
logExtensionError(uuid, 'tempfile: ' + e.toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let stream = new Gio.UnixOutputStream({ fd: fd });
|
|
||||||
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
|
|
||||||
Shell.write_soup_message_to_stream(stream, message);
|
|
||||||
stream.close(null);
|
|
||||||
let [success, pid] = GLib.spawn_async(null,
|
|
||||||
['unzip', '-uod', dir.get_path(), '--', tmpzip],
|
|
||||||
null,
|
|
||||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
|
||||||
null);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
logExtensionError(uuid, 'extract: could not extract');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
|
||||||
GLib.spawn_close_pid(pid);
|
|
||||||
|
|
||||||
// Add extension to 'enabled-extensions' for the user, always...
|
|
||||||
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
|
||||||
if (enabledExtensions.indexOf(uuid) == -1) {
|
|
||||||
enabledExtensions.push(uuid);
|
|
||||||
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function disableExtension(uuid) {
|
function disableExtension(uuid) {
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
@ -178,23 +67,23 @@ function disableExtension(uuid) {
|
|||||||
try {
|
try {
|
||||||
ExtensionUtils.extensions[uuid].stateObj.disable();
|
ExtensionUtils.extensions[uuid].stateObj.disable();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logExtensionError(uuid, e.toString());
|
logExtensionError(uuid, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (extension.stylesheet) {
|
||||||
extension.stateObj.disable();
|
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||||
} catch(e) {
|
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||||
logExtensionError(uuid, e.toString());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension.stateObj.disable();
|
||||||
|
|
||||||
for (let i = 0; i < order.length; i++) {
|
for (let i = 0; i < order.length; i++) {
|
||||||
let uuid = order[i];
|
let uuid = order[i];
|
||||||
try {
|
try {
|
||||||
ExtensionUtils.extensions[uuid].stateObj.enable();
|
ExtensionUtils.extensions[uuid].stateObj.enable();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logExtensionError(uuid, e.toString());
|
logExtensionError(uuid, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,67 +106,86 @@ function enableExtension(uuid) {
|
|||||||
|
|
||||||
extensionOrder.push(uuid);
|
extensionOrder.push(uuid);
|
||||||
|
|
||||||
try {
|
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||||
extension.stateObj.enable();
|
if (stylesheetFile.query_exists(null)) {
|
||||||
} catch(e) {
|
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||||
logExtensionError(uuid, e.toString());
|
theme.load_stylesheet(stylesheetFile.get_path());
|
||||||
return;
|
extension.stylesheet = stylesheetFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension.stateObj.enable();
|
||||||
|
|
||||||
extension.state = ExtensionState.ENABLED;
|
extension.state = ExtensionState.ENABLED;
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
function logExtensionError(uuid, message, state) {
|
function logExtensionError(uuid, error) {
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
if (!extension)
|
if (!extension)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
let message = '' + error;
|
||||||
|
|
||||||
|
if (error.state)
|
||||||
|
extension.state = error.state;
|
||||||
|
else
|
||||||
|
extension.state = ExtensionState.ERROR;
|
||||||
|
|
||||||
if (!extension.errors)
|
if (!extension.errors)
|
||||||
extension.errors = [];
|
extension.errors = [];
|
||||||
|
|
||||||
extension.errors.push(message);
|
log('Extension "%s" had error: %s'.format(uuid, message));
|
||||||
global.logError('Extension "%s" had error: %s'.format(uuid, message));
|
|
||||||
state = state || ExtensionState.ERROR;
|
|
||||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||||
error: message,
|
error: message,
|
||||||
state: state });
|
state: extension.state });
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadExtension(dir, type, enabled) {
|
function loadExtension(extension) {
|
||||||
let uuid = dir.get_basename();
|
|
||||||
let extension;
|
|
||||||
|
|
||||||
if (ExtensionUtils.extensions[uuid] != undefined) {
|
|
||||||
throw new Error('extension already loaded');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
|
||||||
} catch(e) {
|
|
||||||
logExtensionError(uuid, e.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to error, we set success as the last step
|
// Default to error, we set success as the last step
|
||||||
extension.state = ExtensionState.ERROR;
|
extension.state = ExtensionState.ERROR;
|
||||||
|
|
||||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||||
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version');
|
||||||
extension.state = ExtensionState.OUT_OF_DATE;
|
error.state = ExtensionState.OUT_OF_DATE;
|
||||||
return;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
initExtension(uuid);
|
initExtension(extension.uuid);
|
||||||
if (extension.state == ExtensionState.DISABLED)
|
if (extension.state == ExtensionState.DISABLED)
|
||||||
enableExtension(uuid);
|
enableExtension(extension.uuid);
|
||||||
} else {
|
} else {
|
||||||
extension.state = ExtensionState.INITIALIZED;
|
extension.state = ExtensionState.INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
global.log('Loaded extension ' + uuid);
|
}
|
||||||
|
|
||||||
|
function unloadExtension(extension) {
|
||||||
|
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||||
|
// but it will be removed on next reboot, and hopefully nothing
|
||||||
|
// broke too much.
|
||||||
|
disableExtension(extension.uuid);
|
||||||
|
|
||||||
|
extension.state = ExtensionState.UNINSTALLED;
|
||||||
|
_signals.emit('extension-state-changed', extension);
|
||||||
|
|
||||||
|
delete ExtensionUtils.extensions[extension.uuid];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadExtension(oldExtension) {
|
||||||
|
// Grab the things we'll need to pass to createExtensionObject
|
||||||
|
// to reload it.
|
||||||
|
let { uuid: uuid, dir: dir, type: type } = oldExtension;
|
||||||
|
|
||||||
|
// Then unload the old extension.
|
||||||
|
unloadExtension(oldExtension);
|
||||||
|
|
||||||
|
// Now, recreate the extension and load it.
|
||||||
|
let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||||
|
loadExtension(newExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initExtension(uuid) {
|
function initExtension(uuid) {
|
||||||
@ -288,76 +196,43 @@ function initExtension(uuid) {
|
|||||||
throw new Error("Extension was not properly created. Call loadExtension first");
|
throw new Error("Extension was not properly created. Call loadExtension first");
|
||||||
|
|
||||||
let extensionJs = dir.get_child('extension.js');
|
let extensionJs = dir.get_child('extension.js');
|
||||||
if (!extensionJs.query_exists(null)) {
|
if (!extensionJs.query_exists(null))
|
||||||
logExtensionError(uuid, 'Missing extension.js');
|
throw new Error('Missing extension.js');
|
||||||
return;
|
|
||||||
}
|
|
||||||
let stylesheetPath = null;
|
|
||||||
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
||||||
let theme = themeContext.get_theme();
|
|
||||||
let stylesheetFile = dir.get_child('stylesheet.css');
|
|
||||||
if (stylesheetFile.query_exists(null)) {
|
|
||||||
try {
|
|
||||||
theme.load_stylesheet(stylesheetFile.get_path());
|
|
||||||
} catch (e) {
|
|
||||||
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let extensionModule;
|
let extensionModule;
|
||||||
let extensionState = null;
|
let extensionState = null;
|
||||||
try {
|
|
||||||
ExtensionUtils.installImporter(extension);
|
ExtensionUtils.installImporter(extension);
|
||||||
extensionModule = extension.imports.extension;
|
extensionModule = extension.imports.extension;
|
||||||
} catch (e) {
|
|
||||||
if (stylesheetPath != null)
|
|
||||||
theme.unload_stylesheet(stylesheetPath);
|
|
||||||
logExtensionError(uuid, '' + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!extensionModule.init) {
|
if (extensionModule.init) {
|
||||||
logExtensionError(uuid, 'missing \'init\' function');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
extensionState = extensionModule.init(extension);
|
extensionState = extensionModule.init(extension);
|
||||||
} catch (e) {
|
|
||||||
if (stylesheetPath != null)
|
|
||||||
theme.unload_stylesheet(stylesheetPath);
|
|
||||||
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extensionState)
|
if (!extensionState)
|
||||||
extensionState = extensionModule;
|
extensionState = extensionModule;
|
||||||
extension.stateObj = extensionState;
|
extension.stateObj = extensionState;
|
||||||
|
|
||||||
if (!extensionState.enable) {
|
|
||||||
logExtensionError(uuid, 'missing \'enable\' function');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!extensionState.disable) {
|
|
||||||
logExtensionError(uuid, 'missing \'disable\' function');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
extension.state = ExtensionState.DISABLED;
|
extension.state = ExtensionState.DISABLED;
|
||||||
|
|
||||||
_signals.emit('extension-loaded', uuid);
|
_signals.emit('extension-loaded', uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnabledExtensionsChanged() {
|
function onEnabledExtensionsChanged() {
|
||||||
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||||
|
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
// Find and enable all the newly enabled extensions: UUIDs found in the
|
// Find and enable all the newly enabled extensions: UUIDs found in the
|
||||||
// new setting, but not in the old one.
|
// new setting, but not in the old one.
|
||||||
newEnabledExtensions.filter(function(uuid) {
|
newEnabledExtensions.filter(function(uuid) {
|
||||||
return enabledExtensions.indexOf(uuid) == -1;
|
return enabledExtensions.indexOf(uuid) == -1;
|
||||||
}).forEach(function(uuid) {
|
}).forEach(function(uuid) {
|
||||||
|
try {
|
||||||
enableExtension(uuid);
|
enableExtension(uuid);
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||||
@ -365,87 +240,67 @@ function onEnabledExtensionsChanged() {
|
|||||||
enabledExtensions.filter(function(item) {
|
enabledExtensions.filter(function(item) {
|
||||||
return newEnabledExtensions.indexOf(item) == -1;
|
return newEnabledExtensions.indexOf(item) == -1;
|
||||||
}).forEach(function(uuid) {
|
}).forEach(function(uuid) {
|
||||||
|
try {
|
||||||
disableExtension(uuid);
|
disableExtension(uuid);
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
enabledExtensions = newEnabledExtensions;
|
enabledExtensions = newEnabledExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function _loadExtensions() {
|
||||||
ExtensionUtils.init();
|
|
||||||
|
|
||||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||||
}
|
|
||||||
|
|
||||||
function loadExtensions() {
|
let finder = new ExtensionUtils.ExtensionFinder();
|
||||||
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
|
finder.connect('extension-found', function(signals, extension) {
|
||||||
let enabled = enabledExtensions.indexOf(uuid) != -1;
|
try {
|
||||||
loadExtension(dir, type, enabled);
|
loadExtension(extension);
|
||||||
});
|
} catch(e) {
|
||||||
}
|
logExtensionError(extension.uuid, e);
|
||||||
|
|
||||||
const InstallExtensionDialog = new Lang.Class({
|
|
||||||
Name: 'InstallExtensionDialog',
|
|
||||||
Extends: ModalDialog.ModalDialog,
|
|
||||||
|
|
||||||
_init: function(uuid, version_tag, name) {
|
|
||||||
this.parent({ styleClass: 'extension-dialog' });
|
|
||||||
|
|
||||||
this._uuid = uuid;
|
|
||||||
this._version_tag = version_tag;
|
|
||||||
this._name = name;
|
|
||||||
|
|
||||||
this.setButtons([{ label: _("Cancel"),
|
|
||||||
action: Lang.bind(this, this._onCancelButtonPressed),
|
|
||||||
key: Clutter.Escape
|
|
||||||
},
|
|
||||||
{ label: _("Install"),
|
|
||||||
action: Lang.bind(this, this._onInstallButtonPressed)
|
|
||||||
}]);
|
|
||||||
|
|
||||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(name);
|
|
||||||
|
|
||||||
this._descriptionLabel = new St.Label({ text: message });
|
|
||||||
|
|
||||||
this.contentLayout.add(this._descriptionLabel,
|
|
||||||
{ y_fill: true,
|
|
||||||
y_align: St.Align.START });
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCancelButtonPressed: function(button, event) {
|
|
||||||
this.close(global.get_current_time());
|
|
||||||
|
|
||||||
// Even though the extension is already "uninstalled", send through
|
|
||||||
// a state-changed signal for any users who want to know if the install
|
|
||||||
// went through correctly -- using proper async DBus would block more
|
|
||||||
// traditional clients like the plugin
|
|
||||||
let meta = { uuid: this._uuid,
|
|
||||||
state: ExtensionState.UNINSTALLED,
|
|
||||||
error: '' };
|
|
||||||
|
|
||||||
_signals.emit('extension-state-changed', meta);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onInstallButtonPressed: function(button, event) {
|
|
||||||
let state = { uuid: this._uuid,
|
|
||||||
state: ExtensionState.DOWNLOADING,
|
|
||||||
error: '' };
|
|
||||||
|
|
||||||
_signals.emit('extension-state-changed', state);
|
|
||||||
|
|
||||||
let params = { version_tag: this._version_tag,
|
|
||||||
shell_version: Config.PACKAGE_VERSION,
|
|
||||||
api_version: API_VERSION.toString() };
|
|
||||||
|
|
||||||
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
|
|
||||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
|
||||||
|
|
||||||
_httpSession.queue_message(message,
|
|
||||||
Lang.bind(this, function(session, message) {
|
|
||||||
gotExtensionZipFile(session, message, this._uuid);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.close(global.get_current_time());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
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() {
|
||||||
|
if (Main.sessionMode.allowExtensions)
|
||||||
|
enableAllExtensions();
|
||||||
|
else
|
||||||
|
disableAllExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Main.sessionMode.connect('updated', _sessionUpdated);
|
||||||
|
_sessionUpdated();
|
||||||
|
}
|
||||||
|
358
js/ui/grabHelper.js
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
/* -*- 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
|
||||||
|
//
|
||||||
|
// 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) {
|
||||||
|
this._owner = owner;
|
||||||
|
|
||||||
|
this._grabStack = [];
|
||||||
|
|
||||||
|
this._actors = [];
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
this._eventId = 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) {
|
||||||
|
while (actor) {
|
||||||
|
if (this._actors.indexOf(actor) != -1)
|
||||||
|
return true;
|
||||||
|
actor = actor.get_parent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get currentGrab() {
|
||||||
|
return this._grabStack[this._grabStack.length - 1] || {};
|
||||||
|
},
|
||||||
|
|
||||||
|
_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;
|
||||||
|
},
|
||||||
|
|
||||||
|
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,
|
||||||
|
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 (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))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||||
|
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._eventId > 0) {
|
||||||
|
global.stage.disconnect(this._eventId);
|
||||||
|
this._eventId = 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;
|
||||||
|
this._prevFocusedWindow = metaDisplay.focus_window;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._prevFocusedWindow) {
|
||||||
|
let metaDisplay = global.screen.get_display();
|
||||||
|
if (!metaDisplay.focus_window) {
|
||||||
|
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
|
||||||
|
false, 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 });
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!button && this._modalCount == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._isWithinGrabbedActor(event.get_source()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Main.keyboard.shouldTakeEvent(event))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
// If we have a press event, ignore the next event,
|
||||||
|
// which should be a release event.
|
||||||
|
if (press)
|
||||||
|
this._ignoreRelease = true;
|
||||||
|
this.ungrab({ actor: this._grabStack[0].actor });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._modalCount > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
// We catch 'event' rather than 'key-press-event' so that we get
|
||||||
|
// a chance to run before the overview's own Escape check
|
||||||
|
_onEvent: function(actor, event) {
|
||||||
|
if (event.type() == Clutter.EventType.KEY_PRESS &&
|
||||||
|
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||||
|
this.ungrab();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyFocusChanged: function() {
|
||||||
|
let focus = global.stage.key_focus;
|
||||||
|
if (!focus || !this._isWithinGrabbedActor(focus))
|
||||||
|
this.ungrab();
|
||||||
|
},
|
||||||
|
|
||||||
|
_focusWindowChanged: function() {
|
||||||
|
let metaDisplay = global.screen.get_display();
|
||||||
|
if (metaDisplay.focus_window != null)
|
||||||
|
this.ungrab();
|
||||||
|
}
|
||||||
|
});
|
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());
|
||||||
|
}
|
||||||
|
});
|
@ -146,11 +146,6 @@ const BaseIcon = new Lang.Class({
|
|||||||
size = found ? len : ICON_SIZE;
|
size = found ? len : ICON_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't create icons unnecessarily
|
|
||||||
if (size == this.iconSize &&
|
|
||||||
this._iconBin.child)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._createIconTexture(size);
|
this._createIconTexture(size);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -287,6 +282,10 @@ const IconGrid = new Lang.Class({
|
|||||||
return this._computeLayout(rowWidth)[0];
|
return this._computeLayout(rowWidth)[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getRowLimit: function() {
|
||||||
|
return this._rowLimit;
|
||||||
|
},
|
||||||
|
|
||||||
_computeLayout: function (forWidth) {
|
_computeLayout: function (forWidth) {
|
||||||
let nColumns = 0;
|
let nColumns = 0;
|
||||||
let usedWidth = 0;
|
let usedWidth = 0;
|
||||||
@ -310,21 +309,22 @@ const IconGrid = new Lang.Class({
|
|||||||
this._grid.queue_relayout();
|
this._grid.queue_relayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
removeAll: function () {
|
removeAll: function() {
|
||||||
this._grid.get_children().forEach(Lang.bind(this, function (child) {
|
this._grid.destroy_all_children();
|
||||||
child.destroy();
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addItem: function(actor) {
|
addItem: function(actor, index) {
|
||||||
|
if (index !== undefined)
|
||||||
|
this._grid.insert_child_at_index(actor, index);
|
||||||
|
else
|
||||||
this._grid.add_actor(actor);
|
this._grid.add_actor(actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
getItemAtIndex: function(index) {
|
getItemAtIndex: function(index) {
|
||||||
return this._grid.get_children()[index];
|
return this._grid.get_child_at_index(index);
|
||||||
},
|
},
|
||||||
|
|
||||||
visibleItemsCount: function() {
|
visibleItemsCount: function() {
|
||||||
return this._grid.get_children().length - this._grid.get_n_skip_paint();
|
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -93,7 +93,7 @@ const Key = new Lang.Class({
|
|||||||
this._getExtendedKeys();
|
this._getExtendedKeys();
|
||||||
this.actor._extended_keys = this._extended_keyboard;
|
this.actor._extended_keys = this._extended_keyboard;
|
||||||
this._boxPointer.actor.hide();
|
this._boxPointer.actor.hide();
|
||||||
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
|
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ const Key = new Lang.Class({
|
|||||||
this.actor.fake_release();
|
this.actor.fake_release();
|
||||||
this._boxPointer.actor.raise_top();
|
this._boxPointer.actor.raise_top();
|
||||||
this._boxPointer.setPosition(this.actor, 0.5);
|
this._boxPointer.setPosition(this.actor, 0.5);
|
||||||
this._boxPointer.show(true);
|
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||||
this.actor.set_hover(false);
|
this.actor.set_hover(false);
|
||||||
if (!this._grabbed) {
|
if (!this._grabbed) {
|
||||||
Main.pushModal(this.actor);
|
Main.pushModal(this.actor);
|
||||||
@ -186,7 +186,7 @@ const Key = new Lang.Class({
|
|||||||
} else {
|
} else {
|
||||||
if (this._grabbed)
|
if (this._grabbed)
|
||||||
this._ungrab();
|
this._ungrab();
|
||||||
this._boxPointer.hide(true);
|
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -200,8 +200,10 @@ const Keyboard = new Lang.Class({
|
|||||||
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
|
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
|
||||||
|
|
||||||
this.actor = null;
|
this.actor = null;
|
||||||
|
this._focusInTray = false;
|
||||||
|
this._focusInExtendedKeys = false;
|
||||||
|
|
||||||
this._timestamp = global.get_current_time();
|
this._timestamp = global.display.get_current_time_roundtrip();
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||||
|
|
||||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||||
@ -288,7 +290,15 @@ const Keyboard = new Lang.Class({
|
|||||||
|
|
||||||
// Showing an extended key popup and clicking a key from the extended keys
|
// Showing an extended key popup and clicking a key from the extended keys
|
||||||
// will grab focus, but ignore that
|
// will grab focus, but ignore that
|
||||||
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
|
let extendedKeysWereFocused = this._focusInExtendedKeys;
|
||||||
|
this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key);
|
||||||
|
if (this._focusInExtendedKeys || extendedKeysWereFocused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ignore focus changes caused by message tray showing/hiding
|
||||||
|
let trayWasFocused = this._focusInTray;
|
||||||
|
this._focusInTray = (focus && Main.messageTray.actor.contains(focus));
|
||||||
|
if (this._focusInTray || trayWasFocused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let time = global.get_current_time();
|
let time = global.get_current_time();
|
||||||
@ -339,6 +349,13 @@ const Keyboard = new Lang.Class({
|
|||||||
trayButton.reactive = true;
|
trayButton.reactive = true;
|
||||||
trayButton.remove_style_pseudo_class('grayed');
|
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;
|
return trayButton;
|
||||||
},
|
},
|
||||||
@ -468,6 +485,12 @@ const Keyboard = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldTakeEvent: function(event) {
|
||||||
|
let actor = event.get_source();
|
||||||
|
return Main.layoutManager.keyboardBox.contains(actor) ||
|
||||||
|
actor._extended_keys || actor.extended_key;
|
||||||
|
},
|
||||||
|
|
||||||
show: function () {
|
show: function () {
|
||||||
this._redraw();
|
this._redraw();
|
||||||
|
|
||||||
@ -494,14 +517,29 @@ const Keyboard = new Lang.Class({
|
|||||||
this._moveTemporarily();
|
this._moveTemporarily();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// _compareTimestamp:
|
||||||
|
//
|
||||||
|
// Compare two timestamps taking into account
|
||||||
|
// CURRENT_TIME (0)
|
||||||
|
_compareTimestamp: function(one, two) {
|
||||||
|
if (one == two)
|
||||||
|
return 0;
|
||||||
|
if (one == Clutter.CURRENT_TIME)
|
||||||
|
return 1;
|
||||||
|
if (two == Clutter.CURRENT_TIME)
|
||||||
|
return -1;
|
||||||
|
return one - two;
|
||||||
|
},
|
||||||
|
|
||||||
// D-Bus methods
|
// D-Bus methods
|
||||||
Show: function(timestamp) {
|
Show: function(timestamp) {
|
||||||
if (!this._enableKeyboard)
|
if (!this._enableKeyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (timestamp - this._timestamp < 0)
|
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (timestamp != Clutter.CURRENT_TIME)
|
||||||
this._timestamp = timestamp;
|
this._timestamp = timestamp;
|
||||||
this.show();
|
this.show();
|
||||||
},
|
},
|
||||||
@ -510,9 +548,10 @@ const Keyboard = new Lang.Class({
|
|||||||
if (!this._enableKeyboard)
|
if (!this._enableKeyboard)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (timestamp - this._timestamp < 0)
|
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (timestamp != Clutter.CURRENT_TIME)
|
||||||
this._timestamp = timestamp;
|
this._timestamp = timestamp;
|
||||||
this.hide();
|
this.hide();
|
||||||
},
|
},
|
||||||
@ -541,16 +580,9 @@ const KeyboardSource = new Lang.Class({
|
|||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(keyboard) {
|
_init: function(keyboard) {
|
||||||
this.parent(_("Keyboard"));
|
|
||||||
this._keyboard = keyboard;
|
this._keyboard = keyboard;
|
||||||
|
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
this.keepTrayOnSummaryClick = true;
|
||||||
},
|
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
|
||||||
return new St.Icon({ icon_name: 'input-keyboard',
|
|
||||||
icon_type: St.IconType.SYMBOLIC,
|
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSummaryClick: function() {
|
handleSummaryClick: function() {
|
||||||
|
225
js/ui/layout.js
@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
@ -11,12 +12,85 @@ const St = imports.gi.St;
|
|||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const ScreenSaver = imports.misc.screenSaver;
|
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||||
const STARTUP_ANIMATION_TIME = 0.2;
|
const STARTUP_ANIMATION_TIME = 0.2;
|
||||||
const KEYBOARD_ANIMATION_TIME = 0.5;
|
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) {
|
||||||
|
this._primary = v;
|
||||||
|
if (this.actor)
|
||||||
|
this.actor.queue_relayout();
|
||||||
|
this.notify('primary');
|
||||||
|
},
|
||||||
|
|
||||||
|
get index() {
|
||||||
|
return this._index;
|
||||||
|
},
|
||||||
|
|
||||||
|
set index(v) {
|
||||||
|
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({
|
const LayoutManager = new Lang.Class({
|
||||||
Name: 'LayoutManager',
|
Name: 'LayoutManager',
|
||||||
@ -27,32 +101,46 @@ const LayoutManager = new Lang.Class({
|
|||||||
this.primaryMonitor = null;
|
this.primaryMonitor = null;
|
||||||
this.primaryIndex = -1;
|
this.primaryIndex = -1;
|
||||||
this._hotCorners = [];
|
this._hotCorners = [];
|
||||||
|
this._rootPixmap = null;
|
||||||
this._leftPanelBarrier = 0;
|
this._leftPanelBarrier = 0;
|
||||||
this._rightPanelBarrier = 0;
|
this._rightPanelBarrier = 0;
|
||||||
this._trayBarrier = 0;
|
this._trayBarrier = 0;
|
||||||
|
|
||||||
this._chrome = new Chrome(this);
|
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',
|
this.panelBox = new St.BoxLayout({ name: 'panelBox',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this.addChrome(this.panelBox, { affectsStruts: true });
|
this.addChrome(this.panelBox, { affectsStruts: true,
|
||||||
|
trackFullscreen: true });
|
||||||
this.panelBox.connect('allocation-changed',
|
this.panelBox.connect('allocation-changed',
|
||||||
Lang.bind(this, this._updatePanelBarriers));
|
Lang.bind(this, this._updatePanelBarriers));
|
||||||
|
|
||||||
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
|
this.trayBox = new St.Widget({ name: 'trayBox',
|
||||||
this.addChrome(this.trayBox, { visibleInFullscreen: true });
|
layout_manager: new Clutter.BinLayout() });
|
||||||
|
this.addChrome(this.trayBox);
|
||||||
this.trayBox.connect('allocation-changed',
|
this.trayBox.connect('allocation-changed',
|
||||||
Lang.bind(this, this._updateTrayBarrier));
|
Lang.bind(this, this._updateTrayBarrier));
|
||||||
|
|
||||||
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
|
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
track_hover: true });
|
track_hover: true });
|
||||||
this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
|
this.addChrome(this.keyboardBox);
|
||||||
this._keyboardHeightNotifyId = 0;
|
this._keyboardHeightNotifyId = 0;
|
||||||
|
|
||||||
global.screen.connect('monitors-changed',
|
global.screen.connect('monitors-changed',
|
||||||
Lang.bind(this, this._monitorsChanged));
|
Lang.bind(this, this._monitorsChanged));
|
||||||
this._monitorsChanged();
|
this._monitorsChanged();
|
||||||
|
|
||||||
|
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
|
||||||
|
this.emit('primary-fullscreen-changed', state);
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
// This is called by Main after everything else is constructed;
|
// This is called by Main after everything else is constructed;
|
||||||
@ -145,6 +233,9 @@ const LayoutManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateBoxes: function() {
|
_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_position(this.primaryMonitor.x, this.primaryMonitor.y);
|
||||||
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||||
|
|
||||||
@ -224,33 +315,47 @@ const LayoutManager = new Lang.Class({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
get focusIndex() {
|
get currentMonitor() {
|
||||||
let focusWindow = global.display.focus_window;
|
let index = global.screen.get_current_monitor();
|
||||||
|
return this.monitors[index];
|
||||||
if (focusWindow) {
|
|
||||||
let wrect = focusWindow.get_outer_rect();
|
|
||||||
for (let i = 0; i < this.monitors.length; i++) {
|
|
||||||
let monitor = this.monitors[i];
|
|
||||||
|
|
||||||
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
|
|
||||||
monitor.x + monitor.width > wrect.x &&
|
|
||||||
monitor.y + monitor.height > wrect.y)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.primaryIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
get focusMonitor() {
|
|
||||||
return this.monitors[this.focusIndex];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_startupAnimation: function() {
|
_startupAnimation: function() {
|
||||||
|
this.panelBox.anchor_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._rootPixmap = global.create_xrootpmap_texture();
|
||||||
|
if (this._rootPixmap != null) {
|
||||||
|
Main.uiGroup.add_actor(this._rootPixmap);
|
||||||
|
Tweener.addTween(this._rootPixmap,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: PLYMOUTH_TRANSITION_TIME,
|
||||||
|
transition: 'linear',
|
||||||
|
onComplete: this._fadeRootpmapComplete,
|
||||||
|
onCompleteScope: this });
|
||||||
|
plymouthTransitionRunning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plymouthTransitionRunning)
|
||||||
|
this._fadeRootpmapComplete();
|
||||||
|
},
|
||||||
|
|
||||||
|
_fadeRootpmapComplete: function() {
|
||||||
// Don't animate the strut
|
// Don't animate the strut
|
||||||
this._chrome.freezeUpdateRegions();
|
this._chrome.freezeUpdateRegions();
|
||||||
|
|
||||||
this.panelBox.anchor_y = this.panelBox.height;
|
if (this._rootPixmap != null) {
|
||||||
|
this._rootPixmap.destroy();
|
||||||
|
this._rootPixmap = null;
|
||||||
|
}
|
||||||
|
|
||||||
Tweener.addTween(this.panelBox,
|
Tweener.addTween(this.panelBox,
|
||||||
{ anchor_y: 0,
|
{ anchor_y: 0,
|
||||||
time: STARTUP_ANIMATION_TIME,
|
time: STARTUP_ANIMATION_TIME,
|
||||||
@ -265,7 +370,6 @@ const LayoutManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
showKeyboard: function () {
|
showKeyboard: function () {
|
||||||
Main.messageTray.hide();
|
|
||||||
this.keyboardBox.raise_top();
|
this.keyboardBox.raise_top();
|
||||||
Tweener.addTween(this.keyboardBox,
|
Tweener.addTween(this.keyboardBox,
|
||||||
{ anchor_y: this.keyboardBox.height,
|
{ anchor_y: this.keyboardBox.height,
|
||||||
@ -279,6 +383,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
time: KEYBOARD_ANIMATION_TIME,
|
time: KEYBOARD_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.emit('keyboard-visible-changed', true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_showKeyboardComplete: function() {
|
_showKeyboardComplete: function() {
|
||||||
@ -293,7 +399,6 @@ const LayoutManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
hideKeyboard: function (immediate) {
|
hideKeyboard: function (immediate) {
|
||||||
Main.messageTray.hide();
|
|
||||||
if (this._keyboardHeightNotifyId) {
|
if (this._keyboardHeightNotifyId) {
|
||||||
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
|
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
|
||||||
this._keyboardHeightNotifyId = 0;
|
this._keyboardHeightNotifyId = 0;
|
||||||
@ -310,6 +415,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.emit('keyboard-visible-changed', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_hideKeyboardComplete: function() {
|
_hideKeyboardComplete: function() {
|
||||||
@ -331,8 +438,10 @@ const LayoutManager = new Lang.Class({
|
|||||||
// the window manager struts. Changes to @actor's visibility will
|
// the window manager struts. Changes to @actor's visibility will
|
||||||
// NOT affect whether or not the strut is present, however.
|
// NOT affect whether or not the strut is present, however.
|
||||||
//
|
//
|
||||||
// If %visibleInFullscreen in @params is %true, the actor will be
|
// If %trackFullscreen in @params is %true, the actor's visibility
|
||||||
// visible even when a fullscreen window should be covering it.
|
// 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) {
|
addChrome: function(actor, params) {
|
||||||
this._chrome.addActor(actor, params);
|
this._chrome.addActor(actor, params);
|
||||||
},
|
},
|
||||||
@ -346,10 +455,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
// struts or input region to cover specific children.
|
// struts or input region to cover specific children.
|
||||||
//
|
//
|
||||||
// @params can have any of the same values as in addChrome(),
|
// @params can have any of the same values as in addChrome(),
|
||||||
// though some possibilities don't make sense (eg, trying to have
|
// though some possibilities don't make sense. By default, @actor has
|
||||||
// a %visibleInFullscreen child of a non-%visibleInFullscreen
|
// the same params as its chrome ancestor.
|
||||||
// parent). By default, @actor has the same params as its chrome
|
|
||||||
// ancestor.
|
|
||||||
trackChrome: function(actor, params) {
|
trackChrome: function(actor, params) {
|
||||||
this._chrome.trackActor(actor, params);
|
this._chrome.trackActor(actor, params);
|
||||||
},
|
},
|
||||||
@ -555,7 +662,7 @@ const HotCorner = new Lang.Class({
|
|||||||
// workspace content.
|
// workspace content.
|
||||||
|
|
||||||
const defaultParams = {
|
const defaultParams = {
|
||||||
visibleInFullscreen: false,
|
trackFullscreen: false,
|
||||||
affectsStruts: false,
|
affectsStruts: false,
|
||||||
affectsInputRegion: true
|
affectsInputRegion: true
|
||||||
};
|
};
|
||||||
@ -582,24 +689,13 @@ const Chrome = new Lang.Class({
|
|||||||
global.screen.connect('notify::n-workspaces',
|
global.screen.connect('notify::n-workspaces',
|
||||||
Lang.bind(this, this._queueUpdateRegions));
|
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();
|
this._relayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function() {
|
init: function() {
|
||||||
Main.overview.connect('showing',
|
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
|
||||||
Lang.bind(this, this._overviewShowing));
|
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
|
||||||
Main.overview.connect('hidden',
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
Lang.bind(this, this._overviewHidden));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addActor: function(actor, params) {
|
addActor: function(actor, params) {
|
||||||
@ -694,19 +790,18 @@ const Chrome = new Lang.Class({
|
|||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||||
let actorData = this._trackedActors[i], visible;
|
let actorData = this._trackedActors[i], visible;
|
||||||
|
if (!actorData.trackFullscreen)
|
||||||
|
continue;
|
||||||
if (!actorData.isToplevel)
|
if (!actorData.isToplevel)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (this._screenSaverActive)
|
if (this._inOverview || !Main.sessionMode.hasWindows)
|
||||||
visible = false;
|
|
||||||
else if (this._inOverview)
|
|
||||||
visible = true;
|
visible = true;
|
||||||
else if (!actorData.visibleInFullscreen &&
|
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||||
this.findMonitorForActor(actorData.actor).inFullscreen)
|
|
||||||
visible = false;
|
visible = false;
|
||||||
else
|
else
|
||||||
visible = true;
|
visible = true;
|
||||||
Main.uiGroup.set_skip_paint(actorData.actor, !visible);
|
actorData.actor.visible = visible;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -722,6 +817,11 @@ const Chrome = new Lang.Class({
|
|||||||
this._queueUpdateRegions();
|
this._queueUpdateRegions();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
this._updateVisibility();
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
_relayout: function() {
|
_relayout: function() {
|
||||||
this._monitors = this._layoutManager.monitors;
|
this._monitors = this._layoutManager.monitors;
|
||||||
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
||||||
@ -731,12 +831,6 @@ const Chrome = new Lang.Class({
|
|||||||
this._queueUpdateRegions();
|
this._queueUpdateRegions();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onScreenSaverActiveChanged: function(screenSaverActive) {
|
|
||||||
this._screenSaverActive = screenSaverActive;
|
|
||||||
this._updateVisibility();
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_findMonitorForRect: function(x, y, w, h) {
|
_findMonitorForRect: function(x, y, w, h) {
|
||||||
// First look at what monitor the center of the rectangle is at
|
// First look at what monitor the center of the rectangle is at
|
||||||
let cx = x + w/2;
|
let cx = x + w/2;
|
||||||
@ -852,6 +946,8 @@ const Chrome = new Lang.Class({
|
|||||||
for (let i = 0; i < this._monitors.length; i++)
|
for (let i = 0; i < this._monitors.length; i++)
|
||||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||||
|
|
||||||
|
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
|
||||||
|
|
||||||
this._updateFullscreen();
|
this._updateFullscreen();
|
||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
@ -861,10 +957,15 @@ const Chrome = new Lang.Class({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
this._queueUpdateRegions();
|
this._queueUpdateRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
|
||||||
|
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateRegions: function() {
|
updateRegions: function() {
|
||||||
@ -979,3 +1080,5 @@ const Chrome = new Lang.Class({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Signals.addSignalMethods(Chrome.prototype);
|
||||||
|
@ -8,6 +8,8 @@ const St = imports.gi.St;
|
|||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const DEFAULT_FADE_FACTOR = 0.4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lightbox:
|
* Lightbox:
|
||||||
* @container: parent Clutter.Container
|
* @container: parent Clutter.Container
|
||||||
@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener;
|
|||||||
* - inhibitEvents: whether to inhibit events for @container
|
* - inhibitEvents: whether to inhibit events for @container
|
||||||
* - width: shade actor width
|
* - width: shade actor width
|
||||||
* - height: shade actor height
|
* - height: shade actor height
|
||||||
* - fadeTime: seconds used to fade in/out
|
* - fadeInTime: seconds used to fade in
|
||||||
|
* - fadeOutTime: seconds used to fade out
|
||||||
*
|
*
|
||||||
* Lightbox creates a dark translucent "shade" actor to hide the
|
* Lightbox creates a dark translucent "shade" actor to hide the
|
||||||
* contents of @container, and allows you to specify particular actors
|
* contents of @container, and allows you to specify particular actors
|
||||||
@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({
|
|||||||
params = Params.parse(params, { inhibitEvents: false,
|
params = Params.parse(params, { inhibitEvents: false,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
fadeTime: null
|
fadeInTime: null,
|
||||||
|
fadeOutTime: null,
|
||||||
|
fadeFactor: DEFAULT_FADE_FACTOR
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container = container;
|
this._container = container;
|
||||||
this._children = container.get_children();
|
this._children = container.get_children();
|
||||||
this._fadeTime = params.fadeTime;
|
this._fadeInTime = params.fadeInTime;
|
||||||
|
this._fadeOutTime = params.fadeOutTime;
|
||||||
|
this._fadeFactor = params.fadeFactor;
|
||||||
this.actor = new St.Bin({ x: 0,
|
this.actor = new St.Bin({ x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
style_class: 'lightbox',
|
style_class: 'lightbox',
|
||||||
@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({
|
|||||||
container.add_actor(this.actor);
|
container.add_actor(this.actor);
|
||||||
this.actor.raise_top();
|
this.actor.raise_top();
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
this.shown = false;
|
||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
if (this._fadeTime) {
|
if (this._fadeInTime) {
|
||||||
|
this.shown = false;
|
||||||
this.actor.opacity = 0;
|
this.actor.opacity = 0;
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 255,
|
{ opacity: 255 * this._fadeFactor,
|
||||||
time: this._fadeTime,
|
time: this._fadeInTime,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this.shown = true;
|
||||||
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.actor.opacity = 255;
|
this.actor.opacity = 255 * this._fadeFactor;
|
||||||
|
this.shown = true;
|
||||||
}
|
}
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
if (this._fadeTime) {
|
this.shown = false;
|
||||||
|
if (this._fadeOutTime) {
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: this._fadeTime,
|
time: this._fadeOutTime,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
@ -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 Signals = imports.signals;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
const System = imports.system;
|
||||||
|
|
||||||
const History = imports.misc.history;
|
const History = imports.misc.history;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Link = imports.ui.link;
|
|
||||||
const ShellEntry = imports.ui.shellEntry;
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const JsParse = imports.misc.jsParse;
|
const JsParse = imports.misc.jsParse;
|
||||||
|
|
||||||
|
const CHEVRON = '>>> ';
|
||||||
|
|
||||||
/* Imports...feel free to add here as needed */
|
/* Imports...feel free to add here as needed */
|
||||||
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||||
'const GLib = imports.gi.GLib; ' +
|
'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
|
/* Utility functions...we should probably be able to use these
|
||||||
* in the shell core code too. */
|
* in the shell core code too. */
|
||||||
'const stage = global.stage; ' +
|
'const stage = global.stage; ' +
|
||||||
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
|
|
||||||
/* Special lookingGlass functions */
|
/* Special lookingGlass functions */
|
||||||
'const it = Main.lookingGlass.getIt(); ' +
|
'const it = Main.lookingGlass.getIt(); ' +
|
||||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||||
@ -261,9 +262,8 @@ function objectToString(o) {
|
|||||||
|
|
||||||
const ObjLink = new Lang.Class({
|
const ObjLink = new Lang.Class({
|
||||||
Name: 'ObjLink',
|
Name: 'ObjLink',
|
||||||
Extends: Link.Link,
|
|
||||||
|
|
||||||
_init: function(o, title) {
|
_init: function(lookingGlass, o, title) {
|
||||||
let text;
|
let text;
|
||||||
if (title)
|
if (title)
|
||||||
text = title;
|
text = title;
|
||||||
@ -272,24 +272,30 @@ const ObjLink = new Lang.Class({
|
|||||||
text = GLib.markup_escape_text(text, -1);
|
text = GLib.markup_escape_text(text, -1);
|
||||||
this._obj = o;
|
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.get_child().single_line_mode = true;
|
||||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onClicked: function (link) {
|
_onClicked: function (link) {
|
||||||
Main.lookingGlass.inspectObject(this._obj, this.actor);
|
this._lookingGlass.inspectObject(this._obj, this.actor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Result = new Lang.Class({
|
const Result = new Lang.Class({
|
||||||
Name: 'Result',
|
Name: 'Result',
|
||||||
|
|
||||||
_init : function(command, o, index) {
|
_init: function(lookingGlass, command, o, index) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.o = o;
|
this.o = o;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ vertical: true });
|
this.actor = new St.BoxLayout({ vertical: true });
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
|
|
||||||
let cmdTxt = new St.Label({ text: command });
|
let cmdTxt = new St.Label({ text: command });
|
||||||
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
@ -299,7 +305,7 @@ const Result = new Lang.Class({
|
|||||||
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
|
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
|
||||||
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
box.add(resultTxt);
|
box.add(resultTxt);
|
||||||
let objLink = new ObjLink(o);
|
let objLink = new ObjLink(this._lookingGlass, o);
|
||||||
box.add(objLink.actor);
|
box.add(objLink.actor);
|
||||||
let line = new Clutter.Rectangle({ name: 'Separator' });
|
let line = new Clutter.Rectangle({ name: 'Separator' });
|
||||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
||||||
@ -311,16 +317,18 @@ const Result = new Lang.Class({
|
|||||||
const WindowList = new Lang.Class({
|
const WindowList = new Lang.Class({
|
||||||
Name: 'WindowList',
|
Name: 'WindowList',
|
||||||
|
|
||||||
_init : function () {
|
_init: function(lookingGlass) {
|
||||||
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
||||||
global.display.connect('window-created', 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));
|
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
|
||||||
|
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateWindowList: function() {
|
_updateWindowList: function() {
|
||||||
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
this.actor.destroy_all_children();
|
||||||
let windows = global.get_window_actors();
|
let windows = global.get_window_actors();
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
@ -332,7 +340,7 @@ const WindowList = new Lang.Class({
|
|||||||
}
|
}
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
this.actor.add(box);
|
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 });
|
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
|
||||||
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
|
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
|
||||||
box.add(propsBox);
|
box.add(propsBox);
|
||||||
@ -343,7 +351,7 @@ const WindowList = new Lang.Class({
|
|||||||
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
||||||
propsBox.add(propBox);
|
propsBox.add(propBox);
|
||||||
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
|
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(appLink.actor, { y_fill: false });
|
||||||
propBox.add(icon, { y_fill: false });
|
propBox.add(icon, { y_fill: false });
|
||||||
} else {
|
} else {
|
||||||
@ -357,18 +365,21 @@ Signals.addSignalMethods(WindowList.prototype);
|
|||||||
const ObjInspector = new Lang.Class({
|
const ObjInspector = new Lang.Class({
|
||||||
Name: 'ObjInspector',
|
Name: 'ObjInspector',
|
||||||
|
|
||||||
_init : function () {
|
_init: function(lookingGlass) {
|
||||||
this._obj = null;
|
this._obj = null;
|
||||||
this._previousObj = null;
|
this._previousObj = null;
|
||||||
|
|
||||||
this._parentList = [];
|
this._parentList = [];
|
||||||
|
|
||||||
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
|
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
this.actor.get_hscroll_bar().hide();
|
this.actor.get_hscroll_bar().hide();
|
||||||
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
|
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
|
||||||
style_class: 'lg-dialog',
|
style_class: 'lg-dialog',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this.actor.add_actor(this._container);
|
this.actor.add_actor(this._container);
|
||||||
|
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
},
|
},
|
||||||
|
|
||||||
selectObject: function(obj, skipPrevious) {
|
selectObject: function(obj, skipPrevious) {
|
||||||
@ -378,7 +389,7 @@ const ObjInspector = new Lang.Class({
|
|||||||
this._previousObj = null;
|
this._previousObj = null;
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
|
|
||||||
this._container.get_children().forEach(function (child) { child.destroy(); });
|
this._container.destroy_all_children();
|
||||||
|
|
||||||
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
|
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
|
||||||
this._container.add_actor(hbox);
|
this._container.add_actor(hbox);
|
||||||
@ -400,12 +411,19 @@ const ObjInspector = new Lang.Class({
|
|||||||
button.connect('clicked', Lang.bind(this, this.close));
|
button.connect('clicked', Lang.bind(this, this.close));
|
||||||
hbox.add(button);
|
hbox.add(button);
|
||||||
if (typeof(obj) == typeof({})) {
|
if (typeof(obj) == typeof({})) {
|
||||||
|
let properties = [];
|
||||||
for (let propName in obj) {
|
for (let propName in obj) {
|
||||||
|
properties.push(propName);
|
||||||
|
}
|
||||||
|
properties.sort();
|
||||||
|
|
||||||
|
for (let i = 0; i < properties.length; i++) {
|
||||||
|
let propName = properties[i];
|
||||||
let valueStr;
|
let valueStr;
|
||||||
let link;
|
let link;
|
||||||
try {
|
try {
|
||||||
let prop = obj[propName];
|
let prop = obj[propName];
|
||||||
link = new ObjLink(prop).actor;
|
link = new ObjLink(this._lookingGlass, prop).actor;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
link = new St.Label({ text: '<error>' });
|
link = new St.Label({ text: '<error>' });
|
||||||
}
|
}
|
||||||
@ -426,10 +444,6 @@ const ObjInspector = new Lang.Class({
|
|||||||
this.actor.show();
|
this.actor.show();
|
||||||
if (sourceActor) {
|
if (sourceActor) {
|
||||||
this.actor.set_scale(0, 0);
|
this.actor.set_scale(0, 0);
|
||||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
|
||||||
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
|
||||||
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
|
|
||||||
Math.floor(sourceY + sourceHeight / 2));
|
|
||||||
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
time: 0.2 });
|
time: 0.2 });
|
||||||
@ -450,7 +464,7 @@ const ObjInspector = new Lang.Class({
|
|||||||
_onInsert: function() {
|
_onInsert: function() {
|
||||||
let obj = this._obj;
|
let obj = this._obj;
|
||||||
this.close();
|
this.close();
|
||||||
Main.lookingGlass.insertObject(obj);
|
this._lookingGlass.insertObject(obj);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onBack: function() {
|
_onBack: function() {
|
||||||
@ -458,9 +472,14 @@ const ObjInspector = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function addBorderPaintHook(actor) {
|
const RedBorderEffect = new Lang.Class({
|
||||||
let signalId = actor.connect_after('paint',
|
Name: 'RedBorderEffect',
|
||||||
function () {
|
Extends: Clutter.Effect,
|
||||||
|
|
||||||
|
vfunc_paint: function() {
|
||||||
|
let actor = this.get_actor();
|
||||||
|
actor.continue_paint();
|
||||||
|
|
||||||
let color = new Cogl.Color();
|
let color = new Cogl.Color();
|
||||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||||
Cogl.set_source_color(color);
|
Cogl.set_source_color(color);
|
||||||
@ -476,16 +495,13 @@ function addBorderPaintHook(actor) {
|
|||||||
geom.width - width, geom.height - width);
|
geom.width - width, geom.height - width);
|
||||||
Cogl.rectangle(0, geom.height - width,
|
Cogl.rectangle(0, geom.height - width,
|
||||||
width, width);
|
width, width);
|
||||||
});
|
},
|
||||||
|
});
|
||||||
actor.queue_redraw();
|
|
||||||
return signalId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Inspector = new Lang.Class({
|
const Inspector = new Lang.Class({
|
||||||
Name: 'Inspector',
|
Name: 'Inspector',
|
||||||
|
|
||||||
_init: function() {
|
_init: function(lookingGlass) {
|
||||||
let container = new Shell.GenericContainer({ width: 0,
|
let container = new Shell.GenericContainer({ width: 0,
|
||||||
height: 0 });
|
height: 0 });
|
||||||
container.connect('allocate', Lang.bind(this, this._allocate));
|
container.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
@ -499,9 +515,6 @@ const Inspector = new Lang.Class({
|
|||||||
this._displayText = new St.Label();
|
this._displayText = new St.Label();
|
||||||
eventHandler.add(this._displayText, { expand: true });
|
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('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
|
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
|
||||||
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
@ -516,6 +529,8 @@ const Inspector = new Lang.Class({
|
|||||||
// out, or move the pointer outside of _pointerTarget.
|
// out, or move the pointer outside of _pointerTarget.
|
||||||
this._target = null;
|
this._target = null;
|
||||||
this._pointerTarget = null;
|
this._pointerTarget = null;
|
||||||
|
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
_allocate: function(actor, box, flags) {
|
||||||
@ -536,18 +551,13 @@ const Inspector = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_close: function() {
|
_close: function() {
|
||||||
Clutter.ungrab_pointer(this._eventHandler);
|
Clutter.ungrab_pointer();
|
||||||
Clutter.ungrab_keyboard(this._eventHandler);
|
Clutter.ungrab_keyboard();
|
||||||
this._eventHandler.destroy();
|
this._eventHandler.destroy();
|
||||||
this._eventHandler = null;
|
this._eventHandler = null;
|
||||||
this.emit('closed');
|
this.emit('closed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
if (this._borderPaintTarget != null)
|
|
||||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onKeyPressEvent: function (actor, event) {
|
_onKeyPressEvent: function (actor, event) {
|
||||||
if (event.get_key_symbol() == Clutter.Escape)
|
if (event.get_key_symbol() == Clutter.Escape)
|
||||||
this._close();
|
this._close();
|
||||||
@ -616,56 +626,12 @@ const Inspector = new Lang.Class({
|
|||||||
this._displayText.text = '';
|
this._displayText.text = '';
|
||||||
this._displayText.text = position + ' ' + this._target;
|
this._displayText.text = position + ' ' + this._target;
|
||||||
|
|
||||||
if (this._borderPaintTarget != this._target) {
|
this._lookingGlass.setBorderPaintTarget(this._target);
|
||||||
if (this._borderPaintTarget != null)
|
|
||||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
|
||||||
this._borderPaintTarget = this._target;
|
|
||||||
this._borderPaintId = addBorderPaintHook(this._target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Signals.addSignalMethods(Inspector.prototype);
|
Signals.addSignalMethods(Inspector.prototype);
|
||||||
|
|
||||||
const ErrorLog = new Lang.Class({
|
|
||||||
Name: 'ErrorLog',
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.actor = new St.BoxLayout();
|
|
||||||
this.text = new St.Label();
|
|
||||||
this.actor.add(this.text);
|
|
||||||
// We need to override StLabel's default ellipsization when
|
|
||||||
// using line_wrap; otherwise ClutterText's layout is going
|
|
||||||
// to constrain both the width and height, which prevents
|
|
||||||
// scrolling.
|
|
||||||
this.text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
|
||||||
this.text.clutter_text.line_wrap = true;
|
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
|
||||||
},
|
|
||||||
|
|
||||||
_formatTime: function(d){
|
|
||||||
function pad(n) { return n < 10 ? '0' + n : n; }
|
|
||||||
return d.getUTCFullYear()+'-'
|
|
||||||
+ pad(d.getUTCMonth()+1)+'-'
|
|
||||||
+ pad(d.getUTCDate())+'T'
|
|
||||||
+ pad(d.getUTCHours())+':'
|
|
||||||
+ pad(d.getUTCMinutes())+':'
|
|
||||||
+ pad(d.getUTCSeconds())+'Z';
|
|
||||||
},
|
|
||||||
|
|
||||||
_renderText: function() {
|
|
||||||
if (!this.actor.mapped)
|
|
||||||
return;
|
|
||||||
let text = this.text.text;
|
|
||||||
let stack = Main._getAndClearErrorStack();
|
|
||||||
for (let i = 0; i < stack.length; i++) {
|
|
||||||
let logItem = stack[i];
|
|
||||||
text += logItem.category + ' t=' + this._formatTime(new Date(logItem.timestamp)) + ' ' + logItem.message + '\n';
|
|
||||||
}
|
|
||||||
this.text.text = text;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const Memory = new Lang.Class({
|
const Memory = new Lang.Class({
|
||||||
Name: 'Memory',
|
Name: 'Memory',
|
||||||
|
|
||||||
@ -694,7 +660,7 @@ const Memory = new Lang.Class({
|
|||||||
|
|
||||||
this._gcbutton = new St.Button({ label: 'Full GC',
|
this._gcbutton = new St.Button({ label: 'Full GC',
|
||||||
style_class: 'lg-obj-inspector-button' });
|
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,
|
this.actor.add(this._gcbutton, { x_align: St.Align.START,
|
||||||
x_fill: false });
|
x_fill: false });
|
||||||
|
|
||||||
@ -755,13 +721,13 @@ const Extensions = new Lang.Class({
|
|||||||
let extension = actor._extension;
|
let extension = actor._extension;
|
||||||
let uri = extension.dir.get_uri();
|
let uri = extension.dir.get_uri();
|
||||||
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
||||||
Main.lookingGlass.close();
|
this._lookingGlass.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onWebPage: function (actor) {
|
_onWebPage: function (actor) {
|
||||||
let extension = actor._extension;
|
let extension = actor._extension;
|
||||||
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
||||||
Main.lookingGlass.close();
|
this._lookingGlass.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onViewErrors: function (actor) {
|
_onViewErrors: function (actor) {
|
||||||
@ -825,24 +791,33 @@ const Extensions = new Lang.Class({
|
|||||||
text: this._stateToString(extension.state) });
|
text: this._stateToString(extension.state) });
|
||||||
metaBox.add(state);
|
metaBox.add(state);
|
||||||
|
|
||||||
let viewsource = new Link.Link({ label: _("View Source") });
|
let viewsource = new St.Button({ reactive: true,
|
||||||
viewsource.actor._extension = extension;
|
track_hover: true,
|
||||||
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
|
style_class: 'shell-link',
|
||||||
metaBox.add(viewsource.actor);
|
label: _("View Source") });
|
||||||
|
viewsource._extension = extension;
|
||||||
|
viewsource.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||||
|
metaBox.add(viewsource);
|
||||||
|
|
||||||
if (extension.metadata.url) {
|
if (extension.metadata.url) {
|
||||||
let webpage = new Link.Link({ label: _("Web Page") });
|
let webpage = new St.Button({ reactive: true,
|
||||||
webpage.actor._extension = extension;
|
track_hover: true,
|
||||||
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
style_class: 'shell-link',
|
||||||
metaBox.add(webpage.actor);
|
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") });
|
let viewerrors = new St.Button({ reactive: true,
|
||||||
viewerrors.actor._extension = extension;
|
track_hover: true,
|
||||||
viewerrors.actor._parentBox = box;
|
style_class: 'shell-link',
|
||||||
viewerrors.actor._isShowing = false;
|
label: _("Show Errors") });
|
||||||
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
|
viewerrors._extension = extension;
|
||||||
metaBox.add(viewerrors.actor);
|
viewerrors._parentBox = box;
|
||||||
|
viewerrors._isShowing = false;
|
||||||
|
viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors));
|
||||||
|
metaBox.add(viewerrors);
|
||||||
|
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
@ -853,8 +828,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
_init : function() {
|
_init : function() {
|
||||||
this._borderPaintTarget = null;
|
this._borderPaintTarget = null;
|
||||||
this._borderPaintId = 0;
|
this._redBorderEffect = new RedBorderEffect();
|
||||||
this._borderDestroyId = 0;
|
|
||||||
|
|
||||||
this._open = false;
|
this._open = false;
|
||||||
|
|
||||||
@ -884,19 +858,18 @@ const LookingGlass = new Lang.Class({
|
|||||||
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
||||||
Lang.bind(this, this._queueResize));
|
Lang.bind(this, this._queueResize));
|
||||||
|
|
||||||
this._objInspector = new ObjInspector();
|
this._objInspector = new ObjInspector(this);
|
||||||
Main.uiGroup.add_actor(this._objInspector.actor);
|
Main.uiGroup.add_actor(this._objInspector.actor);
|
||||||
this._objInspector.actor.hide();
|
this._objInspector.actor.hide();
|
||||||
|
|
||||||
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
||||||
this.actor.add_actor(toolbar);
|
this.actor.add_actor(toolbar);
|
||||||
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: 24 });
|
icon_size: 24 });
|
||||||
toolbar.add_actor(inspectIcon);
|
toolbar.add_actor(inspectIcon);
|
||||||
inspectIcon.reactive = true;
|
inspectIcon.reactive = true;
|
||||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||||
let inspector = new Inspector();
|
let inspector = new Inspector(this);
|
||||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||||
target);
|
target);
|
||||||
@ -926,23 +899,16 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
|
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
|
||||||
this._evalBox.add_actor(this._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._entryArea.add(label);
|
||||||
|
|
||||||
this._entry = new St.Entry({ can_focus: true });
|
this._entry = new St.Entry({ can_focus: true });
|
||||||
ShellEntry.addContextMenu(this._entry);
|
ShellEntry.addContextMenu(this._entry);
|
||||||
this._entryArea.add(this._entry, { expand: true });
|
this._entryArea.add(this._entry, { expand: true });
|
||||||
|
|
||||||
this._windowList = new WindowList();
|
this._windowList = new WindowList(this);
|
||||||
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
|
|
||||||
notebook.selectIndex(0);
|
|
||||||
this._pushResult('<window selection>', window);
|
|
||||||
}));
|
|
||||||
notebook.appendPage('Windows', this._windowList.actor);
|
notebook.appendPage('Windows', this._windowList.actor);
|
||||||
|
|
||||||
this._errorLog = new ErrorLog();
|
|
||||||
notebook.appendPage('Errors', this._errorLog.actor);
|
|
||||||
|
|
||||||
this._memory = new Memory();
|
this._memory = new Memory();
|
||||||
notebook.appendPage('Memory', this._memory.actor);
|
notebook.appendPage('Memory', this._memory.actor);
|
||||||
|
|
||||||
@ -994,23 +960,22 @@ const LookingGlass = new Lang.Class({
|
|||||||
+ 'font-family: "' + fontDesc.get_family() + '";';
|
+ '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) {
|
_pushResult: function(command, obj) {
|
||||||
let index = this._results.length + this._offset;
|
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._results.push(result);
|
||||||
this._resultsArea.add(result.actor);
|
this._resultsArea.add(result.actor);
|
||||||
if (this._borderPaintTarget != null) {
|
if (obj instanceof Clutter.Actor)
|
||||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
this.setBorderPaintTarget(obj);
|
||||||
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;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
let children = this._resultsArea.get_children();
|
let children = this._resultsArea.get_children();
|
||||||
if (children.length > this._maxItems) {
|
if (children.length > this._maxItems) {
|
||||||
this._results.shift();
|
this._results.shift();
|
||||||
@ -1124,8 +1089,8 @@ const LookingGlass = new Lang.Class({
|
|||||||
this.actor.width = myWidth;
|
this.actor.width = myWidth;
|
||||||
this.actor.height = myHeight;
|
this.actor.height = myHeight;
|
||||||
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
||||||
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
|
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
|
||||||
this._targetY + Math.floor(myHeight * 0.1));
|
primary.y + this._targetY + Math.floor(myHeight * 0.1));
|
||||||
},
|
},
|
||||||
|
|
||||||
insertObject: function(obj) {
|
insertObject: function(obj) {
|
||||||
@ -1191,11 +1156,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._open = false;
|
this._open = false;
|
||||||
Tweener.removeTweens(this.actor);
|
Tweener.removeTweens(this.actor);
|
||||||
|
|
||||||
if (this._borderPaintTarget != null) {
|
this.setBorderPaintTarget(null);
|
||||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
|
||||||
this._borderPaintTarget.disconnect(this._borderDestroyId);
|
|
||||||
this._borderPaintTarget = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Main.popModal(this._entry);
|
Main.popModal(this._entry);
|
||||||
|
|
||||||
|
@ -12,10 +12,11 @@ const Signals = imports.signals;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MagnifierDBus = imports.ui.magnifierDBus;
|
const MagnifierDBus = imports.ui.magnifierDBus;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const PointerWatcher = imports.ui.pointerWatcher;
|
||||||
|
|
||||||
const MOUSE_POLL_FREQUENCY = 50;
|
const MOUSE_POLL_FREQUENCY = 50;
|
||||||
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
||||||
|
const NO_CHANGE = 0.0;
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||||
@ -24,6 +25,14 @@ const SHOW_KEY = 'screen-magnifier-enabled';
|
|||||||
const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
|
const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
|
||||||
const SCREEN_POSITION_KEY = 'screen-position';
|
const SCREEN_POSITION_KEY = 'screen-position';
|
||||||
const MAG_FACTOR_KEY = 'mag-factor';
|
const MAG_FACTOR_KEY = 'mag-factor';
|
||||||
|
const INVERT_LIGHTNESS_KEY = 'invert-lightness';
|
||||||
|
const COLOR_SATURATION_KEY = 'color-saturation';
|
||||||
|
const BRIGHT_RED_KEY = 'brightness-red';
|
||||||
|
const BRIGHT_GREEN_KEY = 'brightness-green';
|
||||||
|
const BRIGHT_BLUE_KEY = 'brightness-blue';
|
||||||
|
const CONTRAST_RED_KEY = 'contrast-red';
|
||||||
|
const CONTRAST_GREEN_KEY = 'contrast-green';
|
||||||
|
const CONTRAST_BLUE_KEY = 'contrast-blue';
|
||||||
const LENS_MODE_KEY = 'lens-mode';
|
const LENS_MODE_KEY = 'lens-mode';
|
||||||
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
||||||
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
||||||
@ -47,7 +56,7 @@ const Magnifier = new Lang.Class({
|
|||||||
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
||||||
this._mouseSprite = new Clutter.Texture();
|
this._mouseSprite = new Clutter.Texture();
|
||||||
xfixesCursor.update_texture_image(this._mouseSprite);
|
xfixesCursor.update_texture_image(this._mouseSprite);
|
||||||
this._cursorRoot = new Clutter.Group();
|
this._cursorRoot = new Clutter.Actor();
|
||||||
this._cursorRoot.add_actor(this._mouseSprite);
|
this._cursorRoot.add_actor(this._mouseSprite);
|
||||||
|
|
||||||
// Create the first ZoomRegion and initialize it according to the
|
// Create the first ZoomRegion and initialize it according to the
|
||||||
@ -127,11 +136,8 @@ const Magnifier = new Lang.Class({
|
|||||||
* Turn on mouse tracking, if not already doing so.
|
* Turn on mouse tracking, if not already doing so.
|
||||||
*/
|
*/
|
||||||
startTrackingMouse: function() {
|
startTrackingMouse: function() {
|
||||||
if (!this._mouseTrackingId)
|
if (!this._pointerWatch)
|
||||||
this._mouseTrackingId = Mainloop.timeout_add(
|
this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(MOUSE_POLL_FREQUENCY, Lang.bind(this, this.scrollToMousePos));
|
||||||
MOUSE_POLL_FREQUENCY,
|
|
||||||
Lang.bind(this, this.scrollToMousePos)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,10 +145,10 @@ const Magnifier = new Lang.Class({
|
|||||||
* Turn off mouse tracking, if not already doing so.
|
* Turn off mouse tracking, if not already doing so.
|
||||||
*/
|
*/
|
||||||
stopTrackingMouse: function() {
|
stopTrackingMouse: function() {
|
||||||
if (this._mouseTrackingId)
|
if (this._pointerWatch)
|
||||||
Mainloop.source_remove(this._mouseTrackingId);
|
this._pointerWatch.remove();
|
||||||
|
|
||||||
this._mouseTrackingId = null;
|
this._pointerWatch = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -294,8 +300,7 @@ const Magnifier = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
setCrosshairsColor: function(color) {
|
setCrosshairsColor: function(color) {
|
||||||
if (this._crossHairs) {
|
if (this._crossHairs) {
|
||||||
let clutterColor = new Clutter.Color();
|
let [res, clutterColor] = Clutter.Color.from_string(color);
|
||||||
clutterColor.from_string(color);
|
|
||||||
this._crossHairs.setColor(clutterColor);
|
this._crossHairs.setColor(clutterColor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -443,6 +448,25 @@ const Magnifier = new Lang.Class({
|
|||||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||||
if (aPref)
|
if (aPref)
|
||||||
zoomRegion.setMouseTrackingMode(aPref);
|
zoomRegion.setMouseTrackingMode(aPref);
|
||||||
|
|
||||||
|
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||||
|
if (aPref)
|
||||||
|
zoomRegion.setInvertLightness(aPref);
|
||||||
|
|
||||||
|
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||||
|
if (aPref)
|
||||||
|
zoomRegion.setColorSaturation(aPref);
|
||||||
|
|
||||||
|
let bc = {};
|
||||||
|
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||||
|
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||||
|
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||||
|
zoomRegion.setBrightness(bc);
|
||||||
|
|
||||||
|
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||||
|
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||||
|
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||||
|
zoomRegion.setContrast(bc);
|
||||||
}
|
}
|
||||||
|
|
||||||
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
||||||
@ -465,6 +489,25 @@ const Magnifier = new Lang.Class({
|
|||||||
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
||||||
Lang.bind(this, this._updateMouseTrackingMode));
|
Lang.bind(this, this._updateMouseTrackingMode));
|
||||||
|
|
||||||
|
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
|
||||||
|
Lang.bind(this, this._updateInvertLightness));
|
||||||
|
this._settings.connect('changed::' + COLOR_SATURATION_KEY,
|
||||||
|
Lang.bind(this, this._updateColorSaturation));
|
||||||
|
|
||||||
|
this._settings.connect('changed::' + BRIGHT_RED_KEY,
|
||||||
|
Lang.bind(this, this._updateBrightness));
|
||||||
|
this._settings.connect('changed::' + BRIGHT_GREEN_KEY,
|
||||||
|
Lang.bind(this, this._updateBrightness));
|
||||||
|
this._settings.connect('changed::' + BRIGHT_BLUE_KEY,
|
||||||
|
Lang.bind(this, this._updateBrightness));
|
||||||
|
|
||||||
|
this._settings.connect('changed::' + CONTRAST_RED_KEY,
|
||||||
|
Lang.bind(this, this._updateContrast));
|
||||||
|
this._settings.connect('changed::' + CONTRAST_GREEN_KEY,
|
||||||
|
Lang.bind(this, this._updateContrast));
|
||||||
|
this._settings.connect('changed::' + CONTRAST_BLUE_KEY,
|
||||||
|
Lang.bind(this, this._updateContrast));
|
||||||
|
|
||||||
this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
|
this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
|
this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
|
||||||
@ -540,7 +583,47 @@ const Magnifier = new Lang.Class({
|
|||||||
this._settings.get_enum(MOUSE_TRACKING_KEY)
|
this._settings.get_enum(MOUSE_TRACKING_KEY)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateInvertLightness: function() {
|
||||||
|
// Applies only to the first zoom region.
|
||||||
|
if (this._zoomRegions.length) {
|
||||||
|
this._zoomRegions[0].setInvertLightness(
|
||||||
|
this._settings.get_boolean(INVERT_LIGHTNESS_KEY)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateColorSaturation: function() {
|
||||||
|
// Applies only to the first zoom region.
|
||||||
|
if (this._zoomRegions.length) {
|
||||||
|
this._zoomRegions[0].setColorSaturation(
|
||||||
|
this._settings.get_double(COLOR_SATURATION_KEY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBrightness: function() {
|
||||||
|
// Applies only to the first zoom region.
|
||||||
|
if (this._zoomRegions.length) {
|
||||||
|
let brightness = {};
|
||||||
|
brightness.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||||
|
brightness.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||||
|
brightness.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||||
|
this._zoomRegions[0].setBrightness(brightness);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateContrast: function() {
|
||||||
|
// Applies only to the first zoom region.
|
||||||
|
if (this._zoomRegions.length) {
|
||||||
|
let contrast = {};
|
||||||
|
contrast.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||||
|
contrast.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||||
|
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||||
|
this._zoomRegions[0].setContrast(contrast);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(Magnifier.prototype);
|
Signals.addSignalMethods(Magnifier.prototype);
|
||||||
|
|
||||||
@ -554,6 +637,10 @@ const ZoomRegion = new Lang.Class({
|
|||||||
this._clampScrollingAtEdges = false;
|
this._clampScrollingAtEdges = false;
|
||||||
this._lensMode = false;
|
this._lensMode = false;
|
||||||
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
||||||
|
this._invertLightness = false;
|
||||||
|
this._colorSaturation = 1.0;
|
||||||
|
this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
|
||||||
|
this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
|
||||||
|
|
||||||
this._magView = null;
|
this._magView = null;
|
||||||
this._background = null;
|
this._background = null;
|
||||||
@ -879,6 +966,107 @@ const ZoomRegion = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setInvertLightness:
|
||||||
|
* Set whether to invert the lightness of the magnified view.
|
||||||
|
* @flag Boolean to either invert brightness (true), or not (false).
|
||||||
|
*/
|
||||||
|
setInvertLightness: function(flag) {
|
||||||
|
this._invertLightness = flag;
|
||||||
|
if (this._magShaderEffects)
|
||||||
|
this._magShaderEffects.setInvertLightness(this._invertLightness);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getInvertLightness:
|
||||||
|
* Retrieve whether the lightness is inverted.
|
||||||
|
* @return Boolean indicating inversion (true), or not (false).
|
||||||
|
*/
|
||||||
|
getInvertLightness: function() {
|
||||||
|
return this._invertLightness;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setColorSaturation:
|
||||||
|
* Set the color saturation of the magnified view.
|
||||||
|
* @sauration A value from 0.0 to 1.0 that defines the color
|
||||||
|
* saturation, with 0.0 defining no color (grayscale),
|
||||||
|
* and 1.0 defining full color.
|
||||||
|
*/
|
||||||
|
setColorSaturation: function(saturation) {
|
||||||
|
this._colorSaturation = saturation;
|
||||||
|
if (this._magShaderEffects)
|
||||||
|
this._magShaderEffects.setColorSaturation(this._colorSaturation);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getColorSaturation:
|
||||||
|
* Retrieve the color saturation of the magnified view.
|
||||||
|
*/
|
||||||
|
getColorSaturation: function() {
|
||||||
|
return this._colorSaturation;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setBrightness:
|
||||||
|
* Alter the brightness of the magnified view.
|
||||||
|
* @brightness Object containing the contrast for the red, green,
|
||||||
|
* and blue channels. Values of 0.0 represent "standard"
|
||||||
|
* brightness (no change), whereas values less or greater than
|
||||||
|
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||||
|
*/
|
||||||
|
setBrightness: function(brightness) {
|
||||||
|
this._brightness.r = brightness.r;
|
||||||
|
this._brightness.g = brightness.g;
|
||||||
|
this._brightness.b = brightness.b;
|
||||||
|
if (this._magShaderEffects)
|
||||||
|
this._magShaderEffects.setBrightness(this._brightness);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBrightness:
|
||||||
|
* Retrive the current brightness of the Zoom Region.
|
||||||
|
* @return Object containing the brightness change for the red, green,
|
||||||
|
* and blue channels.
|
||||||
|
*/
|
||||||
|
getBrightness: function() {
|
||||||
|
let brightness = {};
|
||||||
|
brightness.r = this._brightness.r;
|
||||||
|
brightness.g = this._brightness.g;
|
||||||
|
brightness.b = this._brightness.b;
|
||||||
|
return brightness;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setContrast:
|
||||||
|
* Alter the contrast of the magnified view.
|
||||||
|
* @contrast Object containing the contrast for the red, green,
|
||||||
|
* and blue channels. Values of 0.0 represent "standard"
|
||||||
|
* contrast (no change), whereas values less or greater than
|
||||||
|
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||||
|
*/
|
||||||
|
setContrast: function(contrast) {
|
||||||
|
this._contrast.r = contrast.r;
|
||||||
|
this._contrast.g = contrast.g;
|
||||||
|
this._contrast.b = contrast.b;
|
||||||
|
if (this._magShaderEffects)
|
||||||
|
this._magShaderEffects.setContrast(this._contrast);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getContrast:
|
||||||
|
* Retreive the contrast of the magnified view.
|
||||||
|
* @return Object containing the contrast for the red, green,
|
||||||
|
* and blue channels.
|
||||||
|
*/
|
||||||
|
getContrast: function() {
|
||||||
|
let contrast = {};
|
||||||
|
contrast.r = this._contrast.r;
|
||||||
|
contrast.g = this._contrast.g;
|
||||||
|
contrast.b = this._contrast.b;
|
||||||
|
return contrast;
|
||||||
|
},
|
||||||
|
|
||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
_createActors: function() {
|
_createActors: function() {
|
||||||
@ -889,21 +1077,21 @@ const ZoomRegion = new Lang.Class({
|
|||||||
// hide the magnified region from CLUTTER_PICK_ALL
|
// hide the magnified region from CLUTTER_PICK_ALL
|
||||||
Shell.util_set_hidden_from_pick (this._magView, true);
|
Shell.util_set_hidden_from_pick (this._magView, true);
|
||||||
|
|
||||||
// Append a Clutter.Group to clip the contents of the magnified view.
|
// Add a group to clip the contents of the magnified view.
|
||||||
let mainGroup = new Clutter.Group({ clip_to_allocation: true });
|
let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
|
||||||
this._magView.set_child(mainGroup);
|
this._magView.set_child(mainGroup);
|
||||||
|
|
||||||
// Add a background for when the magnified uiGroup is scrolled
|
// Add a background for when the magnified uiGroup is scrolled
|
||||||
// out of view (don't want to see desktop showing through).
|
// out of view (don't want to see desktop showing through).
|
||||||
this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
|
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
|
||||||
|
width: global.screen_width,
|
||||||
|
height: global.screen_height });
|
||||||
mainGroup.add_actor(this._background);
|
mainGroup.add_actor(this._background);
|
||||||
|
|
||||||
// Clone the group that contains all of UI on the screen. This is the
|
// Clone the group that contains all of UI on the screen. This is the
|
||||||
// chrome, the windows, etc.
|
// chrome, the windows, etc.
|
||||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
||||||
mainGroup.add_actor(this._uiGroupClone);
|
mainGroup.add_actor(this._uiGroupClone);
|
||||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
|
||||||
this._background.set_size(global.screen_width, global.screen_height);
|
|
||||||
|
|
||||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||||
// it.
|
// it.
|
||||||
@ -917,6 +1105,13 @@ const ZoomRegion = new Lang.Class({
|
|||||||
this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
|
this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
|
||||||
else
|
else
|
||||||
this._crossHairsActor = null;
|
this._crossHairsActor = null;
|
||||||
|
|
||||||
|
// Contrast and brightness effects.
|
||||||
|
this._magShaderEffects = new MagShaderEffects(this._uiGroupClone);
|
||||||
|
this._magShaderEffects.setColorSaturation(this._colorSaturation);
|
||||||
|
this._magShaderEffects.setInvertLightness(this._invertLightness);
|
||||||
|
this._magShaderEffects.setBrightness(this._brightness);
|
||||||
|
this._magShaderEffects.setContrast(this._contrast);
|
||||||
},
|
},
|
||||||
|
|
||||||
_destroyActors: function() {
|
_destroyActors: function() {
|
||||||
@ -925,6 +1120,8 @@ const ZoomRegion = new Lang.Class({
|
|||||||
if (this._crossHairs)
|
if (this._crossHairs)
|
||||||
this._crossHairs.removeFromParent(this._crossHairsActor);
|
this._crossHairs.removeFromParent(this._crossHairsActor);
|
||||||
|
|
||||||
|
this._magShaderEffects.destroyEffects();
|
||||||
|
this._magShaderEffects = null;
|
||||||
this._magView.destroy();
|
this._magView.destroy();
|
||||||
this._magView = null;
|
this._magView = null;
|
||||||
this._background = null;
|
this._background = null;
|
||||||
@ -1128,7 +1325,7 @@ const ZoomRegion = new Lang.Class({
|
|||||||
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
|
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
|
||||||
|
|
||||||
let [x, y] = this._screenToViewPort(0, 0);
|
let [x, y] = this._screenToViewPort(0, 0);
|
||||||
this._uiGroupClone.set_position(x, y);
|
this._uiGroupClone.set_position(Math.round(x), Math.round(y));
|
||||||
|
|
||||||
this._updateMousePosition();
|
this._updateMousePosition();
|
||||||
},
|
},
|
||||||
@ -1156,7 +1353,6 @@ const ZoomRegion = new Lang.Class({
|
|||||||
if (!this.isActive())
|
if (!this.isActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
|
||||||
this._background.set_size(global.screen_width, global.screen_height);
|
this._background.set_size(global.screen_width, global.screen_height);
|
||||||
|
|
||||||
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
||||||
@ -1180,15 +1376,15 @@ const Crosshairs = new Lang.Class({
|
|||||||
let groupWidth = global.screen_width * 3;
|
let groupWidth = global.screen_width * 3;
|
||||||
let groupHeight = global.screen_height * 3;
|
let groupHeight = global.screen_height * 3;
|
||||||
|
|
||||||
this._actor = new Clutter.Group({
|
this._actor = new Clutter.Actor({
|
||||||
clip_to_allocation: false,
|
clip_to_allocation: false,
|
||||||
width: groupWidth,
|
width: groupWidth,
|
||||||
height: groupHeight
|
height: groupHeight
|
||||||
});
|
});
|
||||||
this._horizLeftHair = new Clutter.Rectangle();
|
this._horizLeftHair = new Clutter.Actor();
|
||||||
this._horizRightHair = new Clutter.Rectangle();
|
this._horizRightHair = new Clutter.Actor();
|
||||||
this._vertTopHair = new Clutter.Rectangle();
|
this._vertTopHair = new Clutter.Actor();
|
||||||
this._vertBottomHair = new Clutter.Rectangle();
|
this._vertBottomHair = new Clutter.Actor();
|
||||||
this._actor.add_actor(this._horizLeftHair);
|
this._actor.add_actor(this._horizLeftHair);
|
||||||
this._actor.add_actor(this._horizRightHair);
|
this._actor.add_actor(this._horizRightHair);
|
||||||
this._actor.add_actor(this._vertTopHair);
|
this._actor.add_actor(this._vertTopHair);
|
||||||
@ -1228,10 +1424,7 @@ const Crosshairs = new Lang.Class({
|
|||||||
crosshairsActor = new Clutter.Clone({ source: this._actor });
|
crosshairsActor = new Clutter.Clone({ source: this._actor });
|
||||||
this._clones.push(crosshairsActor);
|
this._clones.push(crosshairsActor);
|
||||||
}
|
}
|
||||||
if (this._actor.visible)
|
crosshairsActor.visible = this._actor.visible;
|
||||||
crosshairsActor.show();
|
|
||||||
else
|
|
||||||
crosshairsActor.hide();
|
|
||||||
|
|
||||||
container.add_actor(crosshairsActor);
|
container.add_actor(crosshairsActor);
|
||||||
container.raise_child(magnifiedMouse, crosshairsActor);
|
container.raise_child(magnifiedMouse, crosshairsActor);
|
||||||
@ -1262,10 +1455,10 @@ const Crosshairs = new Lang.Class({
|
|||||||
* @clutterColor: The color as a Clutter.Color.
|
* @clutterColor: The color as a Clutter.Color.
|
||||||
*/
|
*/
|
||||||
setColor: function(clutterColor) {
|
setColor: function(clutterColor) {
|
||||||
this._horizLeftHair.set_color(clutterColor);
|
this._horizLeftHair.background_color = clutterColor;
|
||||||
this._horizRightHair.set_color(clutterColor);
|
this._horizRightHair.background_color = clutterColor;
|
||||||
this._vertTopHair.set_color(clutterColor);
|
this._vertTopHair.background_color = clutterColor;
|
||||||
this._vertBottomHair.set_color(clutterColor);
|
this._vertBottomHair.background_color = clutterColor;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1274,9 +1467,7 @@ const Crosshairs = new Lang.Class({
|
|||||||
* @color: The color as a Clutter.Color.
|
* @color: The color as a Clutter.Color.
|
||||||
*/
|
*/
|
||||||
getColor: function() {
|
getColor: function() {
|
||||||
let clutterColor = new Clutter.Color();
|
return this._horizLeftHair.get_color();
|
||||||
this._horizLeftHair.get_color(clutterColor);
|
|
||||||
return clutterColor;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1436,3 +1627,144 @@ const Crosshairs = new Lang.Class({
|
|||||||
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
|
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const MagShaderEffects = new Lang.Class({
|
||||||
|
Name: 'MagShaderEffects',
|
||||||
|
|
||||||
|
_init: function(uiGroupClone) {
|
||||||
|
this._inverse = new Shell.InvertLightnessEffect();
|
||||||
|
this._brightnessContrast = new Clutter.BrightnessContrastEffect();
|
||||||
|
this._colorDesaturation = new Clutter.DesaturateEffect();
|
||||||
|
this._inverse.set_enabled(false);
|
||||||
|
this._brightnessContrast.set_enabled(false);
|
||||||
|
|
||||||
|
this._magView = uiGroupClone;
|
||||||
|
this._magView.add_effect(this._inverse);
|
||||||
|
this._magView.add_effect(this._brightnessContrast);
|
||||||
|
this._magView.add_effect(this._colorDesaturation);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* destroyEffects:
|
||||||
|
* Remove contrast and brightness effects from the magnified view, and
|
||||||
|
* lose the reference to the actor they were applied to. Don't use this
|
||||||
|
* object after calling this.
|
||||||
|
*/
|
||||||
|
destroyEffects: function() {
|
||||||
|
this._magView.clear_effects();
|
||||||
|
this._colorDesaturation = null;
|
||||||
|
this._brightnessContrast = null;
|
||||||
|
this._inverse = null;
|
||||||
|
this._magView = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setInvertLightness:
|
||||||
|
* Enable/disable invert lightness effect.
|
||||||
|
* @invertFlag: Enabled flag.
|
||||||
|
*/
|
||||||
|
setInvertLightness: function(invertFlag) {
|
||||||
|
this._inverse.set_enabled(invertFlag);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getInvertLightness:
|
||||||
|
* Report whether the inversion effect is enabled.
|
||||||
|
* @return: Boolean.
|
||||||
|
*/
|
||||||
|
getInvertLightness: function() {
|
||||||
|
return this._inverse.get_enabled();
|
||||||
|
},
|
||||||
|
|
||||||
|
setColorSaturation: function(factor) {
|
||||||
|
this._colorDesaturation.set_factor(1.0 - factor);
|
||||||
|
},
|
||||||
|
|
||||||
|
getColorSaturation: function() {
|
||||||
|
return 1.0 - this._colorDesaturation.get_factor();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setBrightness:
|
||||||
|
* Set the brightness of the magnified view.
|
||||||
|
* @brightness: Object containing the brightness for the red, green,
|
||||||
|
* and blue channels. Values of 0.0 represent "standard"
|
||||||
|
* brightness (no change), whereas values less or greater than
|
||||||
|
* 0.0 indicate decreased or incresaed brightness,
|
||||||
|
* respectively.
|
||||||
|
*/
|
||||||
|
setBrightness: function(brightness) {
|
||||||
|
let bRed = brightness.r;
|
||||||
|
let bGreen = brightness.g;
|
||||||
|
let bBlue = brightness.b;
|
||||||
|
this._brightnessContrast.set_brightness_full(bRed, bGreen, bBlue);
|
||||||
|
|
||||||
|
// Enable the effect if the brightness OR contrast change are such that
|
||||||
|
// it modifies the brightness and/or contrast.
|
||||||
|
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||||
|
this._brightnessContrast.set_enabled(
|
||||||
|
(bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
|
||||||
|
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBrightness:
|
||||||
|
* Retrieve current brightness of the magnified view.
|
||||||
|
* @return: Object containing the brightness for the red, green,
|
||||||
|
* and blue channels. Values of 0.0 represent "standard"
|
||||||
|
* brightness (no change), whereas values less or greater than
|
||||||
|
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||||
|
*/
|
||||||
|
getBrightness: function() {
|
||||||
|
let result = {};
|
||||||
|
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||||
|
result.r = bRed;
|
||||||
|
result.g = bGreen;
|
||||||
|
result.b = bBlue;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the contrast of the magnified view.
|
||||||
|
* @contrast: Object containing the contrast for the red, green,
|
||||||
|
* and blue channels. Values of 0.0 represent "standard"
|
||||||
|
* contrast (no change), whereas values less or greater than
|
||||||
|
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||||
|
*/
|
||||||
|
setContrast: function(contrast) {
|
||||||
|
let cRed = contrast.r;
|
||||||
|
let cGreen = contrast.g;
|
||||||
|
let cBlue = contrast.b;
|
||||||
|
|
||||||
|
this._brightnessContrast.set_contrast_full(cRed, cGreen, cBlue);
|
||||||
|
|
||||||
|
// Enable the effect if the contrast OR brightness change are such that
|
||||||
|
// it modifies the brightness and/or contrast.
|
||||||
|
// should be able to use Clutter.color_equal(), but that complains of
|
||||||
|
// a null first argument.
|
||||||
|
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||||
|
this._brightnessContrast.set_enabled(
|
||||||
|
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE ||
|
||||||
|
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve current contrast of the magnified view.
|
||||||
|
* @return: Object containing the contrast for the red, green,
|
||||||
|
* and blue channels. Values of 0.0 represent "standard"
|
||||||
|
* contrast (no change), whereas values less or greater than
|
||||||
|
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||||
|
*/
|
||||||
|
getContrast: function() {
|
||||||
|
let resutl = {};
|
||||||
|
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||||
|
result.r = cRed;
|
||||||
|
result.g = cGreen;
|
||||||
|
result.b = cBlue;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
280
js/ui/main.js
@ -10,158 +10,83 @@ const Meta = imports.gi.Meta;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const AutomountManager = imports.ui.automountManager;
|
const Components = imports.ui.components;
|
||||||
const AutorunManager = imports.ui.autorunManager;
|
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
|
||||||
const KeyringPrompt = imports.ui.keyringPrompt;
|
|
||||||
const Environment = imports.ui.environment;
|
const Environment = imports.ui.environment;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
|
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||||
const Keyboard = imports.ui.keyboard;
|
const Keyboard = imports.ui.keyboard;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const PlaceDisplay = imports.ui.placeDisplay;
|
|
||||||
const RunDialog = imports.ui.runDialog;
|
const RunDialog = imports.ui.runDialog;
|
||||||
const Layout = imports.ui.layout;
|
const Layout = imports.ui.layout;
|
||||||
const LookingGlass = imports.ui.lookingGlass;
|
const LookingGlass = imports.ui.lookingGlass;
|
||||||
const NetworkAgent = imports.ui.networkAgent;
|
|
||||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||||
|
const ScreenShield = imports.ui.screenShield;
|
||||||
const Scripting = imports.ui.scripting;
|
const Scripting = imports.ui.scripting;
|
||||||
|
const SessionMode = imports.ui.sessionMode;
|
||||||
const ShellDBus = imports.ui.shellDBus;
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
const TelepathyClient = imports.ui.telepathyClient;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
const UnlockDialog = imports.ui.unlockDialog;
|
||||||
const WindowManager = imports.ui.windowManager;
|
const WindowManager = imports.ui.windowManager;
|
||||||
const Magnifier = imports.ui.magnifier;
|
const Magnifier = imports.ui.magnifier;
|
||||||
const XdndHandler = imports.ui.xdndHandler;
|
const XdndHandler = imports.ui.xdndHandler;
|
||||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
|
||||||
|
|
||||||
let automountManager = null;
|
let componentManager = null;
|
||||||
let autorunManager = null;
|
|
||||||
let panel = null;
|
let panel = null;
|
||||||
let hotCorners = [];
|
|
||||||
let placesManager = null;
|
|
||||||
let overview = null;
|
let overview = null;
|
||||||
let runDialog = null;
|
let runDialog = null;
|
||||||
let lookingGlass = null;
|
let lookingGlass = null;
|
||||||
let wm = null;
|
let wm = null;
|
||||||
let messageTray = null;
|
let messageTray = null;
|
||||||
|
let screenShield = null;
|
||||||
let notificationDaemon = null;
|
let notificationDaemon = null;
|
||||||
let windowAttentionHandler = null;
|
let windowAttentionHandler = null;
|
||||||
let telepathyClient = null;
|
|
||||||
let ctrlAltTabManager = null;
|
let ctrlAltTabManager = null;
|
||||||
let recorder = null;
|
let sessionMode = null;
|
||||||
let shellDBusService = null;
|
let shellDBusService = null;
|
||||||
|
let shellMountOpDBusService = null;
|
||||||
|
let screenSaverDBus = null;
|
||||||
let modalCount = 0;
|
let modalCount = 0;
|
||||||
let modalActorFocusStack = [];
|
let modalActorFocusStack = [];
|
||||||
let uiGroup = null;
|
let uiGroup = null;
|
||||||
let magnifier = null;
|
let magnifier = null;
|
||||||
let xdndHandler = null;
|
let xdndHandler = null;
|
||||||
let statusIconDispatcher = null;
|
|
||||||
let keyboard = null;
|
let keyboard = null;
|
||||||
let layoutManager = null;
|
let layoutManager = null;
|
||||||
let networkAgent = null;
|
|
||||||
let _errorLogStack = [];
|
|
||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
let _gdmCssStylesheet = null;
|
|
||||||
let _overridesSettings = null;
|
let _overridesSettings = null;
|
||||||
|
|
||||||
let background = null;
|
let background = null;
|
||||||
|
|
||||||
function _createUserSession() {
|
function _sessionUpdated() {
|
||||||
// Load the calendar server. Note that we are careful about
|
Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
|
||||||
// not loading any events until the user presses the clock
|
if (sessionMode.isGreeter)
|
||||||
global.launch_calendar_server();
|
screenShield.showDialog();
|
||||||
|
|
||||||
placesManager = new PlaceDisplay.PlacesManager();
|
|
||||||
telepathyClient = new TelepathyClient.Client();
|
|
||||||
automountManager = new AutomountManager.AutomountManager();
|
|
||||||
autorunManager = new AutorunManager.AutorunManager();
|
|
||||||
networkAgent = new NetworkAgent.NetworkAgent();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _createGDMSession() {
|
|
||||||
// We do this this here instead of at the top to prevent GDM
|
|
||||||
// related code from getting loaded in normal user sessions
|
|
||||||
const LoginDialog = imports.gdm.loginDialog;
|
|
||||||
|
|
||||||
let loginDialog = new LoginDialog.LoginDialog();
|
|
||||||
loginDialog.connect('loaded', function() {
|
|
||||||
loginDialog.open();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initRecorder() {
|
|
||||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
|
||||||
|
|
||||||
global.screen.connect('toggle-recording', function() {
|
|
||||||
if (recorder == null) {
|
|
||||||
recorder = new Shell.Recorder({ stage: global.stage });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recorder.is_recording()) {
|
|
||||||
recorder.pause();
|
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
|
||||||
} else {
|
|
||||||
// read the parameters from GSettings always in case they have changed
|
|
||||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
|
||||||
/* Translators: this is a filename used for screencast recording */
|
|
||||||
// xgettext:no-c-format
|
|
||||||
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
|
|
||||||
let pipeline = recorderSettings.get_string('pipeline');
|
|
||||||
|
|
||||||
if (!pipeline.match(/^\s*$/))
|
|
||||||
recorder.set_pipeline(pipeline);
|
|
||||||
else
|
|
||||||
recorder.set_pipeline(null);
|
|
||||||
|
|
||||||
Meta.disable_unredirect_for_screen(global.screen);
|
|
||||||
recorder.record();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initUserSession() {
|
|
||||||
_initRecorder();
|
|
||||||
|
|
||||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
|
||||||
|
|
||||||
ExtensionSystem.init();
|
|
||||||
ExtensionSystem.loadExtensions();
|
|
||||||
|
|
||||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
|
||||||
getRunDialog().open();
|
|
||||||
});
|
|
||||||
|
|
||||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
|
||||||
overview.toggle();
|
|
||||||
});
|
|
||||||
|
|
||||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
// Monkey patch utility functions into the global proxy;
|
// These are here so we don't break compatibility.
|
||||||
// This is easier and faster than indirecting down into global
|
global.logError = window.log;
|
||||||
// if we want to call back up into JS.
|
global.log = window.log;
|
||||||
global.logError = _logError;
|
|
||||||
global.log = _logDebug;
|
|
||||||
|
|
||||||
// Chain up async errors reported from C
|
// Chain up async errors reported from C
|
||||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||||
|
|
||||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||||
|
|
||||||
|
sessionMode = new SessionMode.SessionMode();
|
||||||
shellDBusService = new ShellDBus.GnomeShell();
|
shellDBusService = new ShellDBus.GnomeShell();
|
||||||
|
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||||
|
|
||||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||||
// also initialize ShellAppSystem first. ShellAppSystem
|
// also initialize ShellAppSystem first. ShellAppSystem
|
||||||
@ -183,7 +108,6 @@ function start() {
|
|||||||
global.stage.no_clear_hint = true;
|
global.stage.no_clear_hint = true;
|
||||||
|
|
||||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||||
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
|
|
||||||
loadTheme();
|
loadTheme();
|
||||||
|
|
||||||
// Set up stage hierarchy to group all UI actors under one container.
|
// Set up stage hierarchy to group all UI actors under one container.
|
||||||
@ -194,9 +118,16 @@ function start() {
|
|||||||
for (let i = 0; i < children.length; i++)
|
for (let i = 0; i < children.length; i++)
|
||||||
children[i].allocate_preferred_size(flags);
|
children[i].allocate_preferred_size(flags);
|
||||||
});
|
});
|
||||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
uiGroup.connect('get-preferred-width',
|
||||||
coordinate: Clutter.BindCoordinate.SIZE });
|
function(actor, forHeight, alloc) {
|
||||||
uiGroup.add_constraint(constraint);
|
let width = global.stage.width;
|
||||||
|
[alloc.min_size, alloc.natural_size] = [width, width];
|
||||||
|
});
|
||||||
|
uiGroup.connect('get-preferred-height',
|
||||||
|
function(actor, forWidth, alloc) {
|
||||||
|
let height = global.stage.height;
|
||||||
|
[alloc.min_size, alloc.natural_size] = [height, height];
|
||||||
|
});
|
||||||
global.window_group.reparent(uiGroup);
|
global.window_group.reparent(uiGroup);
|
||||||
global.overlay_group.reparent(uiGroup);
|
global.overlay_group.reparent(uiGroup);
|
||||||
global.stage.add_actor(uiGroup);
|
global.stage.add_actor(uiGroup);
|
||||||
@ -204,47 +135,40 @@ function start() {
|
|||||||
layoutManager = new Layout.LayoutManager();
|
layoutManager = new Layout.LayoutManager();
|
||||||
xdndHandler = new XdndHandler.XdndHandler();
|
xdndHandler = new XdndHandler.XdndHandler();
|
||||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||||
// This overview object is just a stub for non-user sessions
|
overview = new Overview.Overview();
|
||||||
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
|
|
||||||
magnifier = new Magnifier.Magnifier();
|
magnifier = new Magnifier.Magnifier();
|
||||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
if (UnlockDialog.isSupported())
|
||||||
|
screenShield = new ScreenShield.ScreenShield();
|
||||||
|
else
|
||||||
|
screenShield = new ScreenShield.ScreenShieldFallback();
|
||||||
panel = new Panel.Panel();
|
panel = new Panel.Panel();
|
||||||
wm = new WindowManager.WindowManager();
|
wm = new WindowManager.WindowManager();
|
||||||
messageTray = new MessageTray.MessageTray();
|
messageTray = new MessageTray.MessageTray();
|
||||||
keyboard = new Keyboard.Keyboard();
|
keyboard = new Keyboard.Keyboard();
|
||||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||||
|
componentManager = new Components.ComponentManager();
|
||||||
if (global.session_type == Shell.SessionType.USER)
|
|
||||||
_createUserSession();
|
|
||||||
else if (global.session_type == Shell.SessionType.GDM)
|
|
||||||
_createGDMSession();
|
|
||||||
|
|
||||||
panel.startStatusArea();
|
|
||||||
|
|
||||||
layoutManager.init();
|
layoutManager.init();
|
||||||
keyboard.init();
|
keyboard.init();
|
||||||
overview.init();
|
overview.init();
|
||||||
|
|
||||||
if (global.session_type == Shell.SessionType.USER)
|
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||||
_initUserSession();
|
false, -1, 1);
|
||||||
statusIconDispatcher.start(messageTray.actor);
|
Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
|
||||||
|
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||||
|
|
||||||
|
sessionMode.connect('update', _sessionUpdated);
|
||||||
|
_sessionUpdated();
|
||||||
|
|
||||||
// Provide the bus object for gnome-session to
|
// Provide the bus object for gnome-session to
|
||||||
// initiate logouts.
|
// initiate logouts.
|
||||||
EndSessionDialog.init();
|
EndSessionDialog.init();
|
||||||
|
|
||||||
// Attempt to become a PolicyKit authentication agent
|
|
||||||
PolkitAuthenticationAgent.init()
|
|
||||||
|
|
||||||
// Become a prompter for gnome keyring
|
|
||||||
KeyringPrompt.init();
|
|
||||||
|
|
||||||
_startDate = new Date();
|
_startDate = new Date();
|
||||||
|
|
||||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||||
|
|
||||||
_log('info', 'loaded at ' + _startDate);
|
|
||||||
log('GNOME Shell started at ' + _startDate);
|
log('GNOME Shell started at ' + _startDate);
|
||||||
|
|
||||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||||
@ -264,6 +188,9 @@ function start() {
|
|||||||
global.screen.connect('restacked', _windowsRestacked);
|
global.screen.connect('restacked', _windowsRestacked);
|
||||||
|
|
||||||
_nWorkspacesChanged();
|
_nWorkspacesChanged();
|
||||||
|
|
||||||
|
ExtensionDownloader.init();
|
||||||
|
ExtensionSystem.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _workspaces = [];
|
let _workspaces = [];
|
||||||
@ -487,9 +414,6 @@ function loadTheme() {
|
|||||||
|
|
||||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||||
|
|
||||||
if (global.session_type == Shell.SessionType.GDM)
|
|
||||||
theme.load_stylesheet(_gdmCssStylesheet);
|
|
||||||
|
|
||||||
if (previousTheme) {
|
if (previousTheme) {
|
||||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||||
|
|
||||||
@ -530,59 +454,6 @@ function notifyError(msg, details) {
|
|||||||
notify(msg, details);
|
notify(msg, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* _log:
|
|
||||||
* @category: string message type ('info', 'error')
|
|
||||||
* @msg: A message string
|
|
||||||
* ...: Any further arguments are converted into JSON notation,
|
|
||||||
* and appended to the log message, separated by spaces.
|
|
||||||
*
|
|
||||||
* Log a message into the LookingGlass error
|
|
||||||
* stream. This is primarily intended for use by the
|
|
||||||
* extension system as well as debugging.
|
|
||||||
*/
|
|
||||||
function _log(category, msg) {
|
|
||||||
let text = msg;
|
|
||||||
if (arguments.length > 2) {
|
|
||||||
text += ': ';
|
|
||||||
for (let i = 2; i < arguments.length; i++) {
|
|
||||||
text += JSON.stringify(arguments[i]);
|
|
||||||
if (i < arguments.length - 1)
|
|
||||||
text += ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_errorLogStack.push({timestamp: new Date().getTime(),
|
|
||||||
category: category,
|
|
||||||
message: text });
|
|
||||||
}
|
|
||||||
|
|
||||||
function _logError(msg) {
|
|
||||||
return _log('error', msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _logDebug(msg) {
|
|
||||||
return _log('debug', msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used by the error display in lookingGlass.js
|
|
||||||
function _getAndClearErrorStack() {
|
|
||||||
let errors = _errorLogStack;
|
|
||||||
_errorLogStack = [];
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function logStackTrace(msg) {
|
|
||||||
try {
|
|
||||||
throw new Error();
|
|
||||||
} catch (e) {
|
|
||||||
// e.stack must have at least two lines, with the first being
|
|
||||||
// logStackTrace() (which we strip off), and the second being
|
|
||||||
// our caller.
|
|
||||||
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
|
|
||||||
log(msg ? (msg + '\n' + trace) : trace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||||
return win.get_workspace() == workspaceIndex ||
|
return win.get_workspace() == workspaceIndex ||
|
||||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||||
@ -605,6 +476,11 @@ function _globalKeyPressHandler(actor, event) {
|
|||||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
if (event.type() != Clutter.EventType.KEY_PRESS)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!sessionMode.allowKeybindingsWhenModal) {
|
||||||
|
if (modalCount > (overview.visible ? 1 : 0))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
let keyCode = event.get_key_code();
|
let keyCode = event.get_key_code();
|
||||||
let ignoredModifiers = global.display.get_ignored_modifier_mask();
|
let ignoredModifiers = global.display.get_ignored_modifier_mask();
|
||||||
@ -613,48 +489,49 @@ function _globalKeyPressHandler(actor, event) {
|
|||||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||||
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
||||||
|
|
||||||
// Other bindings are only available to the user session when the overview is up and
|
|
||||||
// no modal dialog is present.
|
|
||||||
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// This isn't a Meta.KeyBindingAction yet
|
|
||||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
|
||||||
overview.hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
||||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
|
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
|
||||||
modifierState);
|
modifierState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// None of the other bindings are relevant outside of the user's session
|
|
||||||
if (global.session_type != Shell.SessionType.USER)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
// 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.
|
// but that could be considered confusing; we also disable them in the main view.
|
||||||
//
|
//
|
||||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||||
|
// if (!sessionMode.hasWorkspaces)
|
||||||
|
// return false;
|
||||||
|
//
|
||||||
// wm.actionMoveWorkspaceLeft();
|
// wm.actionMoveWorkspaceLeft();
|
||||||
// return true;
|
// return true;
|
||||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||||
|
// if (!sessionMode.hasWorkspaces)
|
||||||
|
// return false;
|
||||||
|
//
|
||||||
// wm.actionMoveWorkspaceRight();
|
// wm.actionMoveWorkspaceRight();
|
||||||
// return true;
|
// return true;
|
||||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
case Meta.KeyBindingAction.WORKSPACE_UP:
|
||||||
wm.actionMoveWorkspaceUp();
|
if (!sessionMode.hasWorkspaces)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||||
return true;
|
return true;
|
||||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
||||||
wm.actionMoveWorkspaceDown();
|
if (!sessionMode.hasWorkspaces)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||||
return true;
|
return true;
|
||||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
||||||
case Meta.KeyBindingAction.COMMAND_2:
|
case Meta.KeyBindingAction.COMMAND_2:
|
||||||
getRunDialog().open();
|
if (!sessionMode.hasRunDialog)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
openRunDialog();
|
||||||
return true;
|
return true;
|
||||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||||
|
case Meta.KeyBindingAction.OVERLAY_KEY:
|
||||||
overview.hide();
|
overview.hide();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -670,6 +547,10 @@ function _findModal(actor) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInModalStack(actor) {
|
||||||
|
return _findModal(actor) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pushModal:
|
* pushModal:
|
||||||
* @actor: #ClutterActor which will be given keyboard focus
|
* @actor: #ClutterActor which will be given keyboard focus
|
||||||
@ -702,6 +583,7 @@ function pushModal(actor, timestamp, options) {
|
|||||||
log('pushModal: invocation of begin_modal failed');
|
log('pushModal: invocation of begin_modal failed');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
||||||
@ -710,7 +592,7 @@ function pushModal(actor, timestamp, options) {
|
|||||||
let actorDestroyId = actor.connect('destroy', function() {
|
let actorDestroyId = actor.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
modalActorFocusStack.splice(index, 1);
|
popModal(actor);
|
||||||
});
|
});
|
||||||
let curFocus = global.stage.get_key_focus();
|
let curFocus = global.stage.get_key_focus();
|
||||||
let curFocusDestroyId;
|
let curFocusDestroyId;
|
||||||
@ -782,6 +664,7 @@ function popModal(actor, timestamp) {
|
|||||||
|
|
||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLookingGlass() {
|
function createLookingGlass() {
|
||||||
@ -791,11 +674,11 @@ function createLookingGlass() {
|
|||||||
return lookingGlass;
|
return lookingGlass;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRunDialog() {
|
function openRunDialog() {
|
||||||
if (runDialog == null) {
|
if (runDialog == null) {
|
||||||
runDialog = new RunDialog.RunDialog();
|
runDialog = new RunDialog.RunDialog();
|
||||||
}
|
}
|
||||||
return runDialog;
|
runDialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -924,7 +807,8 @@ function initializeDeferredWork(actor, callback, props) {
|
|||||||
function queueDeferredWork(workId) {
|
function queueDeferredWork(workId) {
|
||||||
let data = _deferredWorkData[workId];
|
let data = _deferredWorkData[workId];
|
||||||
if (!data) {
|
if (!data) {
|
||||||
global.logError('invalid work id ', workId);
|
let message = 'Invalid work id %d'.format(workId);
|
||||||
|
logError(new Error(message), message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_deferredWorkQueue.indexOf(workId) < 0)
|
if (_deferredWorkQueue.indexOf(workId) < 0)
|
||||||
|
1486
js/ui/messageTray.js
@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
|
|||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const Lightbox = imports.ui.lightbox;
|
const Lightbox = imports.ui.lightbox;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
@ -35,17 +36,20 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(params) {
|
_init: function(params) {
|
||||||
params = Params.parse(params, { shellReactive: false,
|
params = Params.parse(params, { shellReactive: false,
|
||||||
styleClass: null });
|
styleClass: null,
|
||||||
|
parentActor: Main.uiGroup,
|
||||||
|
shouldFadeIn: true });
|
||||||
|
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this._hasModal = false;
|
this._hasModal = false;
|
||||||
this._shellReactive = params.shellReactive;
|
this._shellReactive = params.shellReactive;
|
||||||
|
this._shouldFadeIn = params.shouldFadeIn;
|
||||||
|
|
||||||
this._group = new St.Widget({ visible: false,
|
this._group = new St.Widget({ visible: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
accessible_role: Atk.Role.DIALOG });
|
accessible_role: Atk.Role.DIALOG });
|
||||||
Main.uiGroup.add_actor(this._group);
|
params.parentActor.add_actor(this._group);
|
||||||
|
|
||||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||||
coordinate: Clutter.BindCoordinate.ALL });
|
coordinate: Clutter.BindCoordinate.ALL });
|
||||||
@ -54,15 +58,17 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||||
|
|
||||||
this._actionKeys = {};
|
this._actionKeys = {};
|
||||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||||
|
|
||||||
this._backgroundBin = new St.Bin();
|
this._backgroundBin = new St.Bin();
|
||||||
|
this._monitorConstraint = new Layout.MonitorConstraint();
|
||||||
|
this._backgroundBin.add_constraint(this._monitorConstraint);
|
||||||
this._group.add_actor(this._backgroundBin);
|
this._group.add_actor(this._backgroundBin);
|
||||||
|
|
||||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
if (params.styleClass != null) {
|
if (params.styleClass != null) {
|
||||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._shellReactive) {
|
if (!this._shellReactive) {
|
||||||
@ -75,14 +81,14 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||||
stack.add_actor(this._eventBlocker);
|
stack.add_actor(this._eventBlocker);
|
||||||
stack.add_actor(this._dialogLayout);
|
stack.add_actor(this.dialogLayout);
|
||||||
} else {
|
} else {
|
||||||
this._backgroundBin.child = this._dialogLayout;
|
this._backgroundBin.child = this.dialogLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||||
this._dialogLayout.add(this.contentLayout,
|
this.dialogLayout.add(this.contentLayout,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: true,
|
y_fill: true,
|
||||||
x_align: St.Align.MIDDLE,
|
x_align: St.Align.MIDDLE,
|
||||||
@ -91,13 +97,13 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||||
visible: false,
|
visible: false,
|
||||||
vertical: false });
|
vertical: false });
|
||||||
this._dialogLayout.add(this._buttonLayout,
|
this.dialogLayout.add(this._buttonLayout,
|
||||||
{ expand: true,
|
{ expand: true,
|
||||||
x_align: St.Align.MIDDLE,
|
x_align: St.Align.MIDDLE,
|
||||||
y_align: St.Align.END });
|
y_align: St.Align.END });
|
||||||
|
|
||||||
global.focus_manager.add_group(this._dialogLayout);
|
global.focus_manager.add_group(this.dialogLayout);
|
||||||
this._initialKeyFocus = this._dialogLayout;
|
this._initialKeyFocus = this.dialogLayout;
|
||||||
this._initialKeyFocusDestroyId = 0;
|
this._initialKeyFocusDestroyId = 0;
|
||||||
this._savedKeyFocus = null;
|
this._savedKeyFocus = null;
|
||||||
},
|
},
|
||||||
@ -106,6 +112,10 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._group.destroy();
|
this._group.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setActionKey: function(key, action) {
|
||||||
|
this._actionKeys[key] = action;
|
||||||
|
},
|
||||||
|
|
||||||
setButtons: function(buttons) {
|
setButtons: function(buttons) {
|
||||||
let hadChildren = this._buttonLayout.get_children() > 0;
|
let hadChildren = this._buttonLayout.get_children() > 0;
|
||||||
|
|
||||||
@ -119,11 +129,17 @@ const ModalDialog = new Lang.Class({
|
|||||||
let label = buttonInfo['label'];
|
let label = buttonInfo['label'];
|
||||||
let action = buttonInfo['action'];
|
let action = buttonInfo['action'];
|
||||||
let key = buttonInfo['key'];
|
let key = buttonInfo['key'];
|
||||||
|
let isDefault = buttonInfo['default'];
|
||||||
|
|
||||||
|
if (isDefault && !key)
|
||||||
|
key = Clutter.KEY_Return;
|
||||||
|
|
||||||
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
label: label });
|
label: label });
|
||||||
|
if (isDefault)
|
||||||
|
buttonInfo.button.add_style_pseudo_class('default');
|
||||||
|
|
||||||
let x_alignment;
|
let x_alignment;
|
||||||
if (buttons.length == 1)
|
if (buttons.length == 1)
|
||||||
@ -167,12 +183,16 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
_onKeyReleaseEvent: function(object, event) {
|
||||||
let symbol = keyPressEvent.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
let action = this._actionKeys[symbol];
|
let action = this._actionKeys[symbol];
|
||||||
|
|
||||||
if (action)
|
if (action) {
|
||||||
action();
|
action();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onGroupDestroy: function() {
|
_onGroupDestroy: function() {
|
||||||
@ -180,21 +200,18 @@ const ModalDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_fadeOpen: function() {
|
_fadeOpen: function() {
|
||||||
let monitor = Main.layoutManager.focusMonitor;
|
this._monitorConstraint.index = global.screen.get_current_monitor();
|
||||||
|
|
||||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
|
||||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
|
||||||
|
|
||||||
this.state = State.OPENING;
|
this.state = State.OPENING;
|
||||||
|
|
||||||
this._dialogLayout.opacity = 255;
|
this.dialogLayout.opacity = 255;
|
||||||
if (this._lightbox)
|
if (this._lightbox)
|
||||||
this._lightbox.show();
|
this._lightbox.show();
|
||||||
this._group.opacity = 0;
|
this._group.opacity = 0;
|
||||||
this._group.show();
|
this._group.show();
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
time: OPEN_AND_CLOSE_TIME,
|
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this,
|
onComplete: Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
@ -211,7 +228,7 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._initialKeyFocus = actor;
|
this._initialKeyFocus = actor;
|
||||||
|
|
||||||
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
|
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
|
||||||
this._initialKeyFocus = this._dialogLayout;
|
this._initialKeyFocus = this.dialogLayout;
|
||||||
this._initialKeyFocusDestroyId = 0;
|
this._initialKeyFocusDestroyId = 0;
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@ -304,7 +321,7 @@ const ModalDialog = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.popModal(timestamp);
|
this.popModal(timestamp);
|
||||||
Tweener.addTween(this._dialogLayout,
|
Tweener.addTween(this.dialogLayout,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: FADE_OUT_DIALOG_TIME,
|
time: FADE_OUT_DIALOG_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
@ -87,6 +88,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({
|
const NotificationDaemon = new Lang.Class({
|
||||||
Name: 'NotificationDaemon',
|
Name: 'NotificationDaemon',
|
||||||
|
|
||||||
@ -99,18 +115,19 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
this._notifications = {};
|
this._notifications = {};
|
||||||
this._busProxy = new Bus();
|
this._busProxy = new Bus();
|
||||||
|
|
||||||
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
this._trayManager = new Shell.TrayManager();
|
||||||
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||||
|
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||||
|
|
||||||
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
Main.overview.connect('hidden',
|
Main.overview.connect('hidden',
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
|
|
||||||
|
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_iconForNotificationData: function(icon, hints, size) {
|
_iconForNotificationData: function(icon, hints) {
|
||||||
let textureCache = St.TextureCache.get_default();
|
|
||||||
|
|
||||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
// 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
|
// 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
|
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||||
@ -121,20 +138,18 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
// a large image.
|
// a large image.
|
||||||
if (icon) {
|
if (icon) {
|
||||||
if (icon.substr(0, 7) == 'file://')
|
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] == '/') {
|
else if (icon[0] == '/') {
|
||||||
let uri = GLib.filename_to_uri(icon, null);
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||||
return textureCache.load_uri_async(uri, size, size);
|
|
||||||
} else
|
} else
|
||||||
return new St.Icon({ icon_name: icon,
|
return new Gio.ThemedIcon({ name: icon });
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: size });
|
|
||||||
} else if (hints['image-data']) {
|
} else if (hints['image-data']) {
|
||||||
let [width, height, rowStride, hasAlpha,
|
let [width, height, rowStride, hasAlpha,
|
||||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||||
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
|
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||||
|
bitsPerSample, width, height, rowStride);
|
||||||
} else if (hints['image-path']) {
|
} else if (hints['image-path']) {
|
||||||
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||||
} else {
|
} else {
|
||||||
let stockIcon;
|
let stockIcon;
|
||||||
switch (hints.urgency) {
|
switch (hints.urgency) {
|
||||||
@ -146,9 +161,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
stockIcon = 'gtk-dialog-error';
|
stockIcon = 'gtk-dialog-error';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new St.Icon({ icon_name: stockIcon,
|
return new Gio.ThemedIcon({ name: stockIcon });
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: size });
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -221,12 +234,19 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
|
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
|
||||||
let id;
|
let id;
|
||||||
|
|
||||||
|
for (let hint in hints) {
|
||||||
|
// unpack the variants
|
||||||
|
hints[hint] = hints[hint].deep_unpack();
|
||||||
|
}
|
||||||
|
|
||||||
|
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||||
|
|
||||||
// Filter out chat, presence, calls and invitation notifications from
|
// Filter out chat, presence, calls and invitation notifications from
|
||||||
// Empathy, since we handle that information from telepathyClient.js
|
// Empathy, since we handle that information from telepathyClient.js
|
||||||
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
||||||
hints['category'] == 'x-empathy.im.room-invitation' ||
|
hints['category'] == 'x-empathy.im.room-invitation' ||
|
||||||
hints['category'] == 'x-empathy.call.incoming' ||
|
hints['category'] == 'x-empathy.call.incoming' ||
|
||||||
hints['category'] == 'x-empathy.call.incoming"' ||
|
hints['category'] == 'x-empathy.transfer.incoming' ||
|
||||||
hints['category'] == 'x-empathy.im.subscription-request' ||
|
hints['category'] == 'x-empathy.im.subscription-request' ||
|
||||||
hints['category'] == 'presence.online' ||
|
hints['category'] == 'presence.online' ||
|
||||||
hints['category'] == 'presence.offline')) {
|
hints['category'] == 'presence.offline')) {
|
||||||
@ -236,6 +256,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
Mainloop.idle_add(Lang.bind(this,
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
function () {
|
function () {
|
||||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||||
|
return false;
|
||||||
}));
|
}));
|
||||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||||
}
|
}
|
||||||
@ -249,13 +270,6 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let hint in hints) {
|
|
||||||
// unpack the variants
|
|
||||||
hints[hint] = hints[hint].deep_unpack();
|
|
||||||
}
|
|
||||||
|
|
||||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
|
||||||
|
|
||||||
// Be compatible with the various hints for image data and image path
|
// Be compatible with the various hints for image data and image path
|
||||||
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
|
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
|
||||||
|
|
||||||
@ -341,7 +355,9 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||||
ndata.actions, ndata.hints, ndata.notification];
|
ndata.actions, ndata.hints, ndata.notification];
|
||||||
|
|
||||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
let gicon = this._iconForNotificationData(icon, hints);
|
||||||
|
let iconActor = new St.Icon({ gicon: gicon,
|
||||||
|
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
|
||||||
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
notification = new MessageTray.Notification(source, summary, body,
|
notification = new MessageTray.Notification(source, summary, body,
|
||||||
@ -421,8 +437,8 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||||
notification.setTransient(hints['transient'] == true);
|
notification.setTransient(hints['transient'] == true);
|
||||||
|
|
||||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
||||||
source.processNotification(notification, sourceIconActor);
|
source.processNotification(notification, sourceGIcon);
|
||||||
},
|
},
|
||||||
|
|
||||||
CloseNotification: function(id) {
|
CloseNotification: function(id) {
|
||||||
@ -483,7 +499,11 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon) {
|
_onTrayIconAdded: function(o, icon) {
|
||||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, 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);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconRemoved: function(o, icon) {
|
_onTrayIconRemoved: function(o, icon) {
|
||||||
@ -534,11 +554,11 @@ const Source = new Lang.Class({
|
|||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
processNotification: function(notification, icon) {
|
processNotification: function(notification, gicon) {
|
||||||
if (!this.app)
|
if (gicon)
|
||||||
this._setApp();
|
this._gicon = gicon;
|
||||||
if (!this.app && icon)
|
if (!this.trayIcon)
|
||||||
this._setSummaryIcon(icon);
|
this.iconUpdated();
|
||||||
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
if (notification.resident && this.app && tracker.focus_app == this.app)
|
if (notification.resident && this.app && tracker.focus_app == this.app)
|
||||||
@ -563,26 +583,36 @@ const Source = new Lang.Class({
|
|||||||
this.notifications.length > 0)
|
this.notifications.length > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Main.overview.visible) {
|
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
|
||||||
// We can't just connect to Main.overview's 'hidden' signal,
|
|
||||||
// because it's emitted *before* it calls popModal()...
|
|
||||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
|
|
||||||
function () {
|
|
||||||
global.disconnect(id);
|
global.disconnect(id);
|
||||||
this.trayIcon.click(event);
|
this.trayIcon.click(event);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
} else {
|
|
||||||
this.trayIcon.click(event);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getApp: function() {
|
||||||
|
let app;
|
||||||
|
|
||||||
|
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||||
|
if (app != null)
|
||||||
|
return app;
|
||||||
|
|
||||||
|
if (this.trayIcon) {
|
||||||
|
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
|
||||||
|
if (app != null)
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
_setApp: function() {
|
_setApp: function() {
|
||||||
if (this.app)
|
if (this.app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
this.app = this._getApp();
|
||||||
if (!this.app)
|
if (!this.app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -590,10 +620,20 @@ const Source = new Lang.Class({
|
|||||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||||
if (!this.trayIcon) {
|
if (!this.trayIcon) {
|
||||||
this.useNotificationIcon = false;
|
this.useNotificationIcon = false;
|
||||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
this.iconUpdated();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setTitle: function(title) {
|
||||||
|
// Do nothing if .app is set, we don't want to override the
|
||||||
|
// app name with whatever is provided through libnotify (usually
|
||||||
|
// garbage)
|
||||||
|
if (this.app)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.parent(title);
|
||||||
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function(notification) {
|
||||||
this.destroyNonResidentNotifications();
|
this.destroyNonResidentNotifications();
|
||||||
this.openApp();
|
this.openApp();
|
||||||
@ -622,5 +662,20 @@ const Source = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.parent();
|
this.parent();
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -10,21 +10,14 @@ const St = imports.gi.St;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
|
|
||||||
const AppDisplay = imports.ui.appDisplay;
|
|
||||||
const ContactDisplay = imports.ui.contactDisplay;
|
|
||||||
const Dash = imports.ui.dash;
|
const Dash = imports.ui.dash;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Lightbox = imports.ui.lightbox;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const PlaceDisplay = imports.ui.placeDisplay;
|
|
||||||
const RemoteSearch = imports.ui.remoteSearch;
|
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const ViewSelector = imports.ui.viewSelector;
|
const ViewSelector = imports.ui.viewSelector;
|
||||||
const Wanda = imports.ui.wanda;
|
|
||||||
const WorkspacesView = imports.ui.workspacesView;
|
|
||||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||||
|
|
||||||
// Time for initial animation going into Overview mode
|
// Time for initial animation going into Overview mode
|
||||||
@ -77,13 +70,12 @@ const ShellInfo = new Lang.Class({
|
|||||||
let notification = null;
|
let notification = null;
|
||||||
if (this._source.notifications.length == 0) {
|
if (this._source.notifications.length == 0) {
|
||||||
notification = new MessageTray.Notification(this._source, text, null);
|
notification = new MessageTray.Notification(this._source, text, null);
|
||||||
|
notification.setTransient(true);
|
||||||
} else {
|
} else {
|
||||||
notification = this._source.notifications[0];
|
notification = this._source.notifications[0];
|
||||||
notification.update(text, null, { clear: true });
|
notification.update(text, null, { clear: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.setTransient(true);
|
|
||||||
|
|
||||||
this._undoCallback = undoCallback;
|
this._undoCallback = undoCallback;
|
||||||
if (undoCallback) {
|
if (undoCallback) {
|
||||||
notification.addButton('system-undo',
|
notification.addButton('system-undo',
|
||||||
@ -99,18 +91,21 @@ const ShellInfo = new Lang.Class({
|
|||||||
const Overview = new Lang.Class({
|
const Overview = new Lang.Class({
|
||||||
Name: 'Overview',
|
Name: 'Overview',
|
||||||
|
|
||||||
_init : function(params) {
|
_init: function() {
|
||||||
params = Params.parse(params, { isDummy: false });
|
this._overviewCreated = false;
|
||||||
|
|
||||||
this.isDummy = params.isDummy;
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
this._sessionUpdated();
|
||||||
|
},
|
||||||
|
|
||||||
// We only have an overview in user sessions, so
|
_createOverview: function() {
|
||||||
// create a dummy overview in other cases
|
if (this._overviewCreated)
|
||||||
if (this.isDummy) {
|
|
||||||
this.animationInProgress = false;
|
|
||||||
this.visible = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._overviewCreated = true;
|
||||||
|
|
||||||
// The main BackgroundActor is inside global.window_group which is
|
// The main BackgroundActor is inside global.window_group which is
|
||||||
// hidden when displaying the overview, so we create a new
|
// hidden when displaying the overview, so we create a new
|
||||||
@ -147,8 +142,6 @@ const Overview = new Lang.Class({
|
|||||||
this._capturedEventId = 0;
|
this._capturedEventId = 0;
|
||||||
this._buttonPressId = 0;
|
this._buttonPressId = 0;
|
||||||
|
|
||||||
this._workspacesDisplay = null;
|
|
||||||
|
|
||||||
this.visible = false; // animating to overview, in overview, animating out
|
this.visible = false; // animating to overview, in overview, animating out
|
||||||
this._shown = false; // show() and not hide()
|
this._shown = false; // show() and not hide()
|
||||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
||||||
@ -185,6 +178,11 @@ const Overview = new Lang.Class({
|
|||||||
this._needsFakePointerEvent = false;
|
this._needsFakePointerEvent = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
this.isDummy = !Main.sessionMode.hasOverview;
|
||||||
|
this._createOverview();
|
||||||
|
},
|
||||||
|
|
||||||
// The members we construct that are implemented in JS might
|
// The members we construct that are implemented in JS might
|
||||||
// want to access the overview as Main.overview to connect
|
// want to access the overview as Main.overview to connect
|
||||||
// signal handlers and so forth. So we create them after
|
// signal handlers and so forth. So we create them after
|
||||||
@ -195,30 +193,23 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this._shellInfo = new ShellInfo();
|
this._shellInfo = new ShellInfo();
|
||||||
|
|
||||||
this._viewSelector = new ViewSelector.ViewSelector();
|
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._group.add_actor(this._searchEntry);
|
||||||
|
|
||||||
|
this._dash = new Dash.Dash();
|
||||||
|
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||||
|
this._dash.showAppsButton);
|
||||||
this._group.add_actor(this._viewSelector.actor);
|
this._group.add_actor(this._viewSelector.actor);
|
||||||
|
this._group.add_actor(this._dash.actor);
|
||||||
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());
|
|
||||||
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
|
|
||||||
|
|
||||||
// Load remote search providers provided by applications
|
|
||||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
|
|
||||||
|
|
||||||
// TODO - recalculate everything when desktop size changes
|
// TODO - recalculate everything when desktop size changes
|
||||||
this._dash = new Dash.Dash();
|
|
||||||
this._group.add_actor(this._dash.actor);
|
|
||||||
this._dash.actor.add_constraint(this._viewSelector.constrainY);
|
|
||||||
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
||||||
this.dashIconSize = this._dash.iconSize;
|
this.dashIconSize = this._dash.iconSize;
|
||||||
this._dash.connect('icon-size-changed',
|
this._dash.connect('icon-size-changed',
|
||||||
@ -228,7 +219,7 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
// Translators: this is the name of the dock/favorites area on
|
// Translators: this is the name of the dock/favorites area on
|
||||||
// the left of the overview
|
// the left of the overview
|
||||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
|
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||||
|
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||||
this._relayout();
|
this._relayout();
|
||||||
@ -504,13 +495,13 @@ const Overview = new Lang.Class({
|
|||||||
this._coverPane.set_position(0, contentY);
|
this._coverPane.set_position(0, contentY);
|
||||||
this._coverPane.set_size(primary.width, contentHeight);
|
this._coverPane.set_size(primary.width, contentHeight);
|
||||||
|
|
||||||
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
|
let searchWidth = this._searchEntry.get_width();
|
||||||
let viewWidth = primary.width - dashWidth - this._spacing;
|
let searchHeight = this._searchEntry.get_height();
|
||||||
let viewHeight = contentHeight - 2 * this._spacing;
|
let searchX = (primary.width - searchWidth) / 2;
|
||||||
let viewY = contentY + this._spacing;
|
let searchY = contentY + this._spacing;
|
||||||
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
|
||||||
|
|
||||||
// Set the dash's x position - y is handled by a constraint
|
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
|
||||||
|
let dashY = searchY + searchHeight + this._spacing;
|
||||||
let dashX;
|
let dashX;
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
@ -518,8 +509,14 @@ const Overview = new Lang.Class({
|
|||||||
} else {
|
} else {
|
||||||
dashX = 0;
|
dashX = 0;
|
||||||
}
|
}
|
||||||
this._dash.actor.set_x(dashX);
|
|
||||||
|
|
||||||
|
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
||||||
|
let viewY = searchY + searchHeight + this._spacing;
|
||||||
|
let viewWidth = primary.width - dashWidth - this._spacing;
|
||||||
|
let viewHeight = contentHeight - this._spacing - viewY;
|
||||||
|
|
||||||
|
this._searchEntry.set_position(searchX, searchY);
|
||||||
|
this._dash.actor.set_position(dashX, dashY);
|
||||||
this._viewSelector.actor.set_position(viewX, viewY);
|
this._viewSelector.actor.set_position(viewX, viewY);
|
||||||
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||||
},
|
},
|
||||||
@ -569,6 +566,28 @@ const Overview = new Lang.Class({
|
|||||||
Lang.bind(this, this._onButtonPress));
|
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() {
|
_animateVisible: function() {
|
||||||
if (this.visible || this.animationInProgress)
|
if (this.visible || this.animationInProgress)
|
||||||
return;
|
return;
|
||||||
@ -589,21 +608,7 @@ const Overview = new Lang.Class({
|
|||||||
global.window_group.hide();
|
global.window_group.hide();
|
||||||
this._group.show();
|
this._group.show();
|
||||||
this._background.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;
|
this._group.opacity = 0;
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
@ -730,16 +735,7 @@ const Overview = new Lang.Class({
|
|||||||
this.animationInProgress = true;
|
this.animationInProgress = true;
|
||||||
this._hideInProgress = true;
|
this._hideInProgress = true;
|
||||||
|
|
||||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
this._viewSelector.zoomFromOverview();
|
||||||
this._desktopFade.opacity = 0;
|
|
||||||
this._desktopFade.show();
|
|
||||||
Tweener.addTween(this._desktopFade,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad' });
|
|
||||||
}
|
|
||||||
|
|
||||||
this._workspacesDisplay.zoomFromOverview();
|
|
||||||
|
|
||||||
// Make other elements fade out.
|
// Make other elements fade out.
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
@ -781,8 +777,7 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
global.window_group.show();
|
global.window_group.show();
|
||||||
|
|
||||||
this._workspacesDisplay.hide();
|
this._viewSelector.hide();
|
||||||
|
|
||||||
this._desktopFade.hide();
|
this._desktopFade.hide();
|
||||||
this._background.hide();
|
this._background.hide();
|
||||||
this._group.hide();
|
this._group.hide();
|
||||||
|
297
js/ui/panel.js
@ -4,6 +4,7 @@ const Cairo = imports.cairo;
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
@ -13,6 +14,7 @@ const St = imports.gi.St;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Atk = imports.gi.Atk;
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
@ -20,7 +22,6 @@ const Layout = imports.ui.layout;
|
|||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const DateMenu = imports.ui.dateMenu;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
@ -31,33 +32,6 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
|||||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||||
const SPINNER_ANIMATION_TIME = 0.2;
|
const SPINNER_ANIMATION_TIME = 0.2;
|
||||||
|
|
||||||
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
|
|
||||||
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.XKBIndicator,
|
|
||||||
'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 GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
|
|
||||||
const GDM_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.XKBIndicator,
|
|
||||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
|
|
||||||
};
|
|
||||||
|
|
||||||
// To make sure the panel corners blend nicely with the panel,
|
// To make sure the panel corners blend nicely with the panel,
|
||||||
// we draw background and borders the same way, e.g. drawing
|
// we draw background and borders the same way, e.g. drawing
|
||||||
// them as filled shapes from the outside inwards instead of
|
// them as filled shapes from the outside inwards instead of
|
||||||
@ -248,14 +222,14 @@ const AppMenuButton = new Lang.Class({
|
|||||||
Name: 'AppMenuButton',
|
Name: 'AppMenuButton',
|
||||||
Extends: PanelMenu.Button,
|
Extends: PanelMenu.Button,
|
||||||
|
|
||||||
_init: function(menuManager) {
|
_init: function(panel) {
|
||||||
this.parent(0.0, null, true);
|
this.parent(0.0, null, true);
|
||||||
|
|
||||||
this.actor.accessible_role = Atk.Role.MENU;
|
this.actor.accessible_role = Atk.Role.MENU;
|
||||||
|
|
||||||
this._startingApps = [];
|
this._startingApps = [];
|
||||||
|
|
||||||
this._menuManager = menuManager;
|
this._menuManager = panel.menuManager;
|
||||||
this._targetApp = null;
|
this._targetApp = null;
|
||||||
this._appMenuNotifyId = 0;
|
this._appMenuNotifyId = 0;
|
||||||
this._actionGroupNotifyId = 0;
|
this._actionGroupNotifyId = 0;
|
||||||
@ -312,7 +286,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
if (this._visible)
|
if (this._visible || Main.screenShield.locked)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._visible = true;
|
this._visible = true;
|
||||||
@ -373,6 +347,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._stop = true;
|
this._stop = true;
|
||||||
|
this.actor.reactive = true;
|
||||||
Tweener.addTween(this._spinner.actor,
|
Tweener.addTween(this._spinner.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: SPINNER_ANIMATION_TIME,
|
time: SPINNER_ANIMATION_TIME,
|
||||||
@ -387,6 +362,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
startAnimation: function() {
|
startAnimation: function() {
|
||||||
this._stop = false;
|
this._stop = false;
|
||||||
|
this.actor.reactive = false;
|
||||||
this._spinner.actor.show();
|
this._spinner.actor.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -625,8 +601,8 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
|
|
||||||
this.actor.label_actor = this._label;
|
this.actor.label_actor = this._label;
|
||||||
|
|
||||||
this._hotCorner = new Layout.HotCorner();
|
this.hotCorner = new Layout.HotCorner();
|
||||||
container.add_actor(this._hotCorner.actor);
|
container.add_actor(this.hotCorner.actor);
|
||||||
|
|
||||||
// Hack up our menu...
|
// Hack up our menu...
|
||||||
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
|
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
|
||||||
@ -676,10 +652,10 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
hotBox.x1 = Math.round(x);
|
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.y1 = Math.round(y);
|
||||||
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
|
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
|
||||||
this._hotCorner.actor.allocate(hotBox, flags);
|
this.hotCorner.actor.allocate(hotBox, flags);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDragOver: function(source, actor, x, y, time) {
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
@ -701,7 +677,7 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
|
|
||||||
_onCapturedEvent: function(actor, event) {
|
_onCapturedEvent: function(actor, event) {
|
||||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||||
if (!this._hotCorner.shouldToggleOverviewOnClick())
|
if (!this.hotCorner.shouldToggleOverviewOnClick())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -778,10 +754,11 @@ const PanelCorner = new Lang.Class({
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Start at the back and work backward
|
// Start at the back and work backward
|
||||||
let index = children.length - 1;
|
let index;
|
||||||
while (!children[index].visible && index >= 0)
|
for (index = children.length - 1; index >= 0; index--) {
|
||||||
index--;
|
if (children[index].visible)
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -802,10 +779,11 @@ const PanelCorner = new Lang.Class({
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Start at the front and work forward
|
// Start at the front and work forward
|
||||||
let index = 0;
|
let index;
|
||||||
while (!children[index].visible && index < children.length)
|
for (index = 0; index < children.length; index++) {
|
||||||
index++;
|
if (children[index].visible)
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (index == children.length)
|
if (index == children.length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -915,6 +893,29 @@ 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,
|
||||||
|
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||||
|
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||||
|
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Config.HAVE_BLUETOOTH)
|
||||||
|
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
|
||||||
|
imports.ui.status.bluetooth.Indicator;
|
||||||
|
|
||||||
|
try {
|
||||||
|
PANEL_ITEM_IMPLEMENTATIONS['network'] =
|
||||||
|
imports.ui.status.network.NMApplet;
|
||||||
|
} catch(e) {
|
||||||
|
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||||
|
}
|
||||||
|
|
||||||
const Panel = new Lang.Class({
|
const Panel = new Lang.Class({
|
||||||
Name: 'Panel',
|
Name: 'Panel',
|
||||||
@ -924,7 +925,7 @@ const Panel = new Lang.Class({
|
|||||||
reactive: true });
|
reactive: true });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
|
||||||
this._statusArea = {};
|
this.statusArea = {};
|
||||||
|
|
||||||
Main.overview.connect('shown', Lang.bind(this, function () {
|
Main.overview.connect('shown', Lang.bind(this, function () {
|
||||||
this.actor.add_style_class_name('in-overview');
|
this.actor.add_style_class_name('in-overview');
|
||||||
@ -933,7 +934,7 @@ const Panel = new Lang.Class({
|
|||||||
this.actor.remove_style_class_name('in-overview');
|
this.actor.remove_style_class_name('in-overview');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._menus = new PopupMenu.PopupMenuManager(this);
|
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
||||||
|
|
||||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||||
this.actor.add_actor(this._leftBox);
|
this.actor.add_actor(this._leftBox);
|
||||||
@ -960,43 +961,12 @@ const Panel = new Lang.Class({
|
|||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||||
|
|
||||||
/* Button on the left side of the panel. */
|
|
||||||
if (global.session_type == Shell.SessionType.USER) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
this._appMenu = new AppMenuButton(this._menus);
|
|
||||||
this._leftBox.add(this._appMenu.actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* center */
|
|
||||||
if (global.session_type == Shell.SessionType.USER)
|
|
||||||
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
|
|
||||||
else
|
|
||||||
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
|
|
||||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
|
||||||
this._menus.addMenu(this._dateMenu.menu);
|
|
||||||
|
|
||||||
/* right */
|
|
||||||
if (global.session_type == Shell.SessionType.GDM) {
|
|
||||||
this._status_area_order = GDM_STATUS_AREA_ORDER;
|
|
||||||
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION;
|
|
||||||
} else {
|
|
||||||
this._status_area_order = STANDARD_STATUS_AREA_ORDER;
|
|
||||||
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
|
||||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
|
||||||
|
|
||||||
Main.layoutManager.panelBox.add(this.actor);
|
Main.layoutManager.panelBox.add(this.actor);
|
||||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic',
|
||||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
|
||||||
|
this._updatePanel();
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
@ -1112,76 +1082,127 @@ const Panel = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
startStatusArea: function() {
|
openAppMenu: function() {
|
||||||
for (let i = 0; i < this._status_area_order.length; i++) {
|
let indicator = this.statusArea.appMenu;
|
||||||
let role = this._status_area_order[i];
|
if (!indicator) // appMenu not supported by current session mode
|
||||||
let constructor = this._status_area_shell_implementation[role];
|
return;
|
||||||
|
|
||||||
|
let menu = indicator.menu;
|
||||||
|
if (!indicator.actor.reactive || menu.isOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
menu.open();
|
||||||
|
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
|
||||||
|
_initBox: function(elements, box) {
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
let role = elements[i];
|
||||||
|
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||||
if (!constructor) {
|
if (!constructor) {
|
||||||
// This icon is not implemented (this is a bug)
|
// panel icon is not supported (can happen for
|
||||||
|
// bluetooth or network)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let indicator = new constructor();
|
|
||||||
this.addToStatusArea(role, indicator, i);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_insertStatusItem: function(actor, position) {
|
_hideIndicators: function() {
|
||||||
let children = this._rightBox.get_children();
|
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
|
||||||
let i;
|
let indicator = this.statusArea[role];
|
||||||
for (i = children.length - 1; i >= 0; i--) {
|
if (!indicator)
|
||||||
let rolePosition = children[i]._rolePosition;
|
continue;
|
||||||
if (position > rolePosition) {
|
if (indicator.menu)
|
||||||
this._rightBox.insert_child_at_index(actor, i + 1);
|
indicator.menu.close();
|
||||||
break;
|
indicator.container.hide();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addToStatusArea: function(role, indicator, position) {
|
_ensureIndicator: function(role) {
|
||||||
if (this._statusArea[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;
|
||||||
|
}
|
||||||
|
return indicator;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBox: function(elements, box) {
|
||||||
|
let nChildren = box.get_n_children();
|
||||||
|
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
let role = elements[i];
|
||||||
|
let indicator = this._ensureIndicator(role);
|
||||||
|
if (indicator == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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);
|
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
|
||||||
|
|
||||||
if (!(indicator instanceof PanelMenu.Button))
|
if (!(indicator instanceof PanelMenu.Button))
|
||||||
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
||||||
|
|
||||||
if (!position)
|
position = position || 0;
|
||||||
position = 0;
|
let boxes = {
|
||||||
this._insertStatusItem(indicator.actor, position);
|
left: this._leftBox,
|
||||||
this._menus.addMenu(indicator.menu);
|
center: this._centerBox,
|
||||||
|
right: this._rightBox
|
||||||
this._statusArea[role] = indicator;
|
};
|
||||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
let boxContainer = boxes[box] || this._rightBox;
|
||||||
this._statusArea[role] = null;
|
this.statusArea[role] = indicator;
|
||||||
emitter.disconnect(destroyId);
|
this._addToPanelBox(role, indicator, position, boxContainer);
|
||||||
}));
|
|
||||||
|
|
||||||
return indicator;
|
return indicator;
|
||||||
},
|
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon, role) {
|
|
||||||
if (this._status_area_shell_implementation[role]) {
|
|
||||||
// This icon is legacy, and replaced by a Shell version
|
|
||||||
// Hide it
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.height = PANEL_ICON_SIZE;
|
|
||||||
let buttonBox = new PanelMenu.ButtonBox();
|
|
||||||
let box = buttonBox.actor;
|
|
||||||
box.add_actor(icon);
|
|
||||||
|
|
||||||
this._insertStatusItem(box, this._status_area_order.indexOf(role));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onTrayIconRemoved: function(o, icon) {
|
|
||||||
let box = icon.get_parent();
|
|
||||||
if (box && box._delegate instanceof PanelMenu.ButtonBox)
|
|
||||||
box.destroy();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
@ -20,6 +21,10 @@ const ButtonBox = new Lang.Class({
|
|||||||
this.actor = new Shell.GenericContainer(params);
|
this.actor = new Shell.GenericContainer(params);
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
|
||||||
|
this.container = new St.Bin({ y_fill: true,
|
||||||
|
x_fill: true,
|
||||||
|
child: this.actor });
|
||||||
|
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
@ -114,6 +119,12 @@ const Button = new Lang.Class({
|
|||||||
this.setName(nameText);
|
this.setName(nameText);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setSensitive: function(sensitive) {
|
||||||
|
this.actor.reactive = sensitive;
|
||||||
|
this.actor.can_focus = sensitive;
|
||||||
|
this.actor.track_hover = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
setName: function(text) {
|
setName: function(text) {
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
// This is the easiest way to provide a accessible name to
|
// This is the easiest way to provide a accessible name to
|
||||||
@ -175,8 +186,7 @@ const Button = new Lang.Class({
|
|||||||
_onMenuKeyPress: function(actor, event) {
|
_onMenuKeyPress: function(actor, event) {
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||||
let focusManager = St.FocusManager.get_for_stage(global.stage);
|
let group = global.focus_manager.get_group(this.actor);
|
||||||
let group = focusManager.get_group(this.actor);
|
|
||||||
if (group) {
|
if (group) {
|
||||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||||
group.navigate_focus(this.actor, direction, false);
|
group.navigate_focus(this.actor, direction, false);
|
||||||
@ -224,19 +234,36 @@ const SystemStatusButton = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(iconName, nameText) {
|
_init: function(iconName, nameText) {
|
||||||
this.parent(0.0, nameText);
|
this.parent(0.0, nameText);
|
||||||
|
|
||||||
this._iconActor = new St.Icon({ icon_name: iconName,
|
|
||||||
icon_type: St.IconType.SYMBOLIC,
|
|
||||||
style_class: 'system-status-icon' });
|
|
||||||
this.actor.add_actor(this._iconActor);
|
|
||||||
this.actor.add_style_class_name('panel-status-button');
|
this.actor.add_style_class_name('panel-status-button');
|
||||||
|
|
||||||
|
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||||
|
this.actor.add_actor(this._box);
|
||||||
|
|
||||||
|
if (iconName)
|
||||||
|
this.setIcon(iconName);
|
||||||
|
},
|
||||||
|
|
||||||
|
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) {
|
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) {
|
setGIcon: function(gicon) {
|
||||||
this._iconActor.gicon = gicon;
|
if (this.mainIcon)
|
||||||
|
this.mainIcon.gicon = gicon;
|
||||||
|
else
|
||||||
|
this.mainIcon = this.addIcon(gicon);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,434 +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_home_dir(), '.gtk-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"));
|
|
||||||
},
|
|
||||||
|
|
||||||
getResultMetas: function(resultIds) {
|
|
||||||
let metas = [];
|
|
||||||
for (let i = 0; i < resultIds.length; i++) {
|
|
||||||
let placeInfo = Main.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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return metas;
|
|
||||||
},
|
|
||||||
|
|
||||||
activateResult: function(id, params) {
|
|
||||||
let placeInfo = Main.placesManager.lookupPlaceById(id);
|
|
||||||
placeInfo.launch(params);
|
|
||||||
},
|
|
||||||
|
|
||||||
_compareResultMeta: function (idA, idB) {
|
|
||||||
let infoA = Main.placesManager.lookupPlaceById(idA);
|
|
||||||
let infoB = Main.placesManager.lookupPlaceById(idB);
|
|
||||||
return infoA.name.localeCompare(infoB.name);
|
|
||||||
},
|
|
||||||
|
|
||||||
_searchPlaces: function(places, terms) {
|
|
||||||
let multiplePrefixResults = [];
|
|
||||||
let prefixResults = [];
|
|
||||||
let multipleSubstringResults = [];
|
|
||||||
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.MULTIPLE_PREFIX)
|
|
||||||
multiplePrefixResults.push(place.id);
|
|
||||||
else if (mtype == Search.MatchType.PREFIX)
|
|
||||||
prefixResults.push(place.id);
|
|
||||||
else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
|
|
||||||
multipleSubstringResults.push(place.id);
|
|
||||||
else if (mtype == Search.MatchType.SUBSTRING)
|
|
||||||
substringResults.push(place.id);
|
|
||||||
}
|
|
||||||
multiplePrefixResults.sort(this._compareResultMeta);
|
|
||||||
prefixResults.sort(this._compareResultMeta);
|
|
||||||
multipleSubstringResults.sort(this._compareResultMeta);
|
|
||||||
substringResults.sort(this._compareResultMeta);
|
|
||||||
return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults)));
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
|
||||||
let places = Main.placesManager.getAllPlaces();
|
|
||||||
return this._searchPlaces(places, terms);
|
|
||||||
},
|
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
|
||||||
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
|
|
||||||
return this._searchPlaces(places, terms);
|
|
||||||
}
|
|
||||||
});
|
|
126
js/ui/pointerWatcher.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
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 = Shell.IdleMonitor.get();
|
||||||
|
idleMonitor.add_watch(IDLE_TIME,
|
||||||
|
Lang.bind(this, this._onIdleMonitorWatch));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
|
||||||
|
this._idle = userBecameIdle;
|
||||||
|
if (!userBecameIdle)
|
||||||
|
this._updatePointer();
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -37,12 +37,13 @@ const PopupBaseMenuItem = new Lang.Class({
|
|||||||
activate: true,
|
activate: true,
|
||||||
hover: true,
|
hover: true,
|
||||||
sensitive: true,
|
sensitive: true,
|
||||||
style_class: null
|
style_class: null,
|
||||||
|
can_focus: true
|
||||||
});
|
});
|
||||||
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
|
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
|
||||||
reactive: params.reactive,
|
reactive: params.reactive,
|
||||||
track_hover: params.reactive,
|
track_hover: params.reactive,
|
||||||
can_focus: params.reactive,
|
can_focus: params.can_focus,
|
||||||
accessible_role: Atk.Role.MENU_ITEM});
|
accessible_role: Atk.Role.MENU_ITEM});
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
@ -60,6 +61,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
|||||||
|
|
||||||
this.setSensitive(this.sensitive);
|
this.setSensitive(this.sensitive);
|
||||||
|
|
||||||
|
if (!this._activatable)
|
||||||
|
this.actor.add_style_class_name('popup-inactive-menu-item');
|
||||||
|
|
||||||
if (params.style_class)
|
if (params.style_class)
|
||||||
this.actor.add_style_class_name(params.style_class);
|
this.actor.add_style_class_name(params.style_class);
|
||||||
|
|
||||||
@ -69,10 +73,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
|||||||
}
|
}
|
||||||
if (params.reactive && params.hover)
|
if (params.reactive && params.hover)
|
||||||
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
|
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-in', Lang.bind(this, this._onKeyFocusIn));
|
||||||
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
|
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onStyleChanged: function (actor) {
|
_onStyleChanged: function (actor) {
|
||||||
@ -136,10 +139,6 @@ const PopupBaseMenuItem = new Lang.Class({
|
|||||||
this.actor.reactive = sensitive;
|
this.actor.reactive = sensitive;
|
||||||
this.actor.can_focus = 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);
|
this.emit('sensitive-changed', sensitive);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -184,12 +183,14 @@ const PopupBaseMenuItem = new Lang.Class({
|
|||||||
this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' });
|
this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' });
|
||||||
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
|
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
|
||||||
this.actor.add_actor(this._dot);
|
this.actor.add_actor(this._dot);
|
||||||
|
this.actor.add_accessible_state (Atk.StateType.CHECKED);
|
||||||
} else {
|
} else {
|
||||||
if (!this._dot)
|
if (!this._dot)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._dot.destroy();
|
this._dot.destroy();
|
||||||
this._dot = null;
|
this._dot = null;
|
||||||
|
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -400,7 +401,8 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
|||||||
Extends: PopupBaseMenuItem,
|
Extends: PopupBaseMenuItem,
|
||||||
|
|
||||||
_init: function () {
|
_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._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
|
||||||
this.addActor(this._drawingArea, { span: -1, expand: true });
|
this.addActor(this._drawingArea, { span: -1, expand: true });
|
||||||
@ -546,6 +548,10 @@ const PopupSliderMenuItem = new Lang.Class({
|
|||||||
this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
|
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
|
||||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
|
this.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||||
|
if (!this.actor.mapped)
|
||||||
|
this._endDragging();
|
||||||
|
}));
|
||||||
|
|
||||||
this._releaseId = this._motionId = 0;
|
this._releaseId = this._motionId = 0;
|
||||||
this._dragging = false;
|
this._dragging = false;
|
||||||
@ -715,7 +721,8 @@ const Switch = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(state) {
|
_init: function(state) {
|
||||||
this.actor = new St.Bin({ style_class: 'toggle-switch',
|
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"
|
// Translators: this MUST be either "toggle-switch-us"
|
||||||
// (for toggle switches containing the English words
|
// (for toggle switches containing the English words
|
||||||
// "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
// "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||||
@ -759,7 +766,7 @@ const PopupSwitchMenuItem = new Lang.Class({
|
|||||||
{ expand: true, span: -1, align: St.Align.END });
|
{ expand: true, span: -1, align: St.Align.END });
|
||||||
|
|
||||||
this._statusLabel = new St.Label({ text: '',
|
this._statusLabel = new St.Label({ text: '',
|
||||||
style_class: 'popup-inactive-menu-item'
|
style_class: 'popup-status-menu-item'
|
||||||
});
|
});
|
||||||
this._statusBin.child = this._switch.actor;
|
this._statusBin.child = this._switch.actor;
|
||||||
},
|
},
|
||||||
@ -769,12 +776,10 @@ const PopupSwitchMenuItem = new Lang.Class({
|
|||||||
this._statusLabel.text = text;
|
this._statusLabel.text = text;
|
||||||
this._statusBin.child = this._statusLabel;
|
this._statusBin.child = this._statusLabel;
|
||||||
this.actor.reactive = false;
|
this.actor.reactive = false;
|
||||||
this.actor.can_focus = false;
|
|
||||||
this.actor.accessible_role = Atk.Role.MENU_ITEM;
|
this.actor.accessible_role = Atk.Role.MENU_ITEM;
|
||||||
} else {
|
} else {
|
||||||
this._statusBin.child = this._switch.actor;
|
this._statusBin.child = this._switch.actor;
|
||||||
this.actor.reactive = true;
|
this.actor.reactive = true;
|
||||||
this.actor.can_focus = true;
|
|
||||||
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
|
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
|
||||||
}
|
}
|
||||||
this.checkAccessibleState();
|
this.checkAccessibleState();
|
||||||
@ -865,12 +870,15 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
// for the menu which causes its prelight state to freeze
|
// for the menu which causes its prelight state to freeze
|
||||||
this.blockSourceEvents = false;
|
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._activeMenuItem = null;
|
||||||
this._childMenus = [];
|
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) {
|
addAction: function(title, callback) {
|
||||||
@ -884,10 +892,6 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
addSettingsAction: function(title, desktopFile) {
|
addSettingsAction: function(title, desktopFile) {
|
||||||
// Don't allow user settings to get edited unless we're in a user session
|
|
||||||
if (global.session_type != Shell.SessionType.USER)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
let menuItem = this.addAction(title, function() {
|
let menuItem = this.addAction(title, function() {
|
||||||
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||||
|
|
||||||
@ -899,11 +903,22 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
app.activate();
|
app.activate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
menuItem.actor.visible = Main.sessionMode.allowSettings;
|
||||||
|
this._settingsActions[desktopFile] = menuItem;
|
||||||
|
|
||||||
return menuItem;
|
return menuItem;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_setSettingsVisibility: function(visible) {
|
||||||
|
for (let id in this._settingsActions) {
|
||||||
|
let item = this._settingsActions[id];
|
||||||
|
item.actor.visible = visible;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
isEmpty: function() {
|
isEmpty: function() {
|
||||||
return this.box.get_children().length == 0;
|
return this.box.get_n_children() == 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
isChildMenu: function(menu) {
|
isChildMenu: function(menu) {
|
||||||
@ -939,7 +954,7 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
_connectSubMenuSignals: function(object, menu) {
|
_connectSubMenuSignals: function(object, menu) {
|
||||||
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
|
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
|
||||||
this.emit('activate');
|
this.emit('activate');
|
||||||
this.close(true);
|
this.close(BoxPointer.PopupAnimation.FULL);
|
||||||
}));
|
}));
|
||||||
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
|
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
|
||||||
if (this._activeMenuItem && this._activeMenuItem != submenuItem)
|
if (this._activeMenuItem && this._activeMenuItem != submenuItem)
|
||||||
@ -974,7 +989,7 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
||||||
this.emit('activate', menuItem);
|
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 weird name is to avoid a conflict with some random property
|
||||||
// the menuItem may have, called destroyId
|
// the menuItem may have, called destroyId
|
||||||
@ -1044,14 +1059,17 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
|
|
||||||
if (menuItem instanceof PopupMenuSection) {
|
if (menuItem instanceof PopupMenuSection) {
|
||||||
this._connectSubMenuSignals(menuItem, menuItem);
|
this._connectSubMenuSignals(menuItem, menuItem);
|
||||||
menuItem._closingId = this.connect('open-state-changed',
|
menuItem._parentOpenStateChangedId = this.connect('open-state-changed',
|
||||||
function(self, open) {
|
function(self, open) {
|
||||||
if (!open)
|
if (open)
|
||||||
menuItem.close(false);
|
menuItem.open();
|
||||||
|
else
|
||||||
|
menuItem.close();
|
||||||
});
|
});
|
||||||
menuItem.connect('destroy', Lang.bind(this, function() {
|
menuItem.connect('destroy', Lang.bind(this, function() {
|
||||||
menuItem.disconnect(menuItem._subMenuActivateId);
|
menuItem.disconnect(menuItem._subMenuActivateId);
|
||||||
menuItem.disconnect(menuItem._subMenuActiveChangeId);
|
menuItem.disconnect(menuItem._subMenuActiveChangeId);
|
||||||
|
this.disconnect(menuItem._parentOpenStateChangedId);
|
||||||
|
|
||||||
this.length--;
|
this.length--;
|
||||||
}));
|
}));
|
||||||
@ -1064,7 +1082,7 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
this._connectItemSignals(menuItem);
|
this._connectItemSignals(menuItem);
|
||||||
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
|
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
|
||||||
if (!open)
|
if (!open)
|
||||||
menuItem.menu.close(false);
|
menuItem.menu.close(BoxPointer.PopupAnimation.FADE);
|
||||||
});
|
});
|
||||||
} else if (menuItem instanceof PopupSeparatorMenuItem) {
|
} else if (menuItem instanceof PopupSeparatorMenuItem) {
|
||||||
this._connectItemSignals(menuItem);
|
this._connectItemSignals(menuItem);
|
||||||
@ -1086,7 +1104,8 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
let columnWidths = [];
|
let columnWidths = [];
|
||||||
let items = this.box.get_children();
|
let items = this.box.get_children();
|
||||||
for (let i = 0; i < items.length; i++) {
|
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;
|
continue;
|
||||||
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
|
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
|
||||||
let itemColumnWidths = items[i]._delegate.getColumnWidths();
|
let itemColumnWidths = items[i]._delegate.getColumnWidths();
|
||||||
@ -1151,9 +1170,9 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
if (this.isOpen)
|
if (this.isOpen)
|
||||||
this.close(true);
|
this.close(BoxPointer.PopupAnimation.FULL);
|
||||||
else
|
else
|
||||||
this.open(true);
|
this.open(BoxPointer.PopupAnimation.FULL);
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@ -1161,6 +1180,9 @@ const PopupMenuBase = new Lang.Class({
|
|||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
|
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
|
|
||||||
|
Main.sessionMode.disconnect(this._sessionUpdatedId);
|
||||||
|
this._sessionUpdatedId = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(PopupMenuBase.prototype);
|
Signals.addSignalMethods(PopupMenuBase.prototype);
|
||||||
@ -1214,7 +1236,7 @@ const PopupMenu = new Lang.Class({
|
|||||||
|
|
||||||
_onKeyPressEvent: function(actor, event) {
|
_onKeyPressEvent: function(actor, event) {
|
||||||
if (event.get_key_symbol() == Clutter.Escape) {
|
if (event.get_key_symbol() == Clutter.Escape) {
|
||||||
this.close(true);
|
this.close(BoxPointer.PopupAnimation.FULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1277,24 +1299,6 @@ const PopupSubMenu = new Lang.Class({
|
|||||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||||
vscrollbar_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.add_actor(this.box);
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
this.actor.clip_to_allocation = true;
|
this.actor.clip_to_allocation = true;
|
||||||
@ -1344,6 +1348,11 @@ const PopupSubMenu = new Lang.Class({
|
|||||||
this.actor.vscrollbar_policy =
|
this.actor.vscrollbar_policy =
|
||||||
needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
|
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
|
// It looks funny if we animate with a scrollbar (at what point is
|
||||||
// the scrollbar added?) so just skip that case
|
// the scrollbar added?) so just skip that case
|
||||||
if (animate && needsScrollbar)
|
if (animate && needsScrollbar)
|
||||||
@ -1416,7 +1425,7 @@ const PopupSubMenu = new Lang.Class({
|
|||||||
// Move focus back to parent menu if the user types Left.
|
// Move focus back to parent menu if the user types Left.
|
||||||
|
|
||||||
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
|
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
|
||||||
this.close(true);
|
this.close(BoxPointer.PopupAnimation.FULL);
|
||||||
this.sourceActor._delegate.setActive(true);
|
this.sourceActor._delegate.setActive(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1450,7 +1459,7 @@ const PopupMenuSection = new Lang.Class({
|
|||||||
|
|
||||||
// deliberately ignore any attempt to open() or close(), but emit the
|
// deliberately ignore any attempt to open() or close(), but emit the
|
||||||
// corresponding signal so children can still pick it up
|
// 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); },
|
close: function() { this.emit('open-state-changed', false); },
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@ -1498,7 +1507,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
|||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
|
|
||||||
if (symbol == Clutter.KEY_Right) {
|
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);
|
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
|
||||||
return true;
|
return true;
|
||||||
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
|
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
|
||||||
@ -1510,7 +1519,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
activate: function(event) {
|
activate: function(event) {
|
||||||
this.menu.open(true);
|
this.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onButtonReleaseEvent: function(actor) {
|
_onButtonReleaseEvent: function(actor) {
|
||||||
@ -1537,7 +1546,7 @@ const PopupComboMenu = new Lang.Class({
|
|||||||
|
|
||||||
_onKeyPressEvent: function(actor, event) {
|
_onKeyPressEvent: function(actor, event) {
|
||||||
if (event.get_key_symbol() == Clutter.Escape) {
|
if (event.get_key_symbol() == Clutter.Escape) {
|
||||||
this.close(true);
|
this.close(BoxPointer.PopupAnimation.FULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1889,10 +1898,6 @@ const RemoteMenu = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.actor.reactive = item.actor.can_focus = action.enabled;
|
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() {
|
destroyId = item.connect('destroy', Lang.bind(this, function() {
|
||||||
item.disconnect(destroyId);
|
item.disconnect(destroyId);
|
||||||
@ -1917,7 +1922,7 @@ const RemoteMenu = new Lang.Class({
|
|||||||
while (k0 < currentItems.length && currentItems[k0]._ignored)
|
while (k0 < currentItems.length && currentItems[k0]._ignored)
|
||||||
k0++;
|
k0++;
|
||||||
// find the right menu item matching the model item
|
// find the right menu item matching the model item
|
||||||
for (j0 = 0; j0 < position; j0++, k0++) {
|
for (j0 = 0; k0 < currentItems.length && j0 < position; j0++, k0++) {
|
||||||
if (currentItems[k0]._ignored)
|
if (currentItems[k0]._ignored)
|
||||||
k0++;
|
k0++;
|
||||||
}
|
}
|
||||||
@ -1927,7 +1932,7 @@ const RemoteMenu = new Lang.Class({
|
|||||||
for (k = k0; k < currentItems.length; k++)
|
for (k = k0; k < currentItems.length; k++)
|
||||||
currentItems[k].destroy();
|
currentItems[k].destroy();
|
||||||
} else {
|
} else {
|
||||||
for (j = j0, k = k0; j < j0 + removed; j++, k++) {
|
for (j = j0, k = k0; k < currentItems.length && j < j0 + removed; j++, k++) {
|
||||||
currentItems[k].destroy();
|
currentItems[k].destroy();
|
||||||
|
|
||||||
if (currentItems[k]._ignored)
|
if (currentItems[k]._ignored)
|
||||||
@ -1958,8 +1963,9 @@ const RemoteMenu = new Lang.Class({
|
|||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
} else if (changeSignal) {
|
} else if (changeSignal) {
|
||||||
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() {
|
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) {
|
||||||
this.actionGroup.disconnect(signalId);
|
actionGroup.disconnect(signalId);
|
||||||
|
if (this._actions[actionName]) return;
|
||||||
|
|
||||||
// force a full update
|
// force a full update
|
||||||
this._modelChanged(model, 0, -1, model.get_n_items(), target);
|
this._modelChanged(model, 0, -1, model.get_n_items(), target);
|
||||||
@ -2024,11 +2030,6 @@ const RemoteMenu = new Lang.Class({
|
|||||||
for (let i = 0; i < action.items.length; i++) {
|
for (let i = 0; i < action.items.length; i++) {
|
||||||
let item = action.items[i];
|
let item = action.items[i];
|
||||||
item.actor.reactive = item.actor.can_focus = action.enabled;
|
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');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2056,6 +2057,9 @@ const PopupMenuManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
addMenu: function(menu, position) {
|
addMenu: function(menu, position) {
|
||||||
|
if (this._findMenu(menu) > -1)
|
||||||
|
return;
|
||||||
|
|
||||||
let menudata = {
|
let menudata = {
|
||||||
menu: menu,
|
menu: menu,
|
||||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||||
@ -2190,11 +2194,11 @@ const PopupMenuManager = new Lang.Class({
|
|||||||
let oldMenu = this._activeMenu;
|
let oldMenu = this._activeMenu;
|
||||||
this._activeMenu = null;
|
this._activeMenu = null;
|
||||||
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
||||||
this._menuStack[i].close(false);
|
this._menuStack[i].close(BoxPointer.PopupAnimation.FADE);
|
||||||
oldMenu.close(false);
|
oldMenu.close(BoxPointer.PopupAnimation.FADE);
|
||||||
newMenu.open(false);
|
newMenu.open(BoxPointer.PopupAnimation.FADE);
|
||||||
} else
|
} else
|
||||||
newMenu.open(true);
|
newMenu.open(BoxPointer.PopupAnimation.FULL);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMenuSourceEnter: function(menu) {
|
_onMenuSourceEnter: function(menu) {
|
||||||
@ -2279,9 +2283,6 @@ const PopupMenuManager = new Lang.Class({
|
|||||||
this._owner.menuEventFilter(event))
|
this._owner.menuEventFilter(event))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (this._activeMenu != null && this._activeMenu.passEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (this._didPop) {
|
if (this._didPop) {
|
||||||
this._didPop = false;
|
this._didPop = false;
|
||||||
return true;
|
return true;
|
||||||
@ -2309,6 +2310,6 @@ const PopupMenuManager = new Lang.Class({
|
|||||||
|
|
||||||
_closeMenu: function() {
|
_closeMenu: function() {
|
||||||
if (this._activeMenu != null)
|
if (this._activeMenu != null)
|
||||||
this._activeMenu.close(true);
|
this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -34,16 +34,17 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
|||||||
|
|
||||||
function loadRemoteSearchProviders(addProviderCallback) {
|
function loadRemoteSearchProviders(addProviderCallback) {
|
||||||
let dataDirs = GLib.get_system_data_dirs();
|
let dataDirs = GLib.get_system_data_dirs();
|
||||||
|
let loadedProviders = {};
|
||||||
for (let i = 0; i < dataDirs.length; i++) {
|
for (let i = 0; i < dataDirs.length; i++) {
|
||||||
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']);
|
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']);
|
||||||
let dir = Gio.file_new_for_path(path);
|
let dir = Gio.file_new_for_path(path);
|
||||||
if (!dir.query_exists(null))
|
if (!dir.query_exists(null))
|
||||||
continue;
|
continue;
|
||||||
loadRemoteSearchProvidersFromDir(dir, addProviderCallback);
|
loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
|
function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback) {
|
||||||
let dirPath = dir.get_path();
|
let dirPath = dir.get_path();
|
||||||
FileUtils.listDirAsync(dir, Lang.bind(this, function(files) {
|
FileUtils.listDirAsync(dir, Lang.bind(this, function(files) {
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
@ -62,15 +63,34 @@ function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
|
|||||||
let remoteProvider, title;
|
let remoteProvider, title;
|
||||||
try {
|
try {
|
||||||
let group = KEY_FILE_GROUP;
|
let group = KEY_FILE_GROUP;
|
||||||
let icon = keyfile.get_string(group, 'Icon');
|
|
||||||
let busName = keyfile.get_string(group, 'BusName');
|
let busName = keyfile.get_string(group, 'BusName');
|
||||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||||
|
|
||||||
|
if (loadedProviders[objectPath])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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);
|
title = keyfile.get_locale_string(group, 'Title', null);
|
||||||
|
}
|
||||||
|
|
||||||
remoteProvider = new RemoteSearchProvider(title,
|
remoteProvider = new RemoteSearchProvider(title,
|
||||||
icon,
|
icon,
|
||||||
busName,
|
busName,
|
||||||
objectPath);
|
objectPath);
|
||||||
|
loadedProviders[objectPath] = remoteProvider;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log('Failed to add search provider "%s": %s'.format(title, e.toString()));
|
log('Failed to add search provider "%s": %s'.format(title, e.toString()));
|
||||||
continue;
|
continue;
|
||||||
@ -91,15 +111,13 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
dbusName, dbusPath);
|
dbusName, dbusPath);
|
||||||
|
|
||||||
this.parent(title.toUpperCase());
|
this.parent(title.toUpperCase());
|
||||||
this.async = true;
|
|
||||||
this._cancellable = new Gio.Cancellable();
|
this._cancellable = new Gio.Cancellable();
|
||||||
},
|
},
|
||||||
|
|
||||||
createIcon: function(size, meta) {
|
createIcon: function(size, meta) {
|
||||||
if (meta['gicon']) {
|
if (meta['gicon']) {
|
||||||
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
||||||
icon_size: size,
|
icon_size: size });
|
||||||
icon_type: St.IconType.FULLCOLOR });
|
|
||||||
} else if (meta['icon-data']) {
|
} else if (meta['icon-data']) {
|
||||||
let [width, height, rowStride, hasAlpha,
|
let [width, height, rowStride, hasAlpha,
|
||||||
bitsPerSample, nChannels, data] = meta['icon-data'];
|
bitsPerSample, nChannels, data] = meta['icon-data'];
|
||||||
@ -110,8 +128,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
|
|
||||||
// Ugh, but we want to fall back to something ...
|
// Ugh, but we want to fall back to something ...
|
||||||
return new St.Icon({ icon_name: 'text-x-generic',
|
return new St.Icon({ icon_name: 'text-x-generic',
|
||||||
icon_size: size,
|
icon_size: size });
|
||||||
icon_type: St.IconType.FULLCOLOR });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getResultsFinished: function(results, error) {
|
_getResultsFinished: function(results, error) {
|
||||||
@ -120,7 +137,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
this.searchSystem.pushResults(this, results[0]);
|
this.searchSystem.pushResults(this, results[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSetAsync: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
this._cancellable.cancel();
|
this._cancellable.cancel();
|
||||||
this._cancellable.reset();
|
this._cancellable.reset();
|
||||||
try {
|
try {
|
||||||
@ -133,7 +150,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSetAsync: function(previousResults, newTerms) {
|
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||||
this._cancellable.cancel();
|
this._cancellable.cancel();
|
||||||
this._cancellable.reset();
|
this._cancellable.reset();
|
||||||
try {
|
try {
|
||||||
@ -164,7 +181,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
callback(resultMetas);
|
callback(resultMetas);
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultMetasAsync: function(ids, callback) {
|
getResultMetas: function(ids, callback) {
|
||||||
this._cancellable.cancel();
|
this._cancellable.cancel();
|
||||||
this._cancellable.reset();
|
this._cancellable.reset();
|
||||||
try {
|
try {
|
||||||
|
895
js/ui/screenShield.js
Normal file
@ -0,0 +1,895 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Cairo = imports.cairo;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const TweenerEquations = imports.tweener.equations;
|
||||||
|
|
||||||
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
|
const LoginManager = imports.misc.loginManager;
|
||||||
|
const Lightbox = imports.ui.lightbox;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Overview = imports.ui.overview;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||||
|
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||||
|
|
||||||
|
const CURTAIN_SLIDE_TIME = 0.5;
|
||||||
|
// fraction of screen height the arrow must reach before completing
|
||||||
|
// the slide up automatically
|
||||||
|
const ARROW_DRAG_THRESHOLD = 0.1;
|
||||||
|
|
||||||
|
// Parameters for the arrow animation
|
||||||
|
const N_ARROWS = 3;
|
||||||
|
const ARROW_ANIMATION_TIME = 0.6;
|
||||||
|
const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
|
||||||
|
|
||||||
|
// The distance in px that the lock screen will move to when pressing
|
||||||
|
// a key that has no effect in the lock screen (bumping it)
|
||||||
|
const BUMP_SIZE = 25;
|
||||||
|
const BUMP_TIME = 0.3;
|
||||||
|
|
||||||
|
const SUMMARY_ICON_SIZE = 48;
|
||||||
|
|
||||||
|
// Lightbox fading times
|
||||||
|
// STANDARD_FADE_TIME is used when the session goes idle, while
|
||||||
|
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
|
||||||
|
const STANDARD_FADE_TIME = 10;
|
||||||
|
const SHORT_FADE_TIME = 0.8;
|
||||||
|
|
||||||
|
const Clock = new Lang.Class({
|
||||||
|
Name: 'ScreenShieldClock',
|
||||||
|
|
||||||
|
CLOCK_FORMAT_KEY: 'clock-format',
|
||||||
|
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
|
||||||
|
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
|
||||||
|
|
||||||
|
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
|
||||||
|
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
|
||||||
|
this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
|
||||||
|
|
||||||
|
this._updateClock();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateClock: function() {
|
||||||
|
this._time.text = this._wallClock.clock;
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
/* Translators: This is a time format for a date in
|
||||||
|
long format */
|
||||||
|
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.actor.destroy();
|
||||||
|
this._wallClock.run_dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const NotificationsBox = new Lang.Class({
|
||||||
|
Name: 'NotificationsBox',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ vertical: true,
|
||||||
|
name: 'screenShieldNotifications',
|
||||||
|
style_class: 'screen-shield-notifications-box' });
|
||||||
|
|
||||||
|
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||||
|
style_class: 'screen-shield-notifications-box' });
|
||||||
|
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
|
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||||
|
style_class: 'screen-shield-notifications-box' });
|
||||||
|
scrollView.add_actor(this._persistentNotificationBox);
|
||||||
|
|
||||||
|
this.actor.add(this._residentNotificationBox, { x_fill: true });
|
||||||
|
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
this._items = [];
|
||||||
|
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
|
||||||
|
this._summaryItemAdded(Main.messageTray, item, true);
|
||||||
|
}));
|
||||||
|
this._updateVisibility();
|
||||||
|
|
||||||
|
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._summaryAddedId) {
|
||||||
|
Main.messageTray.disconnect(this._summaryAddedId);
|
||||||
|
this._summaryAddedId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._items.length; i++)
|
||||||
|
this._removeItem(this._items[i]);
|
||||||
|
this._items = [];
|
||||||
|
|
||||||
|
this.actor.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateVisibility: function() {
|
||||||
|
if (this._residentNotificationBox.get_n_children() > 0) {
|
||||||
|
this.actor.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = this._persistentNotificationBox.get_children();
|
||||||
|
this.actor.visible = children.some(function(a) { return a.visible; });
|
||||||
|
},
|
||||||
|
|
||||||
|
_sourceIsResident: function(source) {
|
||||||
|
return source.hasResidentNotification() && !source.isChat;
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeNotificationCountText: function(count, isChat) {
|
||||||
|
if (isChat)
|
||||||
|
return ngettext("%d new message", "%d new messages", count).format(count);
|
||||||
|
else
|
||||||
|
return ngettext("%d new notification", "%d new notifications", count).format(count);
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeNotificationSource: function(source) {
|
||||||
|
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
|
||||||
|
|
||||||
|
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
|
||||||
|
box.add(sourceActor.actor, { y_fill: true });
|
||||||
|
|
||||||
|
let textBox = new St.BoxLayout({ vertical: true });
|
||||||
|
box.add(textBox);
|
||||||
|
|
||||||
|
let label = new St.Label({ text: source.title,
|
||||||
|
style_class: 'screen-shield-notification-label' });
|
||||||
|
textBox.add(label);
|
||||||
|
|
||||||
|
let count = source.unseenCount;
|
||||||
|
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
|
||||||
|
style_class: 'screen-shield-notification-count-text' });
|
||||||
|
textBox.add(countLabel);
|
||||||
|
|
||||||
|
box.visible = count != 0;
|
||||||
|
return [box, countLabel];
|
||||||
|
},
|
||||||
|
|
||||||
|
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
|
||||||
|
// Ignore transient sources, or sources explicitly marked not to show
|
||||||
|
// in the lock screen
|
||||||
|
if (item.source.isTransient || !item.source.showInLockScreen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let obj = {
|
||||||
|
item: item,
|
||||||
|
source: item.source,
|
||||||
|
resident: this._sourceIsResident(item.source),
|
||||||
|
contentUpdatedId: 0,
|
||||||
|
sourceDestroyId: 0,
|
||||||
|
sourceBox: null,
|
||||||
|
countLabel: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (obj.resident) {
|
||||||
|
this._residentNotificationBox.add(item.notificationStackWidget);
|
||||||
|
item.closeButtonVisible = false;
|
||||||
|
item.prepareNotificationStackForShowing();
|
||||||
|
} else {
|
||||||
|
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
|
||||||
|
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
|
||||||
|
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
|
||||||
|
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
|
||||||
|
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||||
|
this._items.push(obj);
|
||||||
|
|
||||||
|
if (!dontUpdateVisibility)
|
||||||
|
this._updateVisibility();
|
||||||
|
},
|
||||||
|
|
||||||
|
_findSource: function(source) {
|
||||||
|
for (let i = 0; i < this._items.length; i++) {
|
||||||
|
if (this._items[i].source == source)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onItemContentUpdated: function(item) {
|
||||||
|
let obj = this._items[this._findSource(item.source)];
|
||||||
|
this._updateItem(obj);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceChanged: function(source) {
|
||||||
|
let obj = this._items[this._findSource(source)];
|
||||||
|
this._updateItem(obj);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateItem: function(obj) {
|
||||||
|
let itemShouldBeResident = this._sourceIsResident(obj.source);
|
||||||
|
|
||||||
|
if (itemShouldBeResident && obj.resident) {
|
||||||
|
// Nothing to do here, the actor is already updated
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.resident && !itemShouldBeResident) {
|
||||||
|
// make into a regular item
|
||||||
|
obj.item.doneShowingNotificationStack();
|
||||||
|
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
|
||||||
|
|
||||||
|
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
|
||||||
|
this._persistentNotificationBox.add(obj.sourceBox);
|
||||||
|
} else if (itemShouldBeResident && !obj.resident) {
|
||||||
|
// make into a resident item
|
||||||
|
obj.sourceBox.destroy();
|
||||||
|
obj.sourceBox = obj.countLabel = null;
|
||||||
|
obj.resident = true;
|
||||||
|
|
||||||
|
this._residentNotificationBox.add(obj.item.notificationStackWidget);
|
||||||
|
obj.item.closeButtonVisible = false;
|
||||||
|
obj.item.prepareNotificationStackForShowing();
|
||||||
|
} else {
|
||||||
|
// just update the counter
|
||||||
|
let count = obj.source.unseenCount;
|
||||||
|
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
|
||||||
|
obj.sourceBox.visible = count != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateVisibility();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceDestroy: function(source) {
|
||||||
|
let idx = this._findSource(source);
|
||||||
|
|
||||||
|
this._removeItem(this._items[idx]);
|
||||||
|
this._items.splice(idx, 1);
|
||||||
|
|
||||||
|
this._updateVisibility();
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeItem: function(obj) {
|
||||||
|
if (obj.resident) {
|
||||||
|
obj.item.doneShowingNotificationStack();
|
||||||
|
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
|
||||||
|
} else {
|
||||||
|
obj.sourceBox.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.item.disconnect(obj.contentUpdatedId);
|
||||||
|
obj.source.disconnect(obj.sourceDestroyId);
|
||||||
|
obj.source.disconnect(obj.sourceCountChangedId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Arrow = new Lang.Class({
|
||||||
|
Name: 'Arrow',
|
||||||
|
Extends: St.Bin,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
this.x_fill = this.y_fill = true;
|
||||||
|
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||||
|
|
||||||
|
this._drawingArea = new St.DrawingArea();
|
||||||
|
this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
|
||||||
|
this.child = this._drawingArea;
|
||||||
|
|
||||||
|
this._shadowHelper = null;
|
||||||
|
this._shadowWidth = this._shadowHeight = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_drawArrow: function(arrow) {
|
||||||
|
let cr = arrow.get_context();
|
||||||
|
let [w, h] = arrow.get_surface_size();
|
||||||
|
let node = this.get_theme_node();
|
||||||
|
let thickness = node.get_length('-arrow-thickness');
|
||||||
|
|
||||||
|
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
||||||
|
|
||||||
|
cr.setLineCap(Cairo.LineCap.ROUND);
|
||||||
|
cr.setLineWidth(thickness);
|
||||||
|
|
||||||
|
cr.moveTo(thickness / 2, h - thickness / 2);
|
||||||
|
cr.lineTo(w/2, thickness);
|
||||||
|
cr.lineTo(w - thickness / 2, h - thickness / 2);
|
||||||
|
cr.stroke();
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_style_changed: function() {
|
||||||
|
let node = this.get_theme_node();
|
||||||
|
this._shadow = node.get_shadow('-arrow-shadow');
|
||||||
|
if (this._shadow)
|
||||||
|
this._shadowHelper = St.ShadowHelper.new(this._shadow);
|
||||||
|
else
|
||||||
|
this._shadowHelper = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_paint: function() {
|
||||||
|
if (this._shadowHelper) {
|
||||||
|
this._shadowHelper.update(this._drawingArea);
|
||||||
|
|
||||||
|
let allocation = this._drawingArea.get_allocation_box();
|
||||||
|
let paintOpacity = this._drawingArea.get_paint_opacity();
|
||||||
|
this._shadowHelper.paint(allocation, paintOpacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._drawingArea.paint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To test screen shield, make sure to kill gnome-screensaver.
|
||||||
|
*
|
||||||
|
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
|
||||||
|
* rather than through System Settings, you also need to set
|
||||||
|
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
|
||||||
|
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
|
||||||
|
* This will ensure that the screen blanks at the right time when it fades out.
|
||||||
|
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
|
||||||
|
*/
|
||||||
|
const ScreenShield = new Lang.Class({
|
||||||
|
Name: 'ScreenShield',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.actor = Main.layoutManager.screenShieldGroup;
|
||||||
|
|
||||||
|
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||||
|
this._lockScreenGroup = new St.Widget({ x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
name: 'lockScreenGroup',
|
||||||
|
});
|
||||||
|
this._lockScreenGroup.connect('key-release-event',
|
||||||
|
Lang.bind(this, this._onLockScreenKeyRelease));
|
||||||
|
this._lockScreenGroup.connect('scroll-event',
|
||||||
|
Lang.bind(this, this._onLockScreenScroll));
|
||||||
|
|
||||||
|
this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||||
|
name: 'lockScreenContents' });
|
||||||
|
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||||
|
|
||||||
|
this._background = new St.Bin({ style_class: 'screen-shield-background',
|
||||||
|
child: Meta.BackgroundActor.new_for_screen(global.screen) });
|
||||||
|
this._lockScreenGroup.add_actor(this._background);
|
||||||
|
this._lockScreenGroup.add_actor(this._lockScreenContents);
|
||||||
|
|
||||||
|
this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
|
||||||
|
vertical: true,
|
||||||
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
|
y_align: Clutter.ActorAlign.END,
|
||||||
|
// HACK: without these, ClutterBinLayout
|
||||||
|
// ignores alignment properties on the actor
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true });
|
||||||
|
|
||||||
|
for (let i = 0; i < N_ARROWS; i++) {
|
||||||
|
let arrow = new Arrow({ opacity: 0 });
|
||||||
|
this._arrowContainer.add_actor(arrow);
|
||||||
|
}
|
||||||
|
this._lockScreenContents.add_actor(this._arrowContainer);
|
||||||
|
|
||||||
|
let dragArea = new Clutter.Rect({ origin: new Clutter.Point({ x: 0, y: -global.screen_height, }),
|
||||||
|
size: new Clutter.Size({ width: global.screen_width,
|
||||||
|
height: global.screen_height }) });
|
||||||
|
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS,
|
||||||
|
drag_area: dragArea });
|
||||||
|
action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||||
|
action.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||||
|
this._lockScreenGroup.add_action(action);
|
||||||
|
|
||||||
|
this._lockDialogGroup = new St.Widget({ x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
|
||||||
|
name: 'lockDialogGroup' });
|
||||||
|
|
||||||
|
this.actor.add_actor(this._lockDialogGroup);
|
||||||
|
this.actor.add_actor(this._lockScreenGroup);
|
||||||
|
|
||||||
|
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
|
||||||
|
if (error) {
|
||||||
|
logError(error, 'Error while reading gnome-session presence');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onStatusChanged(proxy.status);
|
||||||
|
}));
|
||||||
|
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
|
||||||
|
this._onStatusChanged(status);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||||
|
|
||||||
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
|
this._loginSession = this._loginManager.getCurrentSessionProxy();
|
||||||
|
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
|
||||||
|
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
|
||||||
|
|
||||||
|
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||||
|
|
||||||
|
this._isModal = false;
|
||||||
|
this._hasLockScreen = false;
|
||||||
|
this._isGreeter = false;
|
||||||
|
this._isActive = false;
|
||||||
|
this._inUnlockAnimation = false;
|
||||||
|
|
||||||
|
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||||
|
{ inhibitEvents: true,
|
||||||
|
fadeInTime: STANDARD_FADE_TIME,
|
||||||
|
fadeFactor: 1 });
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLockScreenKeyRelease: function(actor, event) {
|
||||||
|
let symbol = event.get_key_symbol();
|
||||||
|
|
||||||
|
if (symbol == Clutter.KEY_Escape ||
|
||||||
|
symbol == Clutter.KEY_Return ||
|
||||||
|
symbol == Clutter.KEY_KP_Enter) {
|
||||||
|
this._ensureUnlockDialog();
|
||||||
|
this._hideLockScreen(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't bump if the lock screen is not showing or is
|
||||||
|
// animating, as the bump overrides the animation and would
|
||||||
|
// remove any onComplete handler
|
||||||
|
if (this._lockScreenState == MessageTray.State.SHOWN)
|
||||||
|
this._bumpLockScreen();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLockScreenScroll: function(actor, event) {
|
||||||
|
if (this._lockScreenState != MessageTray.State.SHOWN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let delta = 0;
|
||||||
|
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
|
||||||
|
delta = 5;
|
||||||
|
else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
|
||||||
|
delta = Math.max(0, event.get_scroll_delta()[0]);
|
||||||
|
|
||||||
|
this._lockScreenScrollCounter += delta;
|
||||||
|
|
||||||
|
// 7 standard scrolls to lift up
|
||||||
|
if (this._lockScreenScrollCounter > 35) {
|
||||||
|
this._ensureUnlockDialog();
|
||||||
|
this._hideLockScreen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_animateArrows: function() {
|
||||||
|
let arrows = this._arrowContainer.get_children();
|
||||||
|
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
|
||||||
|
let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
|
||||||
|
for (let i = 0; i < arrows.length; i++) {
|
||||||
|
arrows.opacity = 0;
|
||||||
|
Tweener.addTween(arrows[i],
|
||||||
|
{ opacity: 0,
|
||||||
|
delay: unitaryDelay * (N_ARROWS - (i + 1)),
|
||||||
|
time: ARROW_ANIMATION_TIME,
|
||||||
|
transition: function(t, b, c, d) {
|
||||||
|
if (t < d/2)
|
||||||
|
return TweenerEquations.easeOutQuad(t, 0, maxOpacity, d/2);
|
||||||
|
else
|
||||||
|
return TweenerEquations.easeInQuad(t - d/2, maxOpacity, -maxOpacity, d/2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragBegin: function() {
|
||||||
|
Tweener.removeTweens(this._lockScreenGroup);
|
||||||
|
this._lockScreenState = MessageTray.State.HIDING;
|
||||||
|
this._ensureUnlockDialog();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
|
||||||
|
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
|
||||||
|
// Complete motion automatically
|
||||||
|
this._hideLockScreen(true);
|
||||||
|
} else {
|
||||||
|
// restore the lock screen to its original place
|
||||||
|
// try to use the same speed as the normal animation
|
||||||
|
let h = global.stage.height;
|
||||||
|
let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
|
||||||
|
Tweener.removeTweens(this._lockScreenGroup);
|
||||||
|
Tweener.addTween(this._lockScreenGroup,
|
||||||
|
{ y: 0,
|
||||||
|
time: time,
|
||||||
|
transition: 'linear',
|
||||||
|
onComplete: function() {
|
||||||
|
this._lockScreenGroup.fixed_position_set = false;
|
||||||
|
this._lockScreenState = MessageTray.State.SHOWN;
|
||||||
|
},
|
||||||
|
onCompleteScope: this,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we have a unlock dialog, cancel it
|
||||||
|
if (this._dialog) {
|
||||||
|
this._dialog.cancel();
|
||||||
|
if (!this._isGreeter) {
|
||||||
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStatusChanged: function(status) {
|
||||||
|
if (status == GnomeSession.PresenceStatus.IDLE) {
|
||||||
|
if (this._dialog) {
|
||||||
|
this._dialog.cancel();
|
||||||
|
if (!this._isGreeter) {
|
||||||
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._isModal) {
|
||||||
|
Main.pushModal(this.actor);
|
||||||
|
this._isModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._isActive)
|
||||||
|
this._lightbox.show();
|
||||||
|
} else {
|
||||||
|
let lightboxWasShown = this._lightbox.shown;
|
||||||
|
this._lightbox.hide();
|
||||||
|
|
||||||
|
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
|
||||||
|
if (shouldLock || this._isActive) {
|
||||||
|
this.lock(false);
|
||||||
|
} else if (this._isModal) {
|
||||||
|
this.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showDialog: function() {
|
||||||
|
// Ensure that the stage window is mapped, before taking a grab
|
||||||
|
// otherwise X errors out
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||||
|
if (!this._isModal) {
|
||||||
|
Main.pushModal(this.actor);
|
||||||
|
this._isModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.actor.show();
|
||||||
|
this._isGreeter = Main.sessionMode.isGreeter;
|
||||||
|
this._ensureUnlockDialog();
|
||||||
|
this._hideLockScreen(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_bumpLockScreen: function() {
|
||||||
|
Tweener.removeTweens(this._lockScreenGroup);
|
||||||
|
Tweener.addTween(this._lockScreenGroup,
|
||||||
|
{ y: -BUMP_SIZE,
|
||||||
|
time: BUMP_TIME / 2,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
Tweener.addTween(this,
|
||||||
|
{ y: 0,
|
||||||
|
time: BUMP_TIME / 2,
|
||||||
|
transition: 'easeInQuad' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideLockScreen: function(animate) {
|
||||||
|
this._lockScreenState = MessageTray.State.HIDING;
|
||||||
|
|
||||||
|
if (animate) {
|
||||||
|
// Tween the lock screen out of screen
|
||||||
|
// try to use the same speed regardless of original position
|
||||||
|
let h = global.stage.height;
|
||||||
|
let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
|
||||||
|
Tweener.removeTweens(this._lockScreenGroup);
|
||||||
|
Tweener.addTween(this._lockScreenGroup,
|
||||||
|
{ y: -h,
|
||||||
|
time: time,
|
||||||
|
transition: 'linear',
|
||||||
|
onComplete: function() {
|
||||||
|
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||||
|
this._lockScreenGroup.hide();
|
||||||
|
},
|
||||||
|
onCompleteScope: this,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||||
|
this._lockScreenGroup.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||||
|
Main.sessionMode.popMode('lock-screen');
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureUnlockDialog: function() {
|
||||||
|
if (!this._dialog) {
|
||||||
|
let constructor = Main.sessionMode.unlockDialog;
|
||||||
|
this._dialog = new constructor(this._lockDialogGroup);
|
||||||
|
if (!this._dialog) {
|
||||||
|
// This session mode has no locking capabilities
|
||||||
|
this.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._dialog.connect('loaded', Lang.bind(this, function() {
|
||||||
|
if (!this._dialog.open()) {
|
||||||
|
log('Could not open login dialog: failed to acquire grab');
|
||||||
|
this.unlock();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||||
|
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUnlockFailed: function() {
|
||||||
|
this._resetLockScreen(true, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUnlockSucceded: function() {
|
||||||
|
this._tweenUnlocked();
|
||||||
|
},
|
||||||
|
|
||||||
|
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||||
|
this._ensureLockScreen();
|
||||||
|
this._lockDialogGroup.scale_x = 1;
|
||||||
|
this._lockDialogGroup.scale_y = 1;
|
||||||
|
|
||||||
|
this._lockScreenGroup.show();
|
||||||
|
this._lockScreenState = MessageTray.State.SHOWING;
|
||||||
|
|
||||||
|
if (animateLockScreen) {
|
||||||
|
this._lockScreenGroup.y = -global.screen_height;
|
||||||
|
Tweener.removeTweens(this._lockScreenGroup);
|
||||||
|
Tweener.addTween(this._lockScreenGroup,
|
||||||
|
{ y: 0,
|
||||||
|
time: SHORT_FADE_TIME,
|
||||||
|
transition: 'linear',
|
||||||
|
onComplete: function() {
|
||||||
|
this._lockScreenShown();
|
||||||
|
},
|
||||||
|
onCompleteScope: this
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._lockScreenGroup.fixed_position_set = false;
|
||||||
|
this._lockScreenShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animateLockDialog) {
|
||||||
|
this._lockDialogGroup.opacity = 0;
|
||||||
|
Tweener.removeTweens(this._lockDialogGroup);
|
||||||
|
Tweener.addTween(this._lockDialogGroup,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: SHORT_FADE_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
} else {
|
||||||
|
this._lockDialogGroup.opacity = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lockScreenGroup.grab_key_focus();
|
||||||
|
|
||||||
|
if (Main.sessionMode.currentMode != 'lock-screen')
|
||||||
|
Main.sessionMode.pushMode('lock-screen');
|
||||||
|
},
|
||||||
|
|
||||||
|
_lockScreenShown: function() {
|
||||||
|
if (this._dialog && !this._isGreeter) {
|
||||||
|
this._dialog.destroy();
|
||||||
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._arrowAnimationId)
|
||||||
|
Mainloop.source_remove(this._arrowAnimationId);
|
||||||
|
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
|
||||||
|
this._animateArrows();
|
||||||
|
|
||||||
|
this._lockScreenState = MessageTray.State.SHOWN;
|
||||||
|
this._lockScreenGroup.fixed_position_set = false;
|
||||||
|
this._lockScreenScrollCounter = 0;
|
||||||
|
|
||||||
|
this.emit('lock-screen-shown');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Some of the actors in the lock screen are heavy in
|
||||||
|
// resources, so we only create them when needed
|
||||||
|
_ensureLockScreen: function() {
|
||||||
|
if (this._hasLockScreen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
|
||||||
|
y_align: Clutter.ActorAlign.CENTER,
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
vertical: true });
|
||||||
|
this._clock = new Clock();
|
||||||
|
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
|
||||||
|
y_fill: true });
|
||||||
|
|
||||||
|
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
|
||||||
|
|
||||||
|
if (this._settings.get_boolean('show-notifications')) {
|
||||||
|
this._notificationsBox = new NotificationsBox();
|
||||||
|
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
expand: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hasLockScreen = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearLockScreen: function() {
|
||||||
|
this._clock.destroy();
|
||||||
|
this._clock = null;
|
||||||
|
|
||||||
|
if (this._notificationsBox) {
|
||||||
|
this._notificationsBox.destroy();
|
||||||
|
this._notificationsBox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lockScreenContentsBox.destroy();
|
||||||
|
|
||||||
|
if (this._arrowAnimationId) {
|
||||||
|
Mainloop.source_remove(this._arrowAnimationId);
|
||||||
|
this._arrowAnimationId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hasLockScreen = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get locked() {
|
||||||
|
return this._isActive;
|
||||||
|
},
|
||||||
|
|
||||||
|
_tweenUnlocked: function() {
|
||||||
|
this._inUnlockAnimation = true;
|
||||||
|
this.unlock();
|
||||||
|
Tweener.addTween(this._lockDialogGroup, {
|
||||||
|
scale_x: 0,
|
||||||
|
scale_y: 0,
|
||||||
|
time: Overview.ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
if (this._dialog) {
|
||||||
|
this._dialog.destroy();
|
||||||
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
this.actor.hide();
|
||||||
|
this._inUnlockAnimation = false;
|
||||||
|
},
|
||||||
|
onCompleteScope: this
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
unlock: function() {
|
||||||
|
if (this._hasLockScreen)
|
||||||
|
this._clearLockScreen();
|
||||||
|
|
||||||
|
if (this._dialog && !this._isGreeter) {
|
||||||
|
this._dialog.destroy();
|
||||||
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lightbox.hide();
|
||||||
|
|
||||||
|
if (this._isModal) {
|
||||||
|
Main.popModal(this.actor);
|
||||||
|
this._isModal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._inUnlockAnimation)
|
||||||
|
this.actor.hide();
|
||||||
|
|
||||||
|
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||||
|
Main.sessionMode.popMode('lock-screen');
|
||||||
|
if (Main.sessionMode.currentMode == 'unlock-dialog')
|
||||||
|
Main.sessionMode.popMode('unlock-dialog');
|
||||||
|
|
||||||
|
this._isActive = false;
|
||||||
|
this.emit('lock-status-changed');
|
||||||
|
},
|
||||||
|
|
||||||
|
lock: function(animate) {
|
||||||
|
if (!this._isModal) {
|
||||||
|
Main.pushModal(this.actor);
|
||||||
|
this._isModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actor.show();
|
||||||
|
|
||||||
|
if (Main.sessionMode.currentMode != 'unlock-dialog' &&
|
||||||
|
Main.sessionMode.currentMode != 'lock-screen') {
|
||||||
|
this._isGreeter = Main.sessionMode.isGreeter;
|
||||||
|
if (!this._isGreeter)
|
||||||
|
Main.sessionMode.pushMode('unlock-dialog');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._resetLockScreen(animate, animate);
|
||||||
|
|
||||||
|
this._isActive = true;
|
||||||
|
this.emit('lock-status-changed');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ScreenShield.prototype);
|
||||||
|
|
||||||
|
/* Fallback code to handle session locking using gnome-screensaver,
|
||||||
|
in case the required GDM dependency is not there
|
||||||
|
*/
|
||||||
|
const ScreenShieldFallback = new Lang.Class({
|
||||||
|
Name: 'ScreenShieldFallback',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||||
|
g_name: 'org.gnome.ScreenSaver',
|
||||||
|
g_object_path: '/org/gnome/ScreenSaver',
|
||||||
|
g_interface_name: 'org.gnome.ScreenSaver',
|
||||||
|
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||||
|
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
|
||||||
|
});
|
||||||
|
this._proxy.init(null);
|
||||||
|
|
||||||
|
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
|
||||||
|
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNameOwnerChanged: function(object, pspec) {
|
||||||
|
if (this._proxy.g_name_owner)
|
||||||
|
[this._locked] = this._proxy.call_sync('GetActive', null,
|
||||||
|
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
|
||||||
|
else
|
||||||
|
this._locked = false;
|
||||||
|
|
||||||
|
this.emit('lock-status-changed', this._locked);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSignal: function(proxy, senderName, signalName, params) {
|
||||||
|
if (signalName == 'ActiveChanged') {
|
||||||
|
[this._locked] = params.deep_unpack();
|
||||||
|
this.emit('lock-status-changed', this._locked);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get locked() {
|
||||||
|
return this._locked;
|
||||||
|
},
|
||||||
|
|
||||||
|
lock: function() {
|
||||||
|
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
|
||||||
|
Lang.bind(this, function(proxy, result) {
|
||||||
|
proxy.call_finish(result);
|
||||||
|
|
||||||
|
this.emit('lock-screen-shown');
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
unlock: function() {
|
||||||
|
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
|
||||||
|
Gio.DBusCallFlags.NONE, -1, null, null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ScreenShieldFallback.prototype);
|
199
js/ui/search.js
@ -10,17 +10,13 @@ const Util = imports.misc.util;
|
|||||||
const FileUtils = imports.misc.fileUtils;
|
const FileUtils = imports.misc.fileUtils;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
|
|
||||||
|
|
||||||
// Not currently referenced by the search API, but
|
// Not currently referenced by the search API, but
|
||||||
// this enumeration can be useful for provider
|
// this enumeration can be useful for provider
|
||||||
// implementations.
|
// implementations.
|
||||||
const MatchType = {
|
const MatchType = {
|
||||||
NONE: 0,
|
NONE: 0,
|
||||||
SUBSTRING: 1,
|
SUBSTRING: 1,
|
||||||
MULTIPLE_SUBSTRING: 2,
|
PREFIX: 2
|
||||||
PREFIX: 3,
|
|
||||||
MULTIPLE_PREFIX: 4
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const SearchResultDisplay = new Lang.Class({
|
const SearchResultDisplay = new Lang.Class({
|
||||||
@ -53,7 +49,7 @@ const SearchResultDisplay = new Lang.Class({
|
|||||||
* Remove all results from this display.
|
* Remove all results from this display.
|
||||||
*/
|
*/
|
||||||
clear: function() {
|
clear: function() {
|
||||||
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
this.actor.destroy_all_children();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,11 +68,8 @@ const SearchResultDisplay = new Lang.Class({
|
|||||||
* Subclass this object to add a new result type
|
* Subclass this object to add a new result type
|
||||||
* to the search system, then call registerProvider()
|
* to the search system, then call registerProvider()
|
||||||
* in SearchSystem with an instance.
|
* in SearchSystem with an instance.
|
||||||
* By default, search is synchronous and uses the
|
* Search is asynchronous and uses the
|
||||||
* getInitialResultSet()/getSubsearchResultSet() methods.
|
* getInitialResultSet()/getSubsearchResultSet() methods.
|
||||||
* For asynchronous search, set the async property to true
|
|
||||||
* and implement getInitialResultSetAsync()/getSubsearchResultSetAsync()
|
|
||||||
* instead.
|
|
||||||
*/
|
*/
|
||||||
const SearchProvider = new Lang.Class({
|
const SearchProvider = new Lang.Class({
|
||||||
Name: 'SearchProvider',
|
Name: 'SearchProvider',
|
||||||
@ -84,7 +77,6 @@ const SearchProvider = new Lang.Class({
|
|||||||
_init: function(title) {
|
_init: function(title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.searchSystem = null;
|
this.searchSystem = null;
|
||||||
this.async = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +87,7 @@ const SearchProvider = new Lang.Class({
|
|||||||
* therefore a single term of length one or two), or when
|
* therefore a single term of length one or two), or when
|
||||||
* a new term is added.
|
* a new term is added.
|
||||||
*
|
*
|
||||||
* Should return an array of result identifier strings representing
|
* Should "return" an array of result identifier strings representing
|
||||||
* items which match the given search terms. This
|
* items which match the given search terms. This
|
||||||
* is expected to be a substring match on the metadata for a given
|
* 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,
|
* item. Ordering of returned results is up to the discretion of the provider,
|
||||||
@ -105,6 +97,9 @@ const SearchProvider = new Lang.Class({
|
|||||||
* description) before single matches
|
* description) before single matches
|
||||||
* * Put items which match on a prefix before non-prefix substring 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
|
* This function should be fast; do not perform unindexed full-text searches
|
||||||
* or network queries.
|
* or network queries.
|
||||||
*/
|
*/
|
||||||
@ -112,18 +107,6 @@ const SearchProvider = new Lang.Class({
|
|||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getInitialResultSetAsync:
|
|
||||||
* @terms: Array of search terms, treated as logical AND
|
|
||||||
*
|
|
||||||
* Like getInitialResultSet(), but the method should return immediately
|
|
||||||
* without a return value - use SearchSystem.pushResults() when the
|
|
||||||
* corresponding results are ready.
|
|
||||||
*/
|
|
||||||
getInitialResultSetAsync: function(terms) {
|
|
||||||
throw new Error('Not implemented');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getSubsearchResultSet:
|
* getSubsearchResultSet:
|
||||||
* @previousResults: Array of item identifiers
|
* @previousResults: Array of item identifiers
|
||||||
@ -136,62 +119,26 @@ const SearchProvider = new Lang.Class({
|
|||||||
*
|
*
|
||||||
* This allows search providers to only search through the previous
|
* This allows search providers to only search through the previous
|
||||||
* result set, rather than possibly performing a full re-query.
|
* 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) {
|
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getSubsearchResultSetAsync:
|
|
||||||
* @previousResults: Array of item identifiers
|
|
||||||
* @newTerms: Updated search terms
|
|
||||||
*
|
|
||||||
* Like getSubsearchResultSet(), but the method should return immediately
|
|
||||||
* without a return value - use SearchSystem.pushResults() when the
|
|
||||||
* corresponding results are ready.
|
|
||||||
*/
|
|
||||||
getSubsearchResultSetAsync: function(previousResults, newTerms) {
|
|
||||||
throw new Error('Not implemented');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getResultMetas:
|
* getResultMetas:
|
||||||
* @ids: Result identifier strings
|
* @ids: Result identifier strings
|
||||||
*
|
*
|
||||||
* Return an array of objects with 'id', 'name', (both strings) and
|
* Call callback with array of objects with 'id', 'name', (both strings) and
|
||||||
* 'createIcon' (function(size) returning a Clutter.Texture) properties
|
* 'createIcon' (function(size) returning a Clutter.Texture) properties
|
||||||
* with the same number of members as @ids
|
* with the same number of members as @ids
|
||||||
*/
|
*/
|
||||||
getResultMetas: function(ids) {
|
getResultMetas: function(ids, callback) {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getResultMetasAsync:
|
|
||||||
* @ids: Result identifier strings
|
|
||||||
* @callback: callback to pass the results to when ready
|
|
||||||
*
|
|
||||||
* Like getResultMetas(), but the method should return immediately
|
|
||||||
* without a return value - pass the results to the provided @callback
|
|
||||||
* when ready.
|
|
||||||
*/
|
|
||||||
getResultMetasAsync: 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:
|
* createResultActor:
|
||||||
* @resultMeta: Object with result metadata
|
* @resultMeta: Object with result metadata
|
||||||
@ -220,99 +167,6 @@ const SearchProvider = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(SearchProvider.prototype);
|
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 SearchSystem = new Lang.Class({
|
const SearchSystem = new Lang.Class({
|
||||||
Name: 'SearchSystem',
|
Name: 'SearchSystem',
|
||||||
|
|
||||||
@ -379,42 +233,33 @@ const SearchSystem = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let previousResultsArr = this._previousResults;
|
||||||
|
|
||||||
let results = [];
|
let results = [];
|
||||||
|
this._previousTerms = terms;
|
||||||
|
this._previousResults = results;
|
||||||
|
|
||||||
if (isSubSearch) {
|
if (isSubSearch) {
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
let [provider, previousResults] = this._previousResults[i];
|
let [provider, previousResults] = previousResultsArr[i];
|
||||||
try {
|
try {
|
||||||
if (provider.async) {
|
|
||||||
provider.getSubsearchResultSetAsync(previousResults, terms);
|
|
||||||
results.push([provider, []]);
|
results.push([provider, []]);
|
||||||
} else {
|
provider.getSubsearchResultSet(previousResults, terms);
|
||||||
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
|
||||||
results.push([provider, providerResults]);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
let provider = this._providers[i];
|
let provider = this._providers[i];
|
||||||
try {
|
try {
|
||||||
if (provider.async) {
|
|
||||||
provider.getInitialResultSetAsync(terms);
|
|
||||||
results.push([provider, []]);
|
results.push([provider, []]);
|
||||||
} else {
|
provider.getInitialResultSet(terms);
|
||||||
let providerResults = provider.getInitialResultSet(terms);
|
|
||||||
results.push([provider, providerResults]);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._previousTerms = terms;
|
|
||||||
this._previousResults = results;
|
|
||||||
this.emit('search-completed', results);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(SearchSystem.prototype);
|
Signals.addSignalMethods(SearchSystem.prototype);
|
||||||
|
@ -5,6 +5,7 @@ const Lang = imports.lang;
|
|||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const IconGrid = imports.ui.iconGrid;
|
const IconGrid = imports.ui.iconGrid;
|
||||||
@ -33,12 +34,13 @@ const SearchResult = new Lang.Class({
|
|||||||
content = new St.Bin({ style_class: 'search-result-content',
|
content = new St.Bin({ style_class: 'search-result-content',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
track_hover: true });
|
track_hover: true,
|
||||||
|
accessible_role: Atk.Role.PUSH_BUTTON });
|
||||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||||
{ createIcon: this.metaInfo['createIcon'] });
|
{ createIcon: this.metaInfo['createIcon'] });
|
||||||
content.set_child(icon.actor);
|
content.set_child(icon.actor);
|
||||||
this._dragActorSource = icon.icon;
|
this._dragActorSource = icon.icon;
|
||||||
this.actor.label_actor = icon.label;
|
content.label_actor = icon.label;
|
||||||
} else {
|
} else {
|
||||||
if (content._delegate && content._delegate.getDragActorSource)
|
if (content._delegate && content._delegate.getDragActorSource)
|
||||||
this._dragActorSource = content._delegate.getDragActorSource();
|
this._dragActorSource = content._delegate.getDragActorSource();
|
||||||
@ -119,13 +121,7 @@ const GridSearchResults = new Lang.Class({
|
|||||||
if (results.length == 0)
|
if (results.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (provider.async) {
|
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
|
||||||
provider.getResultMetasAsync(results,
|
|
||||||
Lang.bind(this, this.renderResults));
|
|
||||||
} else {
|
|
||||||
let metas = provider.getResultMetas(results);
|
|
||||||
this.renderResults(metas);
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
this._notDisplayedResult = [];
|
this._notDisplayedResult = [];
|
||||||
@ -135,7 +131,7 @@ const GridSearchResults = new Lang.Class({
|
|||||||
|
|
||||||
getResultsForDisplay: function() {
|
getResultsForDisplay: function() {
|
||||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||||
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
|
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
|
||||||
- alreadyVisible;
|
- alreadyVisible;
|
||||||
|
|
||||||
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
|
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
|
||||||
@ -177,11 +173,9 @@ const GridSearchResults = new Lang.Class({
|
|||||||
const SearchResults = new Lang.Class({
|
const SearchResults = new Lang.Class({
|
||||||
Name: 'SearchResults',
|
Name: 'SearchResults',
|
||||||
|
|
||||||
_init: function(searchSystem, openSearchSystem) {
|
_init: function(searchSystem) {
|
||||||
this._searchSystem = searchSystem;
|
this._searchSystem = searchSystem;
|
||||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
|
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||||
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
|
|
||||||
this._openSearchSystem = openSearchSystem;
|
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
@ -196,7 +190,7 @@ const SearchResults = new Lang.Class({
|
|||||||
scrollView.add_actor(this._content);
|
scrollView.add_actor(this._content);
|
||||||
|
|
||||||
this.actor.add(scrollView, { x_fill: true,
|
this.actor.add(scrollView, { x_fill: true,
|
||||||
y_fill: false,
|
y_fill: true,
|
||||||
expand: true,
|
expand: true,
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
@ -211,87 +205,37 @@ const SearchResults = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
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._providers = this._searchSystem.getProviders();
|
||||||
this._providerMeta = [];
|
this._providerMeta = [];
|
||||||
this._providerMetaResults = {};
|
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
this.createProviderMeta(this._providers[i]);
|
this.createProviderMeta(this._providers[i]);
|
||||||
this._providerMetaResults[this.providers[i].title] = [];
|
|
||||||
}
|
}
|
||||||
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._highlightDefault = false;
|
||||||
this._defaultResult = null;
|
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);
|
|
||||||
},
|
|
||||||
|
|
||||||
createProviderMeta: function(provider) {
|
createProviderMeta: function(provider) {
|
||||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
let title = new St.Label({ style_class: 'search-section-header',
|
let title = new St.Label({ style_class: 'search-section-header',
|
||||||
text: provider.title });
|
text: provider.title });
|
||||||
providerBox.add(title);
|
providerBox.add(title, { x_fill: false, x_align: St.Align.START });
|
||||||
|
|
||||||
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
providerBox.add(resultDisplayBin, { expand: true });
|
providerBox.add(resultDisplayBin, { expand: true });
|
||||||
let resultDisplay = provider.createResultContainerActor();
|
let resultDisplay = new GridSearchResults(provider);
|
||||||
if (resultDisplay == null) {
|
|
||||||
resultDisplay = new GridSearchResults(provider);
|
|
||||||
}
|
|
||||||
resultDisplayBin.set_child(resultDisplay.actor);
|
resultDisplayBin.set_child(resultDisplay.actor);
|
||||||
|
|
||||||
this._providerMeta.push({ provider: provider,
|
this._providerMeta.push({ provider: provider,
|
||||||
actor: providerBox,
|
actor: providerBox,
|
||||||
resultDisplay: resultDisplay,
|
resultDisplay: resultDisplay });
|
||||||
hasPendingResults: false });
|
|
||||||
this._content.add(providerBox);
|
this._content.add(providerBox);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -307,7 +251,6 @@ const SearchResults = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_clearDisplay: function() {
|
_clearDisplay: function() {
|
||||||
this._visibleResultsCount = 0;
|
|
||||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
for (let i = 0; i < this._providerMeta.length; i++) {
|
||||||
let meta = this._providerMeta[i];
|
let meta = this._providerMeta[i];
|
||||||
meta.resultDisplay.clear();
|
meta.resultDisplay.clear();
|
||||||
@ -323,14 +266,14 @@ const SearchResults = new Lang.Class({
|
|||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this._searchSystem.reset();
|
this._searchSystem.reset();
|
||||||
this._statusText.hide();
|
this._statusBin.hide();
|
||||||
this._clearDisplay();
|
this._clearDisplay();
|
||||||
},
|
},
|
||||||
|
|
||||||
startingSearch: function() {
|
startingSearch: function() {
|
||||||
this.reset();
|
this.reset();
|
||||||
this._statusText.set_text(_("Searching..."));
|
this._statusText.set_text(_("Searching..."));
|
||||||
this._statusText.show();
|
this._statusBin.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
doSearch: function (searchString) {
|
doSearch: function (searchString) {
|
||||||
@ -346,8 +289,6 @@ const SearchResults = new Lang.Class({
|
|||||||
|
|
||||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
for (let i = 0; i < this._providerMeta.length; i++) {
|
||||||
let meta = this._providerMeta[i];
|
let meta = this._providerMeta[i];
|
||||||
if (meta.hasPendingResults)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!meta.actor.visible)
|
if (!meta.actor.visible)
|
||||||
continue;
|
continue;
|
||||||
@ -359,9 +300,6 @@ const SearchResults = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newDefaultResult)
|
|
||||||
newDefaultResult = this._searchProvidersBox.get_first_child();
|
|
||||||
|
|
||||||
if (newDefaultResult != this._defaultResult) {
|
if (newDefaultResult != this._defaultResult) {
|
||||||
if (this._defaultResult)
|
if (this._defaultResult)
|
||||||
this._defaultResult.setSelected(false);
|
this._defaultResult.setSelected(false);
|
||||||
@ -372,71 +310,57 @@ const SearchResults = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateCurrentResults: function(searchSystem, results) {
|
_updateStatusText: function () {
|
||||||
let terms = searchSystem.getTerms();
|
let haveResults = false;
|
||||||
let [provider, providerResults] = results;
|
|
||||||
let meta = this._metaForProvider(provider);
|
|
||||||
meta.hasPendingResults = false;
|
|
||||||
this._updateProviderResults(provider, providerResults, terms);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateProviderResults: function(provider, providerResults, terms) {
|
for (let i = 0; i < this._providerMeta.length; ++i)
|
||||||
let meta = this._metaForProvider(provider);
|
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
|
||||||
if (providerResults.length == 0) {
|
haveResults = true;
|
||||||
this._clearDisplayForProvider(provider);
|
break;
|
||||||
meta.resultDisplay.setResults([], []);
|
|
||||||
} else {
|
|
||||||
this._providerMetaResults[provider.title] = providerResults;
|
|
||||||
meta.resultDisplay.setResults(providerResults, terms);
|
|
||||||
let results = meta.resultDisplay.getResultsForDisplay();
|
|
||||||
|
|
||||||
if (provider.async) {
|
|
||||||
provider.getResultMetasAsync(results, Lang.bind(this,
|
|
||||||
function(metas) {
|
|
||||||
this._clearDisplayForProvider(provider);
|
|
||||||
meta.actor.show();
|
|
||||||
this._content.hide();
|
|
||||||
meta.resultDisplay.renderResults(metas);
|
|
||||||
this._maybeSetInitialSelection();
|
|
||||||
this._content.show();
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
let metas = provider.getResultMetas(results);
|
|
||||||
this._clearDisplayForProvider(provider);
|
|
||||||
meta.actor.show();
|
|
||||||
meta.resultDisplay.renderResults(metas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!haveResults) {
|
||||||
|
this._statusText.set_text(_("No results."));
|
||||||
|
this._statusBin.show();
|
||||||
|
} else {
|
||||||
|
this._statusBin.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateResults: function(searchSystem, results) {
|
_updateResults: function(searchSystem, results) {
|
||||||
if (results.length == 0) {
|
|
||||||
this._statusText.set_text(_("No matching results."));
|
|
||||||
this._statusText.show();
|
|
||||||
} else {
|
|
||||||
this._statusText.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
let terms = searchSystem.getTerms();
|
let terms = searchSystem.getTerms();
|
||||||
this._openSearchSystem.setSearchTerms(terms);
|
let [provider, providerResults] = results;
|
||||||
|
let meta = this._metaForProvider(provider);
|
||||||
|
|
||||||
// To avoid CSS transitions causing flickering when the first search
|
if (providerResults.length == 0) {
|
||||||
// result stays the same, we hide the content while filling in the
|
this._clearDisplayForProvider(provider);
|
||||||
// results.
|
meta.resultDisplay.setResults([], []);
|
||||||
|
this._maybeSetInitialSelection();
|
||||||
|
this._updateStatusText();
|
||||||
|
} else {
|
||||||
|
meta.resultDisplay.setResults(providerResults, terms);
|
||||||
|
let results = meta.resultDisplay.getResultsForDisplay();
|
||||||
|
|
||||||
|
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||||
|
this._clearDisplayForProvider(provider);
|
||||||
|
meta.actor.show();
|
||||||
|
|
||||||
|
// Hiding drops the key focus if we have it
|
||||||
|
let focus = global.stage.get_key_focus();
|
||||||
|
// To avoid CSS transitions causing flickering when
|
||||||
|
// the first search result stays the same, we hide the
|
||||||
|
// content while filling in the results.
|
||||||
this._content.hide();
|
this._content.hide();
|
||||||
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
meta.resultDisplay.renderResults(metas);
|
||||||
let [provider, providerResults] = results[i];
|
|
||||||
let meta = this._metaForProvider(provider);
|
|
||||||
meta.hasPendingResults = provider.async;
|
|
||||||
if (!meta.hasPendingResults)
|
|
||||||
this._updateProviderResults(provider, providerResults, terms);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._maybeSetInitialSelection();
|
this._maybeSetInitialSelection();
|
||||||
this._content.show();
|
this._updateStatusText();
|
||||||
|
|
||||||
return true;
|
this._content.show();
|
||||||
|
if (this._content.contains(focus))
|
||||||
|
global.stage.set_key_focus(focus);
|
||||||
|
}));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
activateDefault: function() {
|
activateDefault: function() {
|
||||||
|
160
js/ui/sessionMode.js
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const DEFAULT_MODE = 'restrictive';
|
||||||
|
|
||||||
|
const _modes = {
|
||||||
|
'restrictive': {
|
||||||
|
hasOverview: false,
|
||||||
|
showCalendarEvents: false,
|
||||||
|
allowSettings: false,
|
||||||
|
allowExtensions: false,
|
||||||
|
allowKeybindingsWhenModal: false,
|
||||||
|
hasRunDialog: false,
|
||||||
|
hasWorkspaces: false,
|
||||||
|
hasWindows: false,
|
||||||
|
hasNotifications: false,
|
||||||
|
isLocked: false,
|
||||||
|
isGreeter: false,
|
||||||
|
isPrimary: false,
|
||||||
|
unlockDialog: null,
|
||||||
|
components: [],
|
||||||
|
panel: {
|
||||||
|
left: [],
|
||||||
|
center: [],
|
||||||
|
right: []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'gdm': {
|
||||||
|
allowKeybindingsWhenModal: true,
|
||||||
|
hasNotifications: true,
|
||||||
|
isGreeter: true,
|
||||||
|
isPrimary: true,
|
||||||
|
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
||||||
|
components: ['polkitAgent'],
|
||||||
|
panel: {
|
||||||
|
left: [],
|
||||||
|
center: ['dateMenu'],
|
||||||
|
right: ['a11y', 'display', 'keyboard',
|
||||||
|
'volume', 'battery', 'powerMenu']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'lock-screen': {
|
||||||
|
isLocked: true,
|
||||||
|
isGreeter: undefined,
|
||||||
|
unlockDialog: undefined,
|
||||||
|
components: ['polkitAgent', 'telepathyClient'],
|
||||||
|
panel: {
|
||||||
|
left: ['userMenu'],
|
||||||
|
center: [],
|
||||||
|
right: ['lockScreen']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'unlock-dialog': {
|
||||||
|
isLocked: true,
|
||||||
|
unlockDialog: undefined,
|
||||||
|
components: ['polkitAgent', 'telepathyClient'],
|
||||||
|
panel: {
|
||||||
|
left: ['userMenu'],
|
||||||
|
center: [],
|
||||||
|
right: ['a11y', 'keyboard', 'lockScreen']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'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 listModes() {
|
||||||
|
let modes = Object.getOwnPropertyNames(_modes);
|
||||||
|
for (let i = 0; i < modes.length; i++)
|
||||||
|
if (_modes[modes[i]].isPrimary)
|
||||||
|
print(modes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SessionMode = new Lang.Class({
|
||||||
|
Name: 'SessionMode',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
global.connect('notify::session-mode', Lang.bind(this, this._sync));
|
||||||
|
let mode = _modes[global.session_mode].isPrimary ? global.session_mode
|
||||||
|
: 'user';
|
||||||
|
this._modeStack = [mode];
|
||||||
|
this._sync();
|
||||||
|
},
|
||||||
|
|
||||||
|
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 = _modes[this.currentMode];
|
||||||
|
params = Params.parse(params, _modes[DEFAULT_MODE]);
|
||||||
|
|
||||||
|
// 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);
|
@ -7,6 +7,7 @@ const Shell = imports.gi.Shell;
|
|||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
|
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Flashspot = imports.ui.flashspot;
|
const Flashspot = imports.ui.flashspot;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -17,17 +18,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
|||||||
<arg type="b" direction="out" name="success" />
|
<arg type="b" direction="out" name="success" />
|
||||||
<arg type="s" direction="out" name="result" />
|
<arg type="s" direction="out" name="result" />
|
||||||
</method>
|
</method>
|
||||||
<method name="ListExtensions">
|
|
||||||
<arg type="a{sa{sv}}" direction="out" name="extensions" />
|
|
||||||
</method>
|
|
||||||
<method name="GetExtensionInfo">
|
|
||||||
<arg type="s" direction="in" name="extension" />
|
|
||||||
<arg type="a{sv}" direction="out" name="info" />
|
|
||||||
</method>
|
|
||||||
<method name="GetExtensionErrors">
|
|
||||||
<arg type="s" direction="in" name="extension" />
|
|
||||||
<arg type="as" direction="out" name="errors" />
|
|
||||||
</method>
|
|
||||||
<method name="ScreenshotArea">
|
<method name="ScreenshotArea">
|
||||||
<arg type="i" direction="in" name="x"/>
|
<arg type="i" direction="in" name="x"/>
|
||||||
<arg type="i" direction="in" name="y"/>
|
<arg type="i" direction="in" name="y"/>
|
||||||
@ -56,30 +46,21 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
|||||||
<arg type="i" direction="in" name="width"/>
|
<arg type="i" direction="in" name="width"/>
|
||||||
<arg type="i" direction="in" name="height"/>
|
<arg type="i" direction="in" name="height"/>
|
||||||
</method>
|
</method>
|
||||||
<method name="EnableExtension">
|
|
||||||
<arg type="s" direction="in" name="uuid"/>
|
|
||||||
</method>
|
|
||||||
<method name="DisableExtension">
|
|
||||||
<arg type="s" direction="in" name="uuid"/>
|
|
||||||
</method>
|
|
||||||
<method name="InstallRemoteExtension">
|
|
||||||
<arg type="s" direction="in" name="uuid"/>
|
|
||||||
<arg type="s" direction="in" name="version"/>
|
|
||||||
</method>
|
|
||||||
<method name="UninstallExtension">
|
|
||||||
<arg type="s" direction="in" name="uuid"/>
|
|
||||||
<arg type="b" direction="out" name="success"/>
|
|
||||||
</method>
|
|
||||||
<method name="LaunchExtensionPrefs">
|
|
||||||
<arg type="s" direction="in" name="uuid"/>
|
|
||||||
</method>
|
|
||||||
<property name="OverviewActive" type="b" access="readwrite" />
|
<property name="OverviewActive" type="b" access="readwrite" />
|
||||||
<property name="ApiVersion" type="i" access="read" />
|
|
||||||
<property name="ShellVersion" type="s" access="read" />
|
<property name="ShellVersion" type="s" access="read" />
|
||||||
<signal name="ExtensionStatusChanged">
|
</interface>;
|
||||||
<arg type="s" name="uuid"/>
|
|
||||||
<arg type="i" name="state"/>
|
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
|
||||||
<arg type="s" name="error"/>
|
<method name="Lock">
|
||||||
|
</method>
|
||||||
|
<method name="GetActive">
|
||||||
|
<arg name="active" direction="out" type="b" />
|
||||||
|
</method>
|
||||||
|
<method name="SetActive">
|
||||||
|
<arg name="value" direction="in" type="u" />
|
||||||
|
</method>
|
||||||
|
<signal name="ActiveChanged">
|
||||||
|
<arg name="new_value" type="b" />
|
||||||
</signal>
|
</signal>
|
||||||
</interface>;
|
</interface>;
|
||||||
|
|
||||||
@ -89,8 +70,8 @@ const GnomeShell = new Lang.Class({
|
|||||||
_init: function() {
|
_init: function() {
|
||||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
|
||||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||||
ExtensionSystem.connect('extension-state-changed',
|
|
||||||
Lang.bind(this, this._extensionStateChanged));
|
this._extensionsSerivce = new GnomeShellExtensions();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,6 +183,67 @@ const GnomeShell = new Lang.Class({
|
|||||||
flashspot.fire();
|
flashspot.fire();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get OverviewActive() {
|
||||||
|
return Main.overview.visible;
|
||||||
|
},
|
||||||
|
|
||||||
|
set OverviewActive(visible) {
|
||||||
|
if (visible)
|
||||||
|
Main.overview.show();
|
||||||
|
else
|
||||||
|
Main.overview.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
ShellVersion: Config.PACKAGE_VERSION
|
||||||
|
});
|
||||||
|
|
||||||
|
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
|
||||||
|
<method name="ListExtensions">
|
||||||
|
<arg type="a{sa{sv}}" direction="out" name="extensions" />
|
||||||
|
</method>
|
||||||
|
<method name="GetExtensionInfo">
|
||||||
|
<arg type="s" direction="in" name="extension" />
|
||||||
|
<arg type="a{sv}" direction="out" name="info" />
|
||||||
|
</method>
|
||||||
|
<method name="GetExtensionErrors">
|
||||||
|
<arg type="s" direction="in" name="extension" />
|
||||||
|
<arg type="as" direction="out" name="errors" />
|
||||||
|
</method>
|
||||||
|
<signal name="ExtensionStatusChanged">
|
||||||
|
<arg type="s" name="uuid"/>
|
||||||
|
<arg type="i" name="state"/>
|
||||||
|
<arg type="s" name="error"/>
|
||||||
|
</signal>
|
||||||
|
<method name="InstallRemoteExtension">
|
||||||
|
<arg type="s" direction="in" name="uuid"/>
|
||||||
|
<arg type="s" direction="out" name="result"/>
|
||||||
|
</method>
|
||||||
|
<method name="UninstallExtension">
|
||||||
|
<arg type="s" direction="in" name="uuid"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
</method>
|
||||||
|
<method name="LaunchExtensionPrefs">
|
||||||
|
<arg type="s" direction="in" name="uuid"/>
|
||||||
|
</method>
|
||||||
|
<method name="ReloadExtension">
|
||||||
|
<arg type="s" direction="in" name="uuid"/>
|
||||||
|
</method>
|
||||||
|
<method name="CheckForUpdates">
|
||||||
|
</method>
|
||||||
|
<property name="ShellVersion" type="s" access="read" />
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const GnomeShellExtensions = new Lang.Class({
|
||||||
|
Name: 'GnomeShellExtensionsDBus',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||||
|
ExtensionSystem.connect('extension-state-changed',
|
||||||
|
Lang.bind(this, this._extensionStateChanged));
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
ListExtensions: function() {
|
ListExtensions: function() {
|
||||||
let out = {};
|
let out = {};
|
||||||
for (let uuid in ExtensionUtils.extensions) {
|
for (let uuid in ExtensionUtils.extensions) {
|
||||||
@ -260,26 +302,12 @@ const GnomeShell = new Lang.Class({
|
|||||||
return extension.errors;
|
return extension.errors;
|
||||||
},
|
},
|
||||||
|
|
||||||
EnableExtension: function(uuid) {
|
InstallRemoteExtensionAsync: function([uuid], invocation) {
|
||||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
return ExtensionDownloader.installExtension(uuid, invocation);
|
||||||
if (enabledExtensions.indexOf(uuid) == -1)
|
|
||||||
enabledExtensions.push(uuid);
|
|
||||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
|
||||||
},
|
|
||||||
|
|
||||||
DisableExtension: function(uuid) {
|
|
||||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
|
||||||
while (enabledExtensions.indexOf(uuid) != -1)
|
|
||||||
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
|
|
||||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
|
||||||
},
|
|
||||||
|
|
||||||
InstallRemoteExtension: function(uuid, version_tag) {
|
|
||||||
ExtensionSystem.installExtensionFromUUID(uuid, version_tag);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
UninstallExtension: function(uuid) {
|
UninstallExtension: function(uuid) {
|
||||||
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
|
return ExtensionDownloader.uninstallExtension(uuid);
|
||||||
},
|
},
|
||||||
|
|
||||||
LaunchExtensionPrefs: function(uuid) {
|
LaunchExtensionPrefs: function(uuid) {
|
||||||
@ -289,19 +317,18 @@ const GnomeShell = new Lang.Class({
|
|||||||
['extension:///' + uuid], -1, null);
|
['extension:///' + uuid], -1, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
get OverviewActive() {
|
ReloadExtension: function(uuid) {
|
||||||
return Main.overview.visible;
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
|
if (!extension)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ExtensionSystem.reloadExtension(extension);
|
||||||
},
|
},
|
||||||
|
|
||||||
set OverviewActive(visible) {
|
CheckForUpdates: function() {
|
||||||
if (visible)
|
ExtensionDownloader.checkForUpdates();
|
||||||
Main.overview.show();
|
|
||||||
else
|
|
||||||
Main.overview.hide();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ApiVersion: ExtensionSystem.API_VERSION,
|
|
||||||
|
|
||||||
ShellVersion: Config.PACKAGE_VERSION,
|
ShellVersion: Config.PACKAGE_VERSION,
|
||||||
|
|
||||||
_extensionStateChanged: function(_, newState) {
|
_extensionStateChanged: function(_, newState) {
|
||||||
@ -309,3 +336,42 @@ const GnomeShell = new Lang.Class({
|
|||||||
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
|
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ScreenSaverDBus = new Lang.Class({
|
||||||
|
Name: 'ScreenSaverDBus',
|
||||||
|
|
||||||
|
_init: function(screenShield) {
|
||||||
|
this.parent();
|
||||||
|
|
||||||
|
this._screenShield = screenShield;
|
||||||
|
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
|
||||||
|
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
|
||||||
|
|
||||||
|
Gio.DBus.session.own_name('org.gnome.ScreenSaver', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
LockAsync: function(parameters, invocation) {
|
||||||
|
let tmpId = this._screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||||
|
this._screenShield.disconnect(tmpId);
|
||||||
|
|
||||||
|
invocation.return_value(null);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._screenShield.lock(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
SetActive: function(active) {
|
||||||
|
if (active)
|
||||||
|
this._screenShield.lock(true);
|
||||||
|
else
|
||||||
|
this._screenShield.unlock();
|
||||||
|
},
|
||||||
|
|
||||||
|
GetActive: function() {
|
||||||
|
return this._screenShield.locked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -7,7 +7,7 @@ const Main = imports.ui.main;
|
|||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
const _EntryMenu = new Lang.Class({
|
const EntryMenu = new Lang.Class({
|
||||||
Name: 'ShellEntryMenu',
|
Name: 'ShellEntryMenu',
|
||||||
Extends: PopupMenu.PopupMenu,
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
@ -34,16 +34,35 @@ const _EntryMenu = new Lang.Class({
|
|||||||
this._pasteItem = item;
|
this._pasteItem = item;
|
||||||
|
|
||||||
this._passwordItem = null;
|
this._passwordItem = null;
|
||||||
if (params.isPassword) {
|
if (params.isPassword)
|
||||||
item = new PopupMenu.PopupMenuItem('');
|
this._makePasswordItem();
|
||||||
|
|
||||||
|
Main.uiGroup.add_actor(this.actor);
|
||||||
|
this.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_makePasswordItem: function() {
|
||||||
|
let item = new PopupMenu.PopupMenuItem('');
|
||||||
item.connect('activate', Lang.bind(this,
|
item.connect('activate', Lang.bind(this,
|
||||||
this._onPasswordActivated));
|
this._onPasswordActivated));
|
||||||
this.addMenuItem(item);
|
this.addMenuItem(item);
|
||||||
this._passwordItem = item;
|
this._passwordItem = item;
|
||||||
}
|
},
|
||||||
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
get isPassword() {
|
||||||
this.actor.hide();
|
return this._passwordItem != null;
|
||||||
|
},
|
||||||
|
|
||||||
|
set isPassword(v) {
|
||||||
|
if (v == this.isPassword)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (v)
|
||||||
|
this._makePasswordItem();
|
||||||
|
else {
|
||||||
|
this._passwordItem.destroy();
|
||||||
|
this._passwordItem = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function() {
|
open: function() {
|
||||||
@ -57,6 +76,12 @@ const _EntryMenu = new Lang.Class({
|
|||||||
this.actor.grab_key_focus();
|
this.actor.grab_key_focus();
|
||||||
|
|
||||||
this.parent();
|
this.parent();
|
||||||
|
this._entry.add_style_pseudo_class('focus');
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function() {
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateCopyItem: function() {
|
_updateCopyItem: function() {
|
||||||
@ -104,65 +129,42 @@ const _EntryMenu = new Lang.Class({
|
|||||||
function _setMenuAlignment(entry, stageX) {
|
function _setMenuAlignment(entry, stageX) {
|
||||||
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
|
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
|
||||||
if (success)
|
if (success)
|
||||||
entry._menu.setSourceAlignment(entryX / entry.width);
|
entry.menu.setSourceAlignment(entryX / entry.width);
|
||||||
};
|
};
|
||||||
|
|
||||||
function _onClicked(action, actor) {
|
function _onButtonPressEvent(actor, event, entry) {
|
||||||
let entry = actor._menu ? actor : actor.get_parent();
|
if (entry.menu.isOpen) {
|
||||||
|
entry.menu.close();
|
||||||
if (entry._menu.isOpen) {
|
return true;
|
||||||
entry._menu.close();
|
} else if (event.get_button() == 3) {
|
||||||
} else if (action.get_button() == 3) {
|
let [stageX, stageY] = event.get_coords();
|
||||||
let [stageX, stageY] = action.get_coords();
|
|
||||||
_setMenuAlignment(entry, stageX);
|
_setMenuAlignment(entry, stageX);
|
||||||
entry._menu.open();
|
entry.menu.open();
|
||||||
}
|
return true;
|
||||||
};
|
|
||||||
|
|
||||||
function _onLongPress(action, actor, state) {
|
|
||||||
let entry = actor._menu ? actor : actor.get_parent();
|
|
||||||
|
|
||||||
if (state == Clutter.LongPressState.QUERY)
|
|
||||||
return action.get_button() == 1 && !entry._menu.isOpen;
|
|
||||||
|
|
||||||
if (state == Clutter.LongPressState.ACTIVATE) {
|
|
||||||
let [stageX, stageY] = action.get_coords();
|
|
||||||
_setMenuAlignment(entry, stageX);
|
|
||||||
entry._menu.open();
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
function _onPopup(actor) {
|
function _onPopup(actor, entry) {
|
||||||
let entry = actor._menu ? actor : actor.get_parent();
|
|
||||||
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
||||||
if (success)
|
if (success)
|
||||||
entry._menu.setSourceAlignment(textX / entry.width);
|
entry.menu.setSourceAlignment(textX / entry.width);
|
||||||
entry._menu.open();
|
entry.menu.open();
|
||||||
};
|
};
|
||||||
|
|
||||||
function addContextMenu(entry, params) {
|
function addContextMenu(entry, params) {
|
||||||
if (entry._menu)
|
if (entry.menu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entry._menu = new _EntryMenu(entry, params);
|
entry.menu = new EntryMenu(entry, params);
|
||||||
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
||||||
entry._menuManager.addMenu(entry._menu);
|
entry._menuManager.addMenu(entry.menu);
|
||||||
|
|
||||||
let clickAction;
|
// Add an event handler to both the entry and its clutter_text; the former
|
||||||
|
|
||||||
// Add a click action to both the entry and its clutter_text; the former
|
|
||||||
// so padding is included in the clickable area, the latter because the
|
// so padding is included in the clickable area, the latter because the
|
||||||
// event processing of ClutterText prevents event-bubbling.
|
// event processing of ClutterText prevents event-bubbling.
|
||||||
clickAction = new Clutter.ClickAction();
|
entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
||||||
clickAction.connect('clicked', _onClicked);
|
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
||||||
clickAction.connect('long-press', _onLongPress);
|
|
||||||
entry.clutter_text.add_action(clickAction);
|
|
||||||
|
|
||||||
clickAction = new Clutter.ClickAction();
|
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
|
||||||
clickAction.connect('clicked', _onClicked);
|
|
||||||
clickAction.connect('long-press', _onLongPress);
|
|
||||||
entry.add_action(clickAction);
|
|
||||||
|
|
||||||
entry.connect('popup-menu', _onPopup);
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const CheckBox = imports.ui.checkBox;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
|
||||||
const LIST_ITEM_ICON_SIZE = 48;
|
const LIST_ITEM_ICON_SIZE = 48;
|
||||||
|
|
||||||
@ -48,6 +52,11 @@ function _setLabelsForMessage(dialog, message) {
|
|||||||
_setLabelText(dialog.descriptionLabel, labels[1]);
|
_setLabelText(dialog.descriptionLabel, labels[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _createIcon(gicon) {
|
||||||
|
return new St.Icon({ gicon: gicon,
|
||||||
|
style_class: 'shell-mount-operation-icon' })
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------- */
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
const ListItem = new Lang.Class({
|
const ListItem = new Lang.Class({
|
||||||
@ -91,11 +100,11 @@ const ShellMountOperation = new Lang.Class({
|
|||||||
Name: 'ShellMountOperation',
|
Name: 'ShellMountOperation',
|
||||||
|
|
||||||
_init: function(source, params) {
|
_init: function(source, params) {
|
||||||
params = Params.parse(params, { reaskPassword: false });
|
params = Params.parse(params, { existingDialog: null });
|
||||||
|
|
||||||
this._reaskPassword = params.reaskPassword;
|
|
||||||
|
|
||||||
this._dialog = null;
|
this._dialog = null;
|
||||||
|
this._dialogId = 0;
|
||||||
|
this._existingDialog = params.existingDialog;
|
||||||
this._processesDialog = null;
|
this._processesDialog = null;
|
||||||
|
|
||||||
this.mountOp = new Shell.MountOperation();
|
this.mountOp = new Shell.MountOperation();
|
||||||
@ -107,70 +116,90 @@ const ShellMountOperation = new Lang.Class({
|
|||||||
this.mountOp.connect('show-processes-2',
|
this.mountOp.connect('show-processes-2',
|
||||||
Lang.bind(this, this._onShowProcesses2));
|
Lang.bind(this, this._onShowProcesses2));
|
||||||
this.mountOp.connect('aborted',
|
this.mountOp.connect('aborted',
|
||||||
Lang.bind(this, this._onAborted));
|
Lang.bind(this, this.close));
|
||||||
|
this.mountOp.connect('show-unmount-progress',
|
||||||
|
Lang.bind(this, this._onShowUnmountProgress));
|
||||||
|
|
||||||
this._icon = new St.Icon({ gicon: source.get_icon(),
|
this._gicon = source.get_icon();
|
||||||
style_class: 'shell-mount-operation-icon' });
|
},
|
||||||
|
|
||||||
|
_closeExistingDialog: function() {
|
||||||
|
if (!this._existingDialog)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._existingDialog.close();
|
||||||
|
this._existingDialog = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAskQuestion: function(op, message, choices) {
|
_onAskQuestion: function(op, message, choices) {
|
||||||
this._dialog = new ShellMountQuestionDialog(this._icon);
|
this._closeExistingDialog();
|
||||||
|
this._dialog = new ShellMountQuestionDialog(this._gicon);
|
||||||
|
|
||||||
this._dialog.connect('response',
|
this._dialogId = this._dialog.connect('response', Lang.bind(this,
|
||||||
Lang.bind(this, function(object, choice) {
|
function(object, choice) {
|
||||||
this.mountOp.set_choice(choice);
|
this.mountOp.set_choice(choice);
|
||||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
|
|
||||||
this._dialog.close(global.get_current_time());
|
this.close();
|
||||||
this._dialog = null;
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._dialog.update(message, choices);
|
this._dialog.update(message, choices);
|
||||||
this._dialog.open(global.get_current_time());
|
this._dialog.open();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAskPassword: function(op, message) {
|
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
|
||||||
this._notificationShowing = true;
|
if (this._existingDialog) {
|
||||||
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
|
this._dialog = this._existingDialog;
|
||||||
|
this._dialog.reaskPassword();
|
||||||
|
} else {
|
||||||
|
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._dialogId = this._dialog.connect('response', Lang.bind(this,
|
||||||
|
function(object, choice, password, remember) {
|
||||||
|
if (choice == -1) {
|
||||||
|
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||||
|
} else {
|
||||||
|
if (remember)
|
||||||
|
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
|
||||||
|
else
|
||||||
|
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
|
||||||
|
|
||||||
this._source.connect('password-ready',
|
|
||||||
Lang.bind(this, function(source, password) {
|
|
||||||
this.mountOp.set_password(password);
|
this.mountOp.set_password(password);
|
||||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
|
}
|
||||||
this._notificationShowing = false;
|
|
||||||
this._source.destroy();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._source.connect('destroy',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
if (!this._notificationShowing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._notificationShowing = false;
|
|
||||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
|
||||||
}));
|
}));
|
||||||
|
this._dialog.open();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAborted: function(op) {
|
close: function(op) {
|
||||||
if (!this._dialog)
|
this._closeExistingDialog();
|
||||||
return;
|
this._processesDialog = null;
|
||||||
|
|
||||||
this._dialog.close(global.get_current_time());
|
if (this._dialog) {
|
||||||
|
this._dialog.close();
|
||||||
this._dialog = null;
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._notifier) {
|
||||||
|
this._notifier.done();
|
||||||
|
this._notifier = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onShowProcesses2: function(op) {
|
_onShowProcesses2: function(op) {
|
||||||
|
this._closeExistingDialog();
|
||||||
|
|
||||||
let processes = op.get_show_processes_pids();
|
let processes = op.get_show_processes_pids();
|
||||||
let choices = op.get_show_processes_choices();
|
let choices = op.get_show_processes_choices();
|
||||||
let message = op.get_show_processes_message();
|
let message = op.get_show_processes_message();
|
||||||
|
|
||||||
if (!this._processesDialog) {
|
if (!this._processesDialog) {
|
||||||
this._processesDialog = new ShellProcessesDialog(this._icon);
|
this._processesDialog = new ShellProcessesDialog(this._gicon);
|
||||||
this._dialog = this._processesDialog;
|
this._dialog = this._processesDialog;
|
||||||
|
|
||||||
this._processesDialog.connect('response',
|
this._dialogId = this._processesDialog.connect('response', Lang.bind(this,
|
||||||
Lang.bind(this, function(object, choice) {
|
function(object, choice) {
|
||||||
if (choice == -1) {
|
if (choice == -1) {
|
||||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||||
} else {
|
} else {
|
||||||
@ -178,28 +207,86 @@ const ShellMountOperation = new Lang.Class({
|
|||||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._processesDialog.close(global.get_current_time());
|
this.close();
|
||||||
this._dialog = null;
|
|
||||||
}));
|
}));
|
||||||
this._processesDialog.open(global.get_current_time());
|
this._processesDialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._processesDialog.update(message, processes, choices);
|
this._processesDialog.update(message, processes, choices);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onShowUnmountProgress: function(op, message, timeLeft, bytesLeft) {
|
||||||
|
if (!this._notifier)
|
||||||
|
this._notifier = new ShellUnmountNotifier();
|
||||||
|
|
||||||
|
if (bytesLeft == 0)
|
||||||
|
this._notifier.done(message);
|
||||||
|
else
|
||||||
|
this._notifier.show(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
borrowDialog: function() {
|
||||||
|
if (this._dialogId != 0) {
|
||||||
|
this._dialog.disconnect(this._dialogId);
|
||||||
|
this._dialogId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._dialog;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ShellUnmountNotifier = new Lang.Class({
|
||||||
|
Name: 'ShellUnmountNotifier',
|
||||||
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent('', 'media-removable');
|
||||||
|
|
||||||
|
this._notification = null;
|
||||||
|
Main.messageTray.add(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function(message) {
|
||||||
|
let [header, text] = message.split('\n', 2);
|
||||||
|
|
||||||
|
if (!this._notification) {
|
||||||
|
this._notification = new MessageTray.Notification(this, header, text);
|
||||||
|
this._notification.setTransient(true);
|
||||||
|
this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||||
|
} else {
|
||||||
|
this._notification.update(header, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notify(this._notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
done: function(message) {
|
||||||
|
if (this._notification) {
|
||||||
|
this._notification.destroy();
|
||||||
|
this._notification = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
let notification = new MessageTray.Notification(this, message, null);
|
||||||
|
notification.setTransient(true);
|
||||||
|
|
||||||
|
this.notify(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ShellMountQuestionDialog = new Lang.Class({
|
const ShellMountQuestionDialog = new Lang.Class({
|
||||||
Name: 'ShellMountQuestionDialog',
|
Name: 'ShellMountQuestionDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init: function(icon) {
|
_init: function(gicon) {
|
||||||
this.parent({ styleClass: 'mount-question-dialog' });
|
this.parent({ styleClass: 'mount-question-dialog' });
|
||||||
|
|
||||||
let mainContentLayout = new St.BoxLayout();
|
let mainContentLayout = new St.BoxLayout();
|
||||||
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
||||||
y_fill: false });
|
y_fill: false });
|
||||||
|
|
||||||
this._iconBin = new St.Bin({ child: icon });
|
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
|
||||||
mainContentLayout.add(this._iconBin,
|
mainContentLayout.add(this._iconBin,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
@ -234,62 +321,108 @@ const ShellMountQuestionDialog = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
|
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
|
||||||
|
|
||||||
const ShellMountPasswordSource = new Lang.Class({
|
const ShellMountPasswordDialog = new Lang.Class({
|
||||||
Name: 'ShellMountPasswordSource',
|
Name: 'ShellMountPasswordDialog',
|
||||||
Extends: MessageTray.Source,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init: function(message, icon, reaskPassword) {
|
_init: function(message, gicon, flags) {
|
||||||
let strings = message.split('\n');
|
let strings = message.split('\n');
|
||||||
this.parent(strings[0]);
|
this.parent({ styleClass: 'prompt-dialog' });
|
||||||
|
|
||||||
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
|
vertical: false });
|
||||||
|
this.contentLayout.add(mainContentBox);
|
||||||
|
|
||||||
// add ourselves as a source, and popup the notification
|
let icon = _createIcon(gicon);
|
||||||
Main.messageTray.add(this);
|
mainContentBox.add(icon,
|
||||||
this.notify(this._notification);
|
{ x_fill: true,
|
||||||
},
|
y_fill: false,
|
||||||
});
|
x_align: St.Align.END,
|
||||||
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
|
y_align: St.Align.START });
|
||||||
|
|
||||||
const ShellMountPasswordNotification = new Lang.Class({
|
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
||||||
Name: 'ShellMountPasswordNotification',
|
vertical: true });
|
||||||
Extends: MessageTray.Notification,
|
mainContentBox.add(this._messageBox,
|
||||||
|
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
|
||||||
|
|
||||||
_init: function(source, strings, icon, reaskPassword) {
|
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
|
||||||
this.parent(source, strings[0], null, { customContent: true, icon: icon });
|
this._messageBox.add(subject,
|
||||||
|
{ y_fill: false,
|
||||||
// set the notification to transient and urgent, so that it
|
y_align: St.Align.START });
|
||||||
// expands out
|
if (strings[0])
|
||||||
this.setTransient(true);
|
subject.set_text(strings[0]);
|
||||||
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
|
||||||
|
|
||||||
|
let description = new St.Label({ style_class: 'prompt-dialog-description' });
|
||||||
|
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
description.clutter_text.line_wrap = true;
|
||||||
|
this._messageBox.add(description,
|
||||||
|
{ y_fill: true,
|
||||||
|
y_align: St.Align.START });
|
||||||
if (strings[1])
|
if (strings[1])
|
||||||
this.addBody(strings[1]);
|
description.set_text(strings[1]);
|
||||||
|
|
||||||
if (reaskPassword) {
|
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
|
||||||
let label = new St.Label({ style_class: 'mount-password-reask',
|
this._messageBox.add(this._passwordBox);
|
||||||
text: _("Wrong password, please try again") });
|
|
||||||
|
|
||||||
this.addActor(label);
|
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
|
||||||
|
text: _("Password") }));
|
||||||
|
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._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||||
|
this._passwordBox.add(this._passwordEntry, {expand: true });
|
||||||
|
this.setInitialKeyFocus(this._passwordEntry);
|
||||||
|
|
||||||
|
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
|
||||||
|
text: _("Sorry, that didn\'t work. Please try again.") });
|
||||||
|
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
this._errorMessageLabel.clutter_text.line_wrap = true;
|
||||||
|
this._errorMessageLabel.hide();
|
||||||
|
this._messageBox.add(this._errorMessageLabel);
|
||||||
|
|
||||||
|
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
|
||||||
|
this._rememberChoice = new CheckBox.CheckBox();
|
||||||
|
this._rememberChoice.getLabelActor().text = _("Remember Password");
|
||||||
|
this._rememberChoice.actor.checked = true;
|
||||||
|
this._messageBox.add(this._rememberChoice.actor);
|
||||||
|
} else {
|
||||||
|
this._rememberChoice = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
|
let buttons = [{ label: _("Cancel"),
|
||||||
can_focus: true });
|
action: Lang.bind(this, this._onCancelButton),
|
||||||
this.setActionArea(this._responseEntry);
|
key: Clutter.Escape
|
||||||
|
},
|
||||||
|
{ label: _("Unlock"),
|
||||||
|
action: Lang.bind(this, this._onUnlockButton),
|
||||||
|
default: true
|
||||||
|
}];
|
||||||
|
|
||||||
this._responseEntry.clutter_text.connect('activate',
|
this.setButtons(buttons);
|
||||||
Lang.bind(this, this._onEntryActivated));
|
|
||||||
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
|
||||||
|
|
||||||
this._responseEntry.grab_key_focus();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEntryActivated: function() {
|
reaskPassword: function() {
|
||||||
let text = this._responseEntry.get_text();
|
this._passwordEntry.set_text('');
|
||||||
if (text == '')
|
this._errorMessageLabel.show();
|
||||||
return;
|
},
|
||||||
|
|
||||||
this.source.emit('password-ready', text);
|
_onCancelButton: function() {
|
||||||
|
this.emit('response', -1, '', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUnlockButton: function() {
|
||||||
|
this._onEntryActivate();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEntryActivate: function() {
|
||||||
|
this.emit('response', 1,
|
||||||
|
this._passwordEntry.get_text(),
|
||||||
|
this._rememberChoice &&
|
||||||
|
this._rememberChoice.actor.checked);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -297,14 +430,14 @@ const ShellProcessesDialog = new Lang.Class({
|
|||||||
Name: 'ShellProcessesDialog',
|
Name: 'ShellProcessesDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init: function(icon) {
|
_init: function(gicon) {
|
||||||
this.parent({ styleClass: 'show-processes-dialog' });
|
this.parent({ styleClass: 'show-processes-dialog' });
|
||||||
|
|
||||||
let mainContentLayout = new St.BoxLayout();
|
let mainContentLayout = new St.BoxLayout();
|
||||||
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
||||||
y_fill: false });
|
y_fill: false });
|
||||||
|
|
||||||
this._iconBin = new St.Bin({ child: icon });
|
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
|
||||||
mainContentLayout.add(this._iconBin,
|
mainContentLayout.add(this._iconBin,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
@ -338,21 +471,17 @@ const ShellProcessesDialog = new Lang.Class({
|
|||||||
scrollView.hide();
|
scrollView.hide();
|
||||||
|
|
||||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||||
scrollView.add_actor(this._applicationList,
|
scrollView.add_actor(this._applicationList);
|
||||||
{ x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
this._applicationList.connect('actor-added',
|
this._applicationList.connect('actor-added',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
if (this._applicationList.get_children().length == 1)
|
if (this._applicationList.get_n_children() == 1)
|
||||||
scrollView.show();
|
scrollView.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._applicationList.connect('actor-removed',
|
this._applicationList.connect('actor-removed',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
if (this._applicationList.get_children().length == 0)
|
if (this._applicationList.get_n_children() == 0)
|
||||||
scrollView.hide();
|
scrollView.hide();
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@ -386,3 +515,253 @@ const ShellProcessesDialog = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ShellProcessesDialog.prototype);
|
Signals.addSignalMethods(ShellProcessesDialog.prototype);
|
||||||
|
|
||||||
|
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
|
||||||
|
<method name="AskPassword">
|
||||||
|
<arg type="s" direction="in" name="object_id"/>
|
||||||
|
<arg type="s" direction="in" name="message"/>
|
||||||
|
<arg type="s" direction="in" name="icon_name"/>
|
||||||
|
<arg type="s" direction="in" name="default_user"/>
|
||||||
|
<arg type="s" direction="in" name="default_domain"/>
|
||||||
|
<arg type="u" direction="in" name="flags"/>
|
||||||
|
<arg type="u" direction="out" name="response"/>
|
||||||
|
<arg type="a{sv}" direction="out" name="response_details"/>
|
||||||
|
</method>
|
||||||
|
<method name="AskQuestion">
|
||||||
|
<arg type="s" direction="in" name="object_id"/>
|
||||||
|
<arg type="s" direction="in" name="message"/>
|
||||||
|
<arg type="s" direction="in" name="icon_name"/>
|
||||||
|
<arg type="as" direction="in" name="choices"/>
|
||||||
|
<arg type="u" direction="out" name="response"/>
|
||||||
|
<arg type="a{sv}" direction="out" name="response_details"/>
|
||||||
|
</method>
|
||||||
|
<method name="ShowProcesses">
|
||||||
|
<arg type="s" direction="in" name="object_id"/>
|
||||||
|
<arg type="s" direction="in" name="message"/>
|
||||||
|
<arg type="s" direction="in" name="icon_name"/>
|
||||||
|
<arg type="ai" direction="in" name="application_pids"/>
|
||||||
|
<arg type="as" direction="in" name="choices"/>
|
||||||
|
<arg type="u" direction="out" name="response"/>
|
||||||
|
<arg type="a{sv}" direction="out" name="response_details"/>
|
||||||
|
</method>
|
||||||
|
<method name="Close"/>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const ShellMountOperationType = {
|
||||||
|
NONE: 0,
|
||||||
|
ASK_PASSWORD: 1,
|
||||||
|
ASK_QUESTION: 2,
|
||||||
|
SHOW_PROCESSES: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const GnomeShellMountOpHandler = new Lang.Class({
|
||||||
|
Name: 'GnomeShellMountOpHandler',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
|
||||||
|
Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
|
||||||
|
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||||
|
|
||||||
|
this._dialog = null;
|
||||||
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
|
this._ensureEmptyRequest();
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureEmptyRequest: function() {
|
||||||
|
this._currentId = null;
|
||||||
|
this._currentInvocation = null;
|
||||||
|
this._currentType = ShellMountOperationType.NONE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearCurrentRequest: function(response, details) {
|
||||||
|
if (this._currentInvocation) {
|
||||||
|
this._currentInvocation.return_value(
|
||||||
|
GLib.Variant.new('(ua{sv})', [response, details]));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ensureEmptyRequest();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setCurrentRequest: function(invocation, id, type) {
|
||||||
|
let oldId = this._currentId;
|
||||||
|
let oldType = this._currentType;
|
||||||
|
let requestId = id + '@' + invocation.get_sender();
|
||||||
|
|
||||||
|
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
|
||||||
|
|
||||||
|
this._currentInvocation = invocation;
|
||||||
|
this._currentId = requestId;
|
||||||
|
this._currentType = type;
|
||||||
|
|
||||||
|
if (this._dialog && (oldId == requestId) && (oldType == type))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_closeDialog: function() {
|
||||||
|
if (this._dialog) {
|
||||||
|
this._dialog.close();
|
||||||
|
this._dialog = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_createGIcon: function(iconName) {
|
||||||
|
let realIconName = iconName ? iconName : 'drive-harddisk';
|
||||||
|
return new Gio.ThemedIcon({ name: realIconName,
|
||||||
|
use_default_fallbacks: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AskPassword:
|
||||||
|
* @id: an opaque ID identifying the object for which the operation is requested
|
||||||
|
* The ID must be unique in the context of the calling process.
|
||||||
|
* @message: the message to display
|
||||||
|
* @icon_name: the name of an icon to display
|
||||||
|
* @default_user: the default username for display
|
||||||
|
* @default_domain: the default domain for display
|
||||||
|
* @flags: a set of GAskPasswordFlags
|
||||||
|
* @response: a GMountOperationResult
|
||||||
|
* @response_details: a dictionary containing the response details as
|
||||||
|
* entered by the user. The dictionary MAY contain the following properties:
|
||||||
|
* - "password" -> (s): a password to be used to complete the mount operation
|
||||||
|
* - "password_save" -> (u): a GPasswordSave
|
||||||
|
*
|
||||||
|
* The dialog will stay visible until clients call the Close() method, or
|
||||||
|
* another dialog becomes visible.
|
||||||
|
* Calling AskPassword again for the same id will have the effect to clear
|
||||||
|
* the existing dialog and update it with a message indicating the previous
|
||||||
|
* attempt went wrong.
|
||||||
|
*/
|
||||||
|
AskPasswordAsync: function(params, invocation) {
|
||||||
|
let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
|
||||||
|
|
||||||
|
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
|
||||||
|
this._dialog.reaskPassword();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._closeDialog();
|
||||||
|
|
||||||
|
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
|
||||||
|
this._dialog.connect('response', Lang.bind(this,
|
||||||
|
function(object, choice, password, remember) {
|
||||||
|
let details = {};
|
||||||
|
let response;
|
||||||
|
|
||||||
|
if (choice == -1) {
|
||||||
|
response = Gio.MountOperationResult.ABORTED;
|
||||||
|
} else {
|
||||||
|
response = Gio.MountOperationResult.HANDLED;
|
||||||
|
|
||||||
|
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
|
||||||
|
details['password_save'] = GLib.Variant.new('u', passSave);
|
||||||
|
details['password'] = GLib.Variant.new('s', password);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearCurrentRequest(response, details);
|
||||||
|
}));
|
||||||
|
this._dialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AskQuestion:
|
||||||
|
* @id: an opaque ID identifying the object for which the operation is requested
|
||||||
|
* The ID must be unique in the context of the calling process.
|
||||||
|
* @message: the message to display
|
||||||
|
* @icon_name: the name of an icon to display
|
||||||
|
* @choices: an array of choice strings
|
||||||
|
* GetResponse:
|
||||||
|
* @response: a GMountOperationResult
|
||||||
|
* @response_details: a dictionary containing the response details as
|
||||||
|
* entered by the user. The dictionary MAY contain the following properties:
|
||||||
|
* - "choice" -> (i): the chosen answer among the array of strings passed in
|
||||||
|
*
|
||||||
|
* The dialog will stay visible until clients call the Close() method, or
|
||||||
|
* another dialog becomes visible.
|
||||||
|
* Calling AskQuestion again for the same id will have the effect to clear
|
||||||
|
* update the dialog with the new question.
|
||||||
|
*/
|
||||||
|
AskQuestionAsync: function(params, invocation) {
|
||||||
|
let [id, message, iconName, choices] = params;
|
||||||
|
|
||||||
|
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
|
||||||
|
this._dialog.update(message, choices);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._closeDialog();
|
||||||
|
|
||||||
|
this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
|
||||||
|
this._dialog.connect('response', Lang.bind(this,
|
||||||
|
function(object, choice) {
|
||||||
|
this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
|
||||||
|
{ choice: GLib.Variant.new('i', choice) });
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dialog.update(message, choices);
|
||||||
|
this._dialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ShowProcesses:
|
||||||
|
* @id: an opaque ID identifying the object for which the operation is requested
|
||||||
|
* The ID must be unique in the context of the calling process.
|
||||||
|
* @message: the message to display
|
||||||
|
* @icon_name: the name of an icon to display
|
||||||
|
* @application_pids: the PIDs of the applications to display
|
||||||
|
* @choices: an array of choice strings
|
||||||
|
* @response: a GMountOperationResult
|
||||||
|
* @response_details: a dictionary containing the response details as
|
||||||
|
* entered by the user. The dictionary MAY contain the following properties:
|
||||||
|
* - "choice" -> (i): the chosen answer among the array of strings passed in
|
||||||
|
*
|
||||||
|
* The dialog will stay visible until clients call the Close() method, or
|
||||||
|
* another dialog becomes visible.
|
||||||
|
* Calling ShowProcesses again for the same id will have the effect to clear
|
||||||
|
* the existing dialog and update it with the new message and the new list
|
||||||
|
* of processes.
|
||||||
|
*/
|
||||||
|
ShowProcessesAsync: function(params, invocation) {
|
||||||
|
let [id, message, iconName, applicationPids, choices] = params;
|
||||||
|
|
||||||
|
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
|
||||||
|
this._dialog.update(message, applicationPids, choices);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._closeDialog();
|
||||||
|
|
||||||
|
this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
|
||||||
|
this._dialog.connect('response', Lang.bind(this,
|
||||||
|
function(object, choice) {
|
||||||
|
let response;
|
||||||
|
let details = {};
|
||||||
|
|
||||||
|
if (choice == -1) {
|
||||||
|
response = Gio.MountOperationResult.ABORTED;
|
||||||
|
} else {
|
||||||
|
response = Gio.MountOperationResult.HANDLED;
|
||||||
|
details['choice'] = GLib.Variant.new('i', choice);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearCurrentRequest(response, details);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dialog.update(message, applicationPids, choices);
|
||||||
|
this._dialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close:
|
||||||
|
*
|
||||||
|
* Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
|
||||||
|
* If no dialog is open, does nothing.
|
||||||
|
*/
|
||||||
|
Close: function(params, invocation) {
|
||||||
|
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
|
||||||
|
this._closeDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Gtk = imports.gi.Gtk;
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Signals = imports.signals;
|
|
||||||
const St = imports.gi.St;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Util = imports.misc.util;
|
|
||||||
|
|
||||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||||
@ -44,7 +36,7 @@ const ATIndicator = new Lang.Class({
|
|||||||
Extends: PanelMenu.SystemStatusButton,
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent('preferences-desktop-accessibility', _("Accessibility"));
|
this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
|
||||||
|
|
||||||
let highContrast = this._buildHCItem();
|
let highContrast = this._buildHCItem();
|
||||||
this.menu.addMenuItem(highContrast);
|
this.menu.addMenuItem(highContrast);
|
||||||
@ -56,9 +48,9 @@ const ATIndicator = new Lang.Class({
|
|||||||
let textZoom = this._buildFontItem();
|
let textZoom = this._buildFontItem();
|
||||||
this.menu.addMenuItem(textZoom);
|
this.menu.addMenuItem(textZoom);
|
||||||
|
|
||||||
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
|
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
|
||||||
// 'screen-reader-enabled');
|
'screen-reader-enabled');
|
||||||
// this.menu.addMenuItem(screenReader);
|
this.menu.addMenuItem(screenReader);
|
||||||
|
|
||||||
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
|
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
|
||||||
'screen-keyboard-enabled');
|
'screen-keyboard-enabled');
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gdk = imports.gi.Gdk;
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
|
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
|
||||||
const Gtk = imports.gi.Gtk;
|
const GnomeBluetooth = imports.gi.GnomeBluetooth;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
@ -28,7 +24,7 @@ const Indicator = new Lang.Class({
|
|||||||
Extends: PanelMenu.SystemStatusButton,
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent('bluetooth-disabled', _("Bluetooth"));
|
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
|
||||||
|
|
||||||
this._applet = new GnomeBluetoothApplet.Applet();
|
this._applet = new GnomeBluetoothApplet.Applet();
|
||||||
|
|
||||||
@ -36,11 +32,11 @@ const Indicator = new Lang.Class({
|
|||||||
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
||||||
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
||||||
let current_state = this._applet.killswitch_state;
|
let current_state = this._applet.killswitch_state;
|
||||||
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
|
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
|
||||||
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
|
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
|
||||||
this._applet.killswitch_state = this._killswitch.state ?
|
this._applet.killswitch_state = this._killswitch.state ?
|
||||||
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
|
GnomeBluetooth.KillswitchState.UNBLOCKED:
|
||||||
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
|
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||||
} else
|
} else
|
||||||
this._killswitch.setToggleState(false);
|
this._killswitch.setToggleState(false);
|
||||||
}));
|
}));
|
||||||
@ -94,10 +90,10 @@ const Indicator = new Lang.Class({
|
|||||||
|
|
||||||
_updateKillswitch: function() {
|
_updateKillswitch: function() {
|
||||||
let current_state = this._applet.killswitch_state;
|
let current_state = this._applet.killswitch_state;
|
||||||
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
|
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
|
||||||
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
|
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
|
||||||
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
|
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
|
||||||
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
|
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
|
||||||
|
|
||||||
this._killswitch.setToggleState(on);
|
this._killswitch.setToggleState(on);
|
||||||
if (can_toggle)
|
if (can_toggle)
|
||||||
@ -106,17 +102,14 @@ const Indicator = new Lang.Class({
|
|||||||
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
||||||
this._killswitch.setStatus(_("hardware disabled"));
|
this._killswitch.setStatus(_("hardware disabled"));
|
||||||
|
|
||||||
if (has_adapter)
|
this.actor.visible = has_adapter;
|
||||||
this.actor.show();
|
|
||||||
else
|
|
||||||
this.actor.hide();
|
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
this._discoverable.actor.show();
|
this._discoverable.actor.show();
|
||||||
this.setIcon('bluetooth-active');
|
this.setIcon('bluetooth-active-symbolic');
|
||||||
} else {
|
} else {
|
||||||
this._discoverable.actor.hide();
|
this._discoverable.actor.hide();
|
||||||
this.setIcon('bluetooth-disabled');
|
this.setIcon('bluetooth-disabled-symbolic');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -308,7 +301,7 @@ const Indicator = new Lang.Class({
|
|||||||
|
|
||||||
_ensureSource: function() {
|
_ensureSource: function() {
|
||||||
if (!this._source) {
|
if (!this._source) {
|
||||||
this._source = new Source();
|
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
|
||||||
Main.messageTray.add(this._source);
|
Main.messageTray.add(this._source);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -333,35 +326,6 @@ const Indicator = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Source = new Lang.Class({
|
|
||||||
Name: 'BluetoothSource',
|
|
||||||
Extends: MessageTray.Source,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.parent(_("Bluetooth"));
|
|
||||||
|
|
||||||
this._setSummaryIcon(this.createNotificationIcon());
|
|
||||||
},
|
|
||||||
|
|
||||||
notify: function(notification) {
|
|
||||||
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
|
|
||||||
if (this.notification == notification) {
|
|
||||||
// the destroyed notification is the last for this source
|
|
||||||
this.notification.disconnect(this._private_destroyId);
|
|
||||||
this.destroy();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.parent(notification);
|
|
||||||
},
|
|
||||||
|
|
||||||
createNotificationIcon: function() {
|
|
||||||
return new St.Icon({ icon_name: 'bluetooth-active',
|
|
||||||
icon_type: St.IconType.SYMBOLIC,
|
|
||||||
icon_size: this.ICON_SIZE });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const AuthNotification = new Lang.Class({
|
const AuthNotification = new Lang.Class({
|
||||||
Name: 'AuthNotification',
|
Name: 'AuthNotification',
|
||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
@ -412,7 +376,7 @@ const ConfirmNotification = new Lang.Class({
|
|||||||
this._applet = applet;
|
this._applet = applet;
|
||||||
this._devicePath = device_path;
|
this._devicePath = device_path;
|
||||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||||
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
|
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
|
||||||
|
|
||||||
this.addButton('matches', _("Matches"));
|
this.addButton('matches', _("Matches"));
|
||||||
this.addButton('does-not-match', _("Does not match"));
|
this.addButton('does-not-match', _("Does not match"));
|
||||||
@ -448,6 +412,7 @@ const PinNotification = new Lang.Class({
|
|||||||
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
|
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
|
||||||
let key = event.get_key_symbol();
|
let key = event.get_key_symbol();
|
||||||
if (key == Clutter.KEY_Return) {
|
if (key == Clutter.KEY_Return) {
|
||||||
|
if (this._canActivateOkButton())
|
||||||
this.emit('action-invoked', 'ok');
|
this.emit('action-invoked', 'ok');
|
||||||
return true;
|
return true;
|
||||||
} else if (key == Clutter.KEY_Escape) {
|
} else if (key == Clutter.KEY_Escape) {
|
||||||
@ -461,6 +426,12 @@ const PinNotification = new Lang.Class({
|
|||||||
this.addButton('ok', _("OK"));
|
this.addButton('ok', _("OK"));
|
||||||
this.addButton('cancel', _("Cancel"));
|
this.addButton('cancel', _("Cancel"));
|
||||||
|
|
||||||
|
this.setButtonSensitive('ok', this._canActivateOkButton());
|
||||||
|
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.setButtonSensitive('ok', this._canActivateOkButton());
|
||||||
|
}));
|
||||||
|
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||||
if (action == 'ok') {
|
if (action == 'ok') {
|
||||||
if (this._numeric) {
|
if (this._numeric) {
|
||||||
@ -483,6 +454,14 @@ const PinNotification = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_canActivateOkButton: function() {
|
||||||
|
// PINs have a fixed length of 6
|
||||||
|
if (this._numeric)
|
||||||
|
return this._entry.clutter_text.text.length == 6;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
grabFocus: function(lockTray) {
|
grabFocus: function(lockTray) {
|
||||||
this.parent(lockTray);
|
this.parent(lockTray);
|
||||||
global.stage.set_key_focus(this._entry);
|
global.stage.set_key_focus(this._entry);
|
||||||
|
@ -1,47 +1,198 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
|
||||||
const Gkbd = imports.gi.Gkbd;
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var IBus = imports.gi.IBus;
|
||||||
|
if (!('new_async' in IBus.Bus))
|
||||||
|
throw "IBus version is too old";
|
||||||
|
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
|
||||||
|
} catch (e) {
|
||||||
|
var IBus = null;
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
|
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
|
||||||
|
const KEY_CURRENT_INPUT_SOURCE = 'current';
|
||||||
|
const KEY_INPUT_SOURCES = 'sources';
|
||||||
|
|
||||||
|
const INPUT_SOURCE_TYPE_XKB = 'xkb';
|
||||||
|
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
|
||||||
|
|
||||||
|
const IBusManager = new Lang.Class({
|
||||||
|
Name: 'IBusManager',
|
||||||
|
|
||||||
|
_init: function(readyCallback) {
|
||||||
|
if (!IBus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IBus.init();
|
||||||
|
|
||||||
|
this._readyCallback = readyCallback;
|
||||||
|
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
|
||||||
|
|
||||||
|
this._ibus = null;
|
||||||
|
this._panelService = null;
|
||||||
|
this._engines = {};
|
||||||
|
this._ready = false;
|
||||||
|
this._registerPropertiesId = 0;
|
||||||
|
|
||||||
|
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
|
||||||
|
Gio.BusNameWatcherFlags.NONE,
|
||||||
|
Lang.bind(this, this._onNameAppeared),
|
||||||
|
Lang.bind(this, this._clear));
|
||||||
|
},
|
||||||
|
|
||||||
|
_clear: function() {
|
||||||
|
if (this._panelService)
|
||||||
|
this._panelService.destroy();
|
||||||
|
if (this._ibus)
|
||||||
|
this._ibus.destroy();
|
||||||
|
|
||||||
|
this._ibus = null;
|
||||||
|
this._panelService = null;
|
||||||
|
this._candidatePopup.setPanelService(null);
|
||||||
|
this._engines = {};
|
||||||
|
this._ready = false;
|
||||||
|
this._registerPropertiesId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNameAppeared: function() {
|
||||||
|
this._ibus = IBus.Bus.new_async();
|
||||||
|
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onConnected: function() {
|
||||||
|
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
|
||||||
|
this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
||||||
|
IBus.BusNameFlag.REPLACE_EXISTING,
|
||||||
|
-1, null,
|
||||||
|
Lang.bind(this, this._initPanelService));
|
||||||
|
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
|
||||||
|
},
|
||||||
|
|
||||||
|
_initEngines: function(ibus, result) {
|
||||||
|
let enginesList = this._ibus.list_engines_async_finish(result);
|
||||||
|
if (enginesList) {
|
||||||
|
for (let i = 0; i < enginesList.length; ++i) {
|
||||||
|
let name = enginesList[i].get_name();
|
||||||
|
this._engines[name] = enginesList[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateReadiness();
|
||||||
|
},
|
||||||
|
|
||||||
|
_initPanelService: function(ibus, result) {
|
||||||
|
let success = this._ibus.request_name_async_finish(result);
|
||||||
|
if (success) {
|
||||||
|
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
||||||
|
object_path: IBus.PATH_PANEL });
|
||||||
|
this._candidatePopup.setPanelService(this._panelService);
|
||||||
|
// Need to set this to get 'global-engine-changed' emitions
|
||||||
|
this._ibus.set_watch_ibus_signal(true);
|
||||||
|
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
|
||||||
|
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||||
|
this._resetProperties();
|
||||||
|
} else {
|
||||||
|
this._clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateReadiness();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateReadiness: function() {
|
||||||
|
this._ready = (Object.keys(this._engines).length > 0 &&
|
||||||
|
this._panelService != null);
|
||||||
|
|
||||||
|
if (this._ready && this._readyCallback)
|
||||||
|
this._readyCallback();
|
||||||
|
},
|
||||||
|
|
||||||
|
_resetProperties: function() {
|
||||||
|
this.emit('properties-registered', null);
|
||||||
|
|
||||||
|
if (this._registerPropertiesId != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._registerPropertiesId =
|
||||||
|
this._panelService.connect('register-properties', Lang.bind(this, function(p, props) {
|
||||||
|
if (!props.get(0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._panelService.disconnect(this._registerPropertiesId);
|
||||||
|
this._registerPropertiesId = 0;
|
||||||
|
|
||||||
|
this.emit('properties-registered', props);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateProperty: function(panel, prop) {
|
||||||
|
this.emit('property-updated', prop);
|
||||||
|
},
|
||||||
|
|
||||||
|
activateProperty: function(key, state) {
|
||||||
|
this._panelService.property_activate(key, state);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasProperties: function(id) {
|
||||||
|
if (id == 'anthy')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEngineDesc: function(id) {
|
||||||
|
if (!IBus || !this._ready)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return this._engines[id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(IBusManager.prototype);
|
||||||
|
|
||||||
const LayoutMenuItem = new Lang.Class({
|
const LayoutMenuItem = new Lang.Class({
|
||||||
Name: 'LayoutMenuItem',
|
Name: 'LayoutMenuItem',
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
Extends: PopupMenu.PopupBaseMenuItem,
|
||||||
|
|
||||||
_init: function(config, id, indicator, long_name) {
|
_init: function(displayName, shortName) {
|
||||||
this.parent();
|
this.parent();
|
||||||
|
|
||||||
this._config = config;
|
this.label = new St.Label({ text: displayName });
|
||||||
this._id = id;
|
this.indicator = new St.Label({ text: shortName });
|
||||||
this.label = new St.Label({ text: long_name });
|
|
||||||
this.indicator = indicator;
|
|
||||||
this.addActor(this.label);
|
this.addActor(this.label);
|
||||||
this.addActor(this.indicator);
|
this.addActor(this.indicator);
|
||||||
},
|
this.actor.label_actor = this.label;
|
||||||
|
|
||||||
activate: function(event) {
|
|
||||||
this.parent(event);
|
|
||||||
|
|
||||||
this._config.lock_group(this._id);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const XKBIndicator = new Lang.Class({
|
const InputSourceIndicator = new Lang.Class({
|
||||||
Name: 'XKBIndicator',
|
Name: 'InputSourceIndicator',
|
||||||
Extends: PanelMenu.Button,
|
Extends: PanelMenu.Button,
|
||||||
|
|
||||||
|
_propertiesWhitelist: [
|
||||||
|
'InputMode',
|
||||||
|
'TypingMode',
|
||||||
|
'DictMode'
|
||||||
|
],
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent(0.0);
|
this.parent(0.0, _("Keyboard"));
|
||||||
|
|
||||||
this._container = new Shell.GenericContainer();
|
this._container = new Shell.GenericContainer();
|
||||||
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
||||||
@ -50,122 +201,325 @@ const XKBIndicator = new Lang.Class({
|
|||||||
this.actor.add_actor(this._container);
|
this.actor.add_actor(this._container);
|
||||||
this.actor.add_style_class_name('panel-status-button');
|
this.actor.add_style_class_name('panel-status-button');
|
||||||
|
|
||||||
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
|
this._labelActors = {};
|
||||||
this._container.add_actor(this._iconActor);
|
this._layoutItems = {};
|
||||||
this._labelActors = [ ];
|
|
||||||
this._layoutItems = [ ];
|
|
||||||
|
|
||||||
this._showFlags = false;
|
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||||
this._config = Gkbd.Configuration.get();
|
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||||
this._config.connect('changed', Lang.bind(this, this._syncConfig));
|
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
|
||||||
this._config.connect('group-changed', Lang.bind(this, this._syncGroup));
|
|
||||||
this._config.start_listen();
|
|
||||||
|
|
||||||
this._syncConfig();
|
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||||
|
this._xkbInfo = new GnomeDesktop.XkbInfo();
|
||||||
|
|
||||||
|
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
this.menu.addMenuItem(this._propSeparator);
|
||||||
|
this._propSection = new PopupMenu.PopupMenuSection();
|
||||||
|
this.menu.addMenuItem(this._propSection);
|
||||||
|
this._propSection.actor.hide();
|
||||||
|
|
||||||
|
this._properties = null;
|
||||||
|
|
||||||
|
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
|
||||||
|
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
|
||||||
|
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
|
||||||
|
this._inputSourcesChanged();
|
||||||
|
|
||||||
if (global.session_type == Shell.SessionType.USER) {
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
|
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
||||||
Main.overview.hide();
|
|
||||||
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
}));
|
this._sessionUpdated();
|
||||||
}
|
|
||||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||||
},
|
},
|
||||||
|
|
||||||
_adjustGroupNames: function(names) {
|
_sessionUpdated: function() {
|
||||||
// Disambiguate duplicate names with a subscript
|
// re-using "allowSettings" for the keyboard layout is a bit shady,
|
||||||
// This is O(N^2) to avoid sorting names
|
// but at least for now it is used as "allow popping up windows
|
||||||
// but N <= 4 so who cares?
|
// from shell menus"; we can always add a separate sessionMode
|
||||||
|
// option if need arises.
|
||||||
for (let i = 0; i < names.length; i++) {
|
this._showLayoutItem.visible = Main.sessionMode.allowSettings;
|
||||||
let name = names[i];
|
|
||||||
let cnt = 0;
|
|
||||||
for (let j = i + 1; j < names.length; j++) {
|
|
||||||
if (names[j] == name) {
|
|
||||||
cnt++;
|
|
||||||
// U+2081 SUBSCRIPT ONE
|
|
||||||
names[j] = name + String.fromCharCode(0x2081 + cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cnt != 0)
|
|
||||||
names[i] = name + '\u2081';
|
|
||||||
}
|
|
||||||
|
|
||||||
return names;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_syncConfig: function() {
|
_currentInputSourceChanged: function() {
|
||||||
this._showFlags = this._config.if_flags_shown();
|
let nVisibleSources = Object.keys(this._layoutItems).length;
|
||||||
if (this._showFlags) {
|
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||||
this._container.set_skip_paint(this._iconActor, false);
|
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
|
||||||
} else {
|
let hasProperties;
|
||||||
this._container.set_skip_paint(this._iconActor, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let groups = this._config.get_group_names();
|
if (newLayoutItem)
|
||||||
if (groups.length > 1) {
|
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
|
||||||
this.actor.show();
|
else
|
||||||
} else {
|
hasProperties = false;
|
||||||
|
|
||||||
|
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
|
||||||
|
// This source index might be invalid if we weren't able
|
||||||
|
// to build a menu item for it, so we hide ourselves since
|
||||||
|
// we can't fix it here. *shrug*
|
||||||
|
|
||||||
|
// We also hide if we have only one visible source unless
|
||||||
|
// it's an IBus source with properties.
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this._layoutItems.length; i++)
|
this.actor.show();
|
||||||
|
|
||||||
|
if (this._layoutItems[this._currentSourceIndex]) {
|
||||||
|
this._layoutItems[this._currentSourceIndex].setShowDot(false);
|
||||||
|
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
newLayoutItem.setShowDot(true);
|
||||||
|
|
||||||
|
let newLabelActor = this._labelActors[newCurrentSourceIndex];
|
||||||
|
this._container.set_skip_paint(newLabelActor, false);
|
||||||
|
|
||||||
|
if (hasProperties)
|
||||||
|
newLabelActor.set_text(newLayoutItem.indicator.get_text());
|
||||||
|
|
||||||
|
this._currentSourceIndex = newCurrentSourceIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
_inputSourcesChanged: function() {
|
||||||
|
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||||
|
let nSources = sources.n_children();
|
||||||
|
|
||||||
|
for (let i in this._layoutItems)
|
||||||
this._layoutItems[i].destroy();
|
this._layoutItems[i].destroy();
|
||||||
|
|
||||||
for (let i = 0; i < this._labelActors.length; i++)
|
for (let i in this._labelActors)
|
||||||
this._labelActors[i].destroy();
|
this._labelActors[i].destroy();
|
||||||
|
|
||||||
let short_names = this._adjustGroupNames(this._config.get_short_group_names());
|
this._layoutItems = {};
|
||||||
|
this._labelActors = {};
|
||||||
|
|
||||||
this._selectedLayout = null;
|
let infos = [];
|
||||||
this._layoutItems = [ ];
|
let infosByShortName = {};
|
||||||
this._selectedLabel = null;
|
|
||||||
this._labelActors = [ ];
|
for (let i = 0; i < nSources; i++) {
|
||||||
for (let i = 0; i < groups.length; i++) {
|
let info = { exists: false };
|
||||||
let icon_name = this._config.get_group_name(i);
|
let [type, id] = sources.get_child_value(i).deep_unpack();
|
||||||
let actor;
|
|
||||||
if (this._showFlags)
|
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||||
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
|
[info.exists, info.displayName, info.shortName, , ] =
|
||||||
else
|
this._xkbInfo.get_layout_info(id);
|
||||||
actor = new St.Label({ text: short_names[i] });
|
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||||
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
|
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||||
item._short_group_name = short_names[i];
|
if (engineDesc) {
|
||||||
item._icon_name = icon_name;
|
let language = IBus.get_language_name(engineDesc.get_language());
|
||||||
this._layoutItems.push(item);
|
|
||||||
|
info.exists = true;
|
||||||
|
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
|
||||||
|
info.shortName = this._makeEngineShortName(engineDesc);
|
||||||
|
info.ibusEngineId = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.exists)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
info.sourceIndex = i;
|
||||||
|
|
||||||
|
if (!(info.shortName in infosByShortName))
|
||||||
|
infosByShortName[info.shortName] = [];
|
||||||
|
infosByShortName[info.shortName].push(info);
|
||||||
|
infos.push(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < infos.length; i++) {
|
||||||
|
let info = infos[i];
|
||||||
|
if (infosByShortName[info.shortName].length > 1) {
|
||||||
|
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
|
||||||
|
info.shortName += String.fromCharCode(0x2080 + sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = new LayoutMenuItem(info.displayName, info.shortName);
|
||||||
|
item.ibusEngineId = info.ibusEngineId;
|
||||||
|
this._layoutItems[info.sourceIndex] = item;
|
||||||
this.menu.addMenuItem(item, i);
|
this.menu.addMenuItem(item, i);
|
||||||
|
item.connect('activate', Lang.bind(this, function() {
|
||||||
|
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
||||||
|
GLib.Variant.new_uint32(info.sourceIndex));
|
||||||
|
}));
|
||||||
|
|
||||||
let shortLabel = new St.Label({ text: short_names[i] });
|
let shortLabel = new St.Label({ text: info.shortName });
|
||||||
this._labelActors.push(shortLabel);
|
this._labelActors[info.sourceIndex] = shortLabel;
|
||||||
this._container.add_actor(shortLabel);
|
this._container.add_actor(shortLabel);
|
||||||
this._container.set_skip_paint(shortLabel, true);
|
this._container.set_skip_paint(shortLabel, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._syncGroup();
|
this._currentInputSourceChanged();
|
||||||
},
|
},
|
||||||
|
|
||||||
_syncGroup: function() {
|
_showLayout: function() {
|
||||||
let selected = this._config.get_current_group();
|
Main.overview.hide();
|
||||||
|
|
||||||
if (this._selectedLayout) {
|
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||||
this._selectedLayout.setShowDot(false);
|
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||||
this._selectedLayout = null;
|
let [type, id] = sources.get_child_value(current).deep_unpack();
|
||||||
|
let xkbLayout = '';
|
||||||
|
let xkbVariant = '';
|
||||||
|
|
||||||
|
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||||
|
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
|
||||||
|
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||||
|
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||||
|
if (engineDesc) {
|
||||||
|
xkbLayout = engineDesc.get_layout();
|
||||||
|
xkbVariant = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._selectedLabel) {
|
if (!xkbLayout || xkbLayout.length == 0)
|
||||||
this._container.set_skip_paint(this._selectedLabel, true);
|
return;
|
||||||
this._selectedLabel = null;
|
|
||||||
|
let description = xkbLayout;
|
||||||
|
if (xkbVariant.length > 0)
|
||||||
|
description = description + '\t' + xkbVariant;
|
||||||
|
|
||||||
|
Util.spawn(['gkbd-keyboard-display', '-l', description]);
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeEngineShortName: function(engineDesc) {
|
||||||
|
let symbol = engineDesc.get_symbol();
|
||||||
|
if (symbol && symbol[0])
|
||||||
|
return symbol;
|
||||||
|
|
||||||
|
let langCode = engineDesc.get_language().split('_', 1)[0];
|
||||||
|
if (langCode.length == 2 || langCode.length == 3)
|
||||||
|
return langCode.toLowerCase();
|
||||||
|
|
||||||
|
return String.fromCharCode(0x2328); // keyboard glyph
|
||||||
|
},
|
||||||
|
|
||||||
|
_propertyWhitelisted: function(prop) {
|
||||||
|
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
|
||||||
|
let key = prop.get_key();
|
||||||
|
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_ibusPropertiesRegistered: function(im, props) {
|
||||||
|
this._properties = props;
|
||||||
|
this._buildPropSection();
|
||||||
|
},
|
||||||
|
|
||||||
|
_ibusPropertyUpdated: function(im, prop) {
|
||||||
|
if (!this._propertyWhitelisted(prop))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._updateSubProperty(this._properties, prop))
|
||||||
|
this._buildPropSection();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSubProperty: function(props, prop) {
|
||||||
|
if (!props)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let p;
|
||||||
|
for (let i = 0; (p = props.get(i)) != null; ++i) {
|
||||||
|
if (p.get_key() == prop.get_key() && p.get_prop_type() == prop.get_prop_type()) {
|
||||||
|
p.update(prop);
|
||||||
|
return true;
|
||||||
|
} else if (p.get_prop_type() == IBus.PropType.MENU) {
|
||||||
|
if (this._updateSubProperty(p.get_sub_props(), prop))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateIndicatorLabel: function(text) {
|
||||||
|
let layoutItem = this._layoutItems[this._currentSourceIndex];
|
||||||
|
let hasProperties;
|
||||||
|
|
||||||
|
if (layoutItem)
|
||||||
|
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
|
||||||
|
else
|
||||||
|
hasProperties = false;
|
||||||
|
|
||||||
|
if (hasProperties)
|
||||||
|
this._labelActors[this._currentSourceIndex].set_text(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildPropSection: function() {
|
||||||
|
this._propSeparator.actor.hide();
|
||||||
|
this._propSection.actor.hide();
|
||||||
|
this._propSection.removeAll();
|
||||||
|
|
||||||
|
this._buildPropSubMenu(this._propSection, this._properties);
|
||||||
|
|
||||||
|
if (!this._propSection.isEmpty()) {
|
||||||
|
this._propSection.actor.show();
|
||||||
|
this._propSeparator.actor.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildPropSubMenu: function(menu, props) {
|
||||||
|
if (!props)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let radioGroup = [];
|
||||||
|
let p;
|
||||||
|
for (let i = 0; (p = props.get(i)) != null; ++i) {
|
||||||
|
let prop = p;
|
||||||
|
|
||||||
|
if (!this._propertyWhitelisted(prop) ||
|
||||||
|
!prop.get_visible())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (prop.get_key() == 'InputMode') {
|
||||||
|
let text;
|
||||||
|
if (prop.get_symbol)
|
||||||
|
text = prop.get_symbol().get_text();
|
||||||
|
else
|
||||||
|
text = prop.get_label().get_text();
|
||||||
|
|
||||||
|
if (text && text.length > 0 && text.length < 3)
|
||||||
|
this._updateIndicatorLabel(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = this._layoutItems[selected];
|
let item;
|
||||||
|
let type = prop.get_prop_type();
|
||||||
|
if (type == IBus.PropType.MENU) {
|
||||||
|
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
|
||||||
|
this._buildPropSubMenu(item.menu, prop.get_sub_props());
|
||||||
|
} else if (type == IBus.PropType.RADIO) {
|
||||||
|
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
|
||||||
|
item.prop = prop;
|
||||||
|
radioGroup.push(item);
|
||||||
|
item.radioGroup = radioGroup;
|
||||||
|
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
|
||||||
|
item.connect('activate', Lang.bind(this, function() {
|
||||||
|
if (item.prop.get_state() == IBus.PropState.CHECKED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let group = item.radioGroup;
|
||||||
|
for (let i = 0; i < group.length; ++i) {
|
||||||
|
if (group[i] == item) {
|
||||||
item.setShowDot(true);
|
item.setShowDot(true);
|
||||||
|
item.prop.set_state(IBus.PropState.CHECKED);
|
||||||
|
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||||
|
IBus.PropState.CHECKED);
|
||||||
|
} else {
|
||||||
|
group[i].setShowDot(false);
|
||||||
|
group[i].prop.set_state(IBus.PropState.UNCHECKED);
|
||||||
|
this._ibusManager.activateProperty(group[i].prop.get_key(),
|
||||||
|
IBus.PropState.UNCHECKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this._iconActor.icon_name = item._icon_name;
|
item.setSensitive(prop.get_sensitive());
|
||||||
this._selectedLabel = this._labelActors[selected];
|
menu.addMenuItem(item);
|
||||||
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
|
}
|
||||||
|
|
||||||
this._selectedLayout = item;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
||||||
@ -173,16 +527,12 @@ const XKBIndicator = new Lang.Class({
|
|||||||
// for the height of all children, but we ignore the results
|
// for the height of all children, but we ignore the results
|
||||||
// for those we don't actually display.
|
// for those we don't actually display.
|
||||||
let max_min_width = 0, max_natural_width = 0;
|
let max_min_width = 0, max_natural_width = 0;
|
||||||
if (this._showFlags)
|
|
||||||
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
|
|
||||||
|
|
||||||
for (let i = 0; i < this._labelActors.length; i++) {
|
for (let i in this._labelActors) {
|
||||||
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
|
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
|
||||||
if (!this._showFlags) {
|
|
||||||
max_min_width = Math.max(max_min_width, min_width);
|
max_min_width = Math.max(max_min_width, min_width);
|
||||||
max_natural_width = Math.max(max_natural_width, natural_width);
|
max_natural_width = Math.max(max_natural_width, natural_width);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
alloc.min_size = max_min_width;
|
alloc.min_size = max_min_width;
|
||||||
alloc.natural_size = max_natural_width;
|
alloc.natural_size = max_natural_width;
|
||||||
@ -190,16 +540,12 @@ const XKBIndicator = new Lang.Class({
|
|||||||
|
|
||||||
_containerGetPreferredHeight: function(container, for_width, alloc) {
|
_containerGetPreferredHeight: function(container, for_width, alloc) {
|
||||||
let max_min_height = 0, max_natural_height = 0;
|
let max_min_height = 0, max_natural_height = 0;
|
||||||
if (this._showFlags)
|
|
||||||
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
|
|
||||||
|
|
||||||
for (let i = 0; i < this._labelActors.length; i++) {
|
for (let i in this._labelActors) {
|
||||||
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
|
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
|
||||||
if (!this._showFlags) {
|
|
||||||
max_min_height = Math.max(max_min_height, min_height);
|
max_min_height = Math.max(max_min_height, min_height);
|
||||||
max_natural_height = Math.max(max_natural_height, natural_height);
|
max_natural_height = Math.max(max_natural_height, natural_height);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
alloc.min_size = max_min_height;
|
alloc.min_size = max_min_height;
|
||||||
alloc.natural_size = max_natural_height;
|
alloc.natural_size = max_natural_height;
|
||||||
@ -212,8 +558,7 @@ const XKBIndicator = new Lang.Class({
|
|||||||
box.y2 -= box.y1;
|
box.y2 -= box.y1;
|
||||||
box.y1 = 0;
|
box.y1 = 0;
|
||||||
|
|
||||||
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
|
for (let i in this._labelActors)
|
||||||
for (let i = 0; i < this._labelActors.length; i++)
|
|
||||||
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
|
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|