Compare commits
507 Commits
3.8.1
...
wip/smartc
Author | SHA1 | Date | |
---|---|---|---|
d139adc25f | |||
0d151f8d94 | |||
e3fdc2858d | |||
05dcd8575c | |||
214440fde4 | |||
e3621e13ac | |||
df0aace025 | |||
023f4b31d9 | |||
16fba7bd5f | |||
ebce362c05 | |||
c4ca17d127 | |||
3455dd1e76 | |||
57abfab923 | |||
3a13e7484a | |||
a04895383a | |||
43357a1b69 | |||
f38d5a9c06 | |||
696efcdf20 | |||
728c3a72ad | |||
c8c839a569 | |||
6827dacb4b | |||
246271fd9d | |||
4305ebc5b0 | |||
39bff8bfbc | |||
fc6a0c1006 | |||
d6dc9af5ff | |||
e62045eb7f | |||
bda3e53511 | |||
6295d0fc6c | |||
3271e65d56 | |||
f8225141dc | |||
8e9e583b79 | |||
bbadfce65a | |||
9e191d681f | |||
a3236997be | |||
be961cd60e | |||
4efd363134 | |||
7bdee9ce05 | |||
7dc9a9bf2f | |||
87245c7b33 | |||
db497a2ecf | |||
9d250bec05 | |||
1371f69810 | |||
82ee6aed7f | |||
d730fd0a5f | |||
cb4e4bb2db | |||
6fd3c0fbb4 | |||
13170fbb3c | |||
7e7295f259 | |||
d30cb2d4d9 | |||
f4100ac507 | |||
1a0415a100 | |||
1cc5bb5ec4 | |||
43661fff76 | |||
509bdceac2 | |||
835cb8caa5 | |||
ed32adc9bb | |||
44f8fecf84 | |||
0cee0e08e9 | |||
73cd595b73 | |||
c50b23e9ca | |||
d1a763bc21 | |||
de8ca5c1b5 | |||
14372ba7f3 | |||
ca861d8ff9 | |||
37a316e66c | |||
22c8be6645 | |||
8cce1b4f6c | |||
040bb9354a | |||
fb0f9cd1a1 | |||
3816db03f5 | |||
d802416dae | |||
2cd41773ba | |||
8ce5b087bc | |||
03fd74da4e | |||
ed178b702f | |||
b4567acc6b | |||
953f44bcc5 | |||
be4f259b71 | |||
e9531487d9 | |||
d715110961 | |||
3c31908e08 | |||
09d34a2129 | |||
e5e3f3c299 | |||
bcd1c793ea | |||
185152a74a | |||
ded99b9a09 | |||
55a04bbf2b | |||
7d5d7453c2 | |||
952f58153f | |||
d0d981435a | |||
fd83990d8a | |||
ad0c4caf1c | |||
126f0ed95d | |||
a2b499c460 | |||
5a5b3bf291 | |||
a4a6e7cf53 | |||
658db43ad3 | |||
cfecd063c9 | |||
a8fe063726 | |||
dcf637edd8 | |||
c0345f1088 | |||
0cbdfca141 | |||
cb1f696e22 | |||
cf7355e4d0 | |||
dab8c5ea56 | |||
4b889eac32 | |||
86835db8f2 | |||
bc317bf3f2 | |||
5c036eadf9 | |||
263474705b | |||
1e781ec78f | |||
ef1eabf033 | |||
2fa40555e6 | |||
2aae272d86 | |||
7db0900cc8 | |||
c1e2d66abd | |||
78b1ba56ce | |||
5385205b8e | |||
5c8bbb511e | |||
fde01f0b71 | |||
1c04ae3216 | |||
35b4907e52 | |||
2431b8e021 | |||
bd5c04b923 | |||
6e00b6e214 | |||
c9b079cbb5 | |||
9163372786 | |||
dabcd29fb6 | |||
d36e435801 | |||
a18fb27d0f | |||
2dbe511519 | |||
e031a5d28b | |||
f9b32474b0 | |||
53d268a7ef | |||
70da558802 | |||
11215374ff | |||
cb45a38838 | |||
bb4d430ebf | |||
c17f84ca23 | |||
0ae1f9ffc7 | |||
d15bcd9845 | |||
f79a11d993 | |||
9391d9d11b | |||
aee90a3116 | |||
ffac5279a7 | |||
318283fc70 | |||
3582ba0c77 | |||
985d0c786c | |||
9c8c282e08 | |||
93dc7a51c0 | |||
393577ee78 | |||
eef593a34e | |||
3790e924e9 | |||
2f165aade8 | |||
2c502aec45 | |||
6bbf246752 | |||
0b3e8e29cf | |||
d509ab7779 | |||
502a9aefdc | |||
ccba18aa8f | |||
586ebcd5be | |||
317b9a9c87 | |||
67f10ea7eb | |||
793edd3a2b | |||
5dabaf2fe9 | |||
1d9b25f771 | |||
74cd20116a | |||
867695eb4f | |||
9e3592ebf3 | |||
f8ec14d625 | |||
1f21e50663 | |||
7403545a48 | |||
1d95b317ba | |||
f7568b69d4 | |||
8d7649eaec | |||
10b1c7e8a3 | |||
b1c936164c | |||
74ad6abfc2 | |||
9786b2d096 | |||
ea02380c15 | |||
048d5dc914 | |||
aa6b63373e | |||
0b219bf8cb | |||
109b29aeb5 | |||
bc069b99ec | |||
16fa186b63 | |||
e70c0d3e2d | |||
8d47afb195 | |||
a01469fb08 | |||
929636ebd0 | |||
681ef1efec | |||
4f14f122bd | |||
67e9ed5d60 | |||
5c25497e16 | |||
626cbea9cf | |||
aa7ed319e9 | |||
580bd67278 | |||
9e44978aed | |||
64b5ec0b11 | |||
48f9ea3d9e | |||
798a0ca240 | |||
5ee6cbd4c8 | |||
3b219a6a9a | |||
536ff6f561 | |||
013b6aa44a | |||
0e7d3a7558 | |||
8bd4895538 | |||
465c77ddcf | |||
6ef2d4a4cc | |||
7ae7f046c2 | |||
f4051e810e | |||
e6c239d0f3 | |||
35a7a3c1ac | |||
a0991c8261 | |||
9520880568 | |||
719d793e22 | |||
c7fb65c78e | |||
dd74ea99a7 | |||
c6fe6eb7ab | |||
2cbee05c8a | |||
308b1d6039 | |||
4cd832c05a | |||
2af4925d95 | |||
5e52f0e2a8 | |||
a46a68d616 | |||
203d7c4b43 | |||
24703ffa57 | |||
5cd913a527 | |||
91844e48e9 | |||
6c2f3d1d17 | |||
9c222c7e5c | |||
2249da7976 | |||
a55288bda0 | |||
e645edbda7 | |||
aee7cd73c4 | |||
380a71dd21 | |||
23dd5cc160 | |||
847cb5b972 | |||
cc64091f9c | |||
b68eb44ca5 | |||
403540e8a1 | |||
c39497222f | |||
9e56e668e0 | |||
41ae93dba0 | |||
6c527c1bb4 | |||
90c7876341 | |||
10b77a8305 | |||
1902f4773b | |||
4b95be6a95 | |||
61323926e0 | |||
e30d18febe | |||
9f2f80ae4f | |||
bd6e0ceb81 | |||
673d7038d8 | |||
3a6231dcc1 | |||
9d54e46ce7 | |||
63e6d11892 | |||
0509bb9bb4 | |||
5c78908a5f | |||
5a2269c6c6 | |||
a7e9655e32 | |||
1ec82d2ddd | |||
98eaef621a | |||
74a6ca58ef | |||
747faa43ae | |||
32a53f7412 | |||
9c94e9813c | |||
19749bb37f | |||
5ab4c484a5 | |||
e602199bfb | |||
62e1c08dd6 | |||
37e2b60cd3 | |||
f299078585 | |||
bf0a0d5bad | |||
365cda386c | |||
5de5b7a66a | |||
d6de0a64ed | |||
26f8441f73 | |||
51bca08386 | |||
1579aad362 | |||
a2a580954a | |||
944c28f3b3 | |||
c13a597fe0 | |||
86e0ae0b93 | |||
f71ffed3c7 | |||
cae96d9023 | |||
676b649731 | |||
634ce34e00 | |||
6c3096f71f | |||
991ed2da72 | |||
907f02cd28 | |||
9ba78ce04a | |||
14a3d9f7fb | |||
f73a01295c | |||
ccfa3d3be1 | |||
727e4c0b37 | |||
6ce470b9b2 | |||
4b7e230531 | |||
aefe0d3ddd | |||
bbb23b515f | |||
976166a04a | |||
b20129c37e | |||
4fe1360b2c | |||
f0113c5ff5 | |||
a259016436 | |||
9a79c71e88 | |||
e62d22a50e | |||
402f2d939c | |||
374aee75d8 | |||
88ce65266e | |||
e0a6a623d2 | |||
d0310bd745 | |||
35c665156b | |||
3754a76f10 | |||
db2e6e5b95 | |||
d5f95db68d | |||
d96726c392 | |||
d1c54f55e6 | |||
176a73f4e9 | |||
54a9592e19 | |||
b2aa29e221 | |||
0eba0f8dd3 | |||
d8d0afff0b | |||
b799e8c0a6 | |||
583255e1a8 | |||
c68ccbf263 | |||
d658ec8de2 | |||
8727661c1c | |||
731e8bfe2b | |||
f88d9c06f5 | |||
248a0c1b6c | |||
434f1edb25 | |||
2a550e6466 | |||
4f4132943d | |||
62760d5b2d | |||
34aa501637 | |||
6330379f77 | |||
30c64baa7f | |||
cdbed0c615 | |||
f6e7034172 | |||
cd9c5b9c5d | |||
7833e21b01 | |||
12ba2b222f | |||
8583ca73e4 | |||
b588ae4e0e | |||
28abc15c00 | |||
4d663680d8 | |||
6505f8e94a | |||
ff3c20dda2 | |||
c698dee071 | |||
8891a41793 | |||
026f61f5aa | |||
af063dc2f2 | |||
3075f3cfe4 | |||
419f2dca15 | |||
5803f69605 | |||
bfd1cc99a0 | |||
a6a2cea414 | |||
10e857cebe | |||
ed76b54511 | |||
9659d056b7 | |||
b79b0952b4 | |||
fa44dc7d16 | |||
b0dc841e00 | |||
c72ae375c8 | |||
0fd6ae5330 | |||
c8792ccfa6 | |||
b689972e67 | |||
ae6d7bbfa3 | |||
2591bc90ac | |||
61fe000daa | |||
c864ebeabb | |||
24c530611f | |||
d44d00f0f4 | |||
dc3082e66d | |||
c61573a8b7 | |||
3b95560d32 | |||
990f68375e | |||
1290c98c9b | |||
08599afdd4 | |||
bb040918e3 | |||
6fac33ea7d | |||
e16c16b3ef | |||
1eeeead78f | |||
046a1a7af8 | |||
db89648f62 | |||
9c839cdc70 | |||
938b1ff06a | |||
ecd838bf01 | |||
855a31ff25 | |||
7464add904 | |||
7514607129 | |||
12a1d7b38d | |||
d6fe008b2c | |||
d920da6624 | |||
e2c86cef47 | |||
8e270dc246 | |||
22b6a25408 | |||
cde695d903 | |||
e98eb57e3e | |||
11d997c42b | |||
eab1ab0fac | |||
660b801775 | |||
eb80503bcc | |||
14ceb10555 | |||
d9a4688e98 | |||
092586c931 | |||
31478e9fb4 | |||
cc7630c236 | |||
c29810b2f6 | |||
c1240d3f2c | |||
654f1dd055 | |||
53c609c278 | |||
a2fcbb7e65 | |||
db14dc973a | |||
c29aaa047d | |||
49064ed56d | |||
72bc46d339 | |||
40b895d16b | |||
33cad9a824 | |||
e96d9e0ea1 | |||
ed63bda932 | |||
18107d567d | |||
608818fa9f | |||
9ae2440ec1 | |||
ef9c50e63a | |||
5d50b08351 | |||
123fc19c4e | |||
a9058e471c | |||
bd1f48d9fe | |||
66ab4d217d | |||
7a8b392607 | |||
bfdf069d2d | |||
b4b00a48d9 | |||
e5f226612e | |||
20619ad3c1 | |||
37dce7d4c3 | |||
a67b82e730 | |||
e0b8ad7911 | |||
dbb39d366e | |||
e1de36398d | |||
871c28aeeb | |||
c84dc6254d | |||
60cb1ad7c5 | |||
4a5ff5dcfb | |||
5c40307745 | |||
39426f03e6 | |||
92e5d2b8f5 | |||
09aa59f98b | |||
c9c1c89a27 | |||
96b76709e9 | |||
ca3107e21b | |||
2e4f223207 | |||
17df668186 | |||
aef70152de | |||
e0252f35be | |||
0f47534766 | |||
54feaa67e8 | |||
64ecfa49eb | |||
fdae613b14 | |||
c57c08b2c6 | |||
d2103995cb | |||
196fb0f16e | |||
c0afe7260a | |||
099c8703ae | |||
b03e480dbf | |||
8430353389 | |||
a123ec94ef | |||
4a2f54f6ff | |||
b5c85eaeca | |||
8b3b91d78d | |||
e1de3973fe | |||
c1993a6ffc | |||
ab26fc438a | |||
c37259b01d | |||
bde8cc3285 | |||
59ba5504d0 | |||
65eb5a3d05 | |||
b925322e9e | |||
f0c2ad00f8 | |||
fc53a25a4c | |||
58872d162b | |||
30f1b8f02a | |||
ee4f199a9f | |||
e3957f3bac | |||
2506673514 | |||
7cb12015fd | |||
caaac9b9ec | |||
1dac4d00c4 | |||
b41902f4df | |||
ada70dd683 | |||
7e5d8a8d54 | |||
e981cae27c | |||
d7c377c229 | |||
8772edcd33 | |||
254740cf68 | |||
c3775c0f56 | |||
5ecf40e967 | |||
45026df4bd | |||
b8830f4a09 | |||
811ee1d989 | |||
8c32102e99 | |||
1b135095c7 | |||
becf4396c9 | |||
929e066506 | |||
355ad9ac2c |
136
NEWS
136
NEWS
@ -1,3 +1,139 @@
|
|||||||
|
3.9.4
|
||||||
|
=====
|
||||||
|
* Fix chat entries not being focused when expanded [Jasper; #698778]
|
||||||
|
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
|
||||||
|
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
|
||||||
|
* Round the ends of slider trough [Jasper; #702825]
|
||||||
|
* Add support for "box-shadow: none" [Cosimo; #702782]
|
||||||
|
* Keep chrome below popup windows [Florian; #702338]
|
||||||
|
* Move the session list to a popup menu [Ray; #702818]
|
||||||
|
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
|
||||||
|
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
|
||||||
|
* Fix management of asynchronous background loading [Lionel; #703001]
|
||||||
|
* Rework focus handling [Jasper; #700735]
|
||||||
|
* Optimize box-shadow rendering [Lionel; #689858]
|
||||||
|
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
|
||||||
|
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
|
||||||
|
#702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
|
||||||
|
#698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||||
|
Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
|
||||||
|
Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
|
||||||
|
Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
|
||||||
|
Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
|
||||||
|
|
||||||
|
3.9.3
|
||||||
|
=====
|
||||||
|
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
|
||||||
|
* Tweak timeout for activating windows during XDND [Adel; #700150]
|
||||||
|
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
|
||||||
|
* Fix DND to empty dash [Florian; #684618]
|
||||||
|
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
|
||||||
|
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
|
||||||
|
* Allow session mode specific overrides schema [Florian; #701717]
|
||||||
|
* window-switcher: Only show windows from current workspace by default
|
||||||
|
[Florian; #701214]
|
||||||
|
* logout dialog: Show the correct text right away [Matthias; #702056]
|
||||||
|
* bluetooth: Port to bluez 5 [Emilio; #700891]
|
||||||
|
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
|
||||||
|
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
|
||||||
|
#700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
|
||||||
|
#701849, #702121]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
|
||||||
|
Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
|
||||||
|
Jonh Wendell
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
|
||||||
|
Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
|
||||||
|
eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
|
||||||
|
Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.9.2
|
||||||
|
=====
|
||||||
|
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
|
||||||
|
* Move paint state cache into StWidget [Jasper; #697274]
|
||||||
|
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
|
||||||
|
* Make calendar keyboard navigable [Tanner; #667434]
|
||||||
|
* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
|
||||||
|
* Update how branding appears on login screen [Florian; #694912, #699877]
|
||||||
|
* Allow OSD popups to grow if necessary [Marta; #696523]
|
||||||
|
* Fix offset of shadow offscreen rendering [Lionel; #698301]
|
||||||
|
* Fix insensitive button preventing empty keyring password [Stef; #696304]
|
||||||
|
* Allow cancelling keyring dialog between prompts [Stef; #682830]
|
||||||
|
* modalDialog: Show spinner while working [Stef; #684438]
|
||||||
|
* overview: Only show close buttons for windows that may close [Jasper; #699269]
|
||||||
|
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
|
||||||
|
* Set input-purpose property for password entries [Rui; #700043]
|
||||||
|
* Provide a DBus API for screencasting [Florian; #696247]
|
||||||
|
* overview: Disable hotcorner during DND [Jasper; #698484]
|
||||||
|
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
|
||||||
|
* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
|
||||||
|
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
|
||||||
|
* Update man page [Matthias; #700339]
|
||||||
|
* Add FocusSearch DBus method [Florian; #700536]
|
||||||
|
* Hide frequent view when app monitoring is disabled [Florian; #699714]
|
||||||
|
* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
|
||||||
|
* gdm: Update the session chooser style [Allan; #695742]
|
||||||
|
* Fix some app folders getting truncated at the top [Florian; #694371]
|
||||||
|
* Don't block the message tray while a notification is showing [Jasper; #700639]
|
||||||
|
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
|
||||||
|
* Re-lock screen when restarted after a crash [Colin; #691987]
|
||||||
|
* Synchronize input source switching with key events [Rui; #697007]
|
||||||
|
* Switch input source on modifiers-only accelerator [Rui; #697008]
|
||||||
|
* Allow input source switching in message tray [Rui; #697009]
|
||||||
|
* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
|
||||||
|
Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
|
||||||
|
#698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
|
||||||
|
#700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
|
||||||
|
#700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
|
||||||
|
#700944, #697661, #700854, #700190, #699189, #701097]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||||
|
Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
|
||||||
|
Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
|
||||||
|
Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
|
||||||
|
Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.9.1
|
||||||
|
=====
|
||||||
|
* Add additional toggle-overview keybinding [Matthias; #698251]
|
||||||
|
* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
|
||||||
|
* Disable tray context menu while a notification displays [Jasper; #695800]
|
||||||
|
* Watch GApplication busy state [Cosimo; #697207]
|
||||||
|
* Disable style transitions if animations are disabled [Jasper; #698391]
|
||||||
|
* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
|
||||||
|
* Fix window previews swapping place randomly [Jasper; #694469, #698776]
|
||||||
|
* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
|
||||||
|
* Fix hotcorner regression in RTL locales [Jasper; #698884]
|
||||||
|
* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
|
||||||
|
* Make open-app-menu keybinding a toggle action [Florian; #686756]
|
||||||
|
* Only recognize common URL schemes in notification messages [Monica; #661225]
|
||||||
|
* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
|
||||||
|
#698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
|
||||||
|
#699075, #696720, #649748]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
|
||||||
|
Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
|
||||||
|
Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
|
||||||
|
Žygimantas Beručka [lt]
|
||||||
|
|
||||||
3.8.1
|
3.8.1
|
||||||
=====
|
=====
|
||||||
* Clip window group during startup animation [Jasper; #696323]
|
* Clip window group during startup animation [Jasper; #696323]
|
||||||
|
14
configure.ac
14
configure.ac
@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
AC_INIT([gnome-shell],[3.8.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
AC_INIT([gnome-shell],[3.9.4],[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])
|
||||||
@ -63,18 +63,18 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
|||||||
CLUTTER_MIN_VERSION=1.13.4
|
CLUTTER_MIN_VERSION=1.13.4
|
||||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||||
GJS_MIN_VERSION=1.35.4
|
GJS_MIN_VERSION=1.35.4
|
||||||
MUTTER_MIN_VERSION=3.8.1
|
MUTTER_MIN_VERSION=3.9.4
|
||||||
GTK_MIN_VERSION=3.7.9
|
GTK_MIN_VERSION=3.7.9
|
||||||
GIO_MIN_VERSION=2.35.0
|
GIO_MIN_VERSION=2.37.0
|
||||||
LIBECAL_MIN_VERSION=3.5.3
|
LIBECAL_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||||
POLKIT_MIN_VERSION=0.100
|
POLKIT_MIN_VERSION=0.100
|
||||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||||
GCR_MIN_VERSION=3.3.90
|
GCR_MIN_VERSION=3.7.5
|
||||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
|
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
|
||||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||||
NETWORKMANAGER_MIN_VERSION=0.9.6
|
NETWORKMANAGER_MIN_VERSION=0.9.8
|
||||||
PULSE_MIN_VERS=2.0
|
PULSE_MIN_VERS=2.0
|
||||||
|
|
||||||
# Collect more than 20 libraries for a prize!
|
# Collect more than 20 libraries for a prize!
|
||||||
@ -96,7 +96,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
|||||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||||
libsecret-unstable gcr-3 >= $GCR_MIN_VERSION)
|
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||||
@ -109,7 +109,7 @@ PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
|
|||||||
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
|
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
|
||||||
|
|
||||||
AC_MSG_CHECKING([for bluetooth support])
|
AC_MSG_CHECKING([for bluetooth support])
|
||||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
|
||||||
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
|
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
|
||||||
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
|
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
|
||||||
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
<KeyListEntry name="focus-active-notification"
|
<KeyListEntry name="focus-active-notification"
|
||||||
_description="Focus the active notification"/>
|
_description="Focus the active notification"/>
|
||||||
|
|
||||||
|
<KeyListEntry name="toggle-overview"
|
||||||
|
_description="Show the overview"/>
|
||||||
|
|
||||||
<KeyListEntry name="toggle-application-view"
|
<KeyListEntry name="toggle-application-view"
|
||||||
_description="Show all applications"/>
|
_description="Show all applications"/>
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
|||||||
|
|
||||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||||
introspection_DATA = \
|
introspection_DATA = \
|
||||||
|
org.gnome.Shell.Screencast.xml \
|
||||||
org.gnome.Shell.Screenshot.xml \
|
org.gnome.Shell.Screenshot.xml \
|
||||||
org.gnome.ShellSearchProvider.xml \
|
org.gnome.ShellSearchProvider.xml \
|
||||||
org.gnome.ShellSearchProvider2.xml
|
org.gnome.ShellSearchProvider2.xml
|
||||||
|
96
data/org.gnome.Shell.Screencast.xml
Normal file
96
data/org.gnome.Shell.Screencast.xml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.Screencast:
|
||||||
|
@short_description: Screencast interface
|
||||||
|
|
||||||
|
The interface used to record screen contents.
|
||||||
|
-->
|
||||||
|
<interface name="org.gnome.Shell.Screencast">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Screencast:
|
||||||
|
@file_template: the template for the filename to use
|
||||||
|
@options: a dictionary of optional parameters
|
||||||
|
@success: whether the screencast was started successfully
|
||||||
|
@filename_used: the file where the screencast is being saved
|
||||||
|
|
||||||
|
Records a screencast of the whole screen and saves it
|
||||||
|
(by default) as webm video under a filename derived from
|
||||||
|
@file_template. The template is either a relative or absolute
|
||||||
|
filename which may contain some escape sequences - %d and %t
|
||||||
|
will be replaced by the start date and time of the recording.
|
||||||
|
If a relative name is used, the screencast will be saved in the
|
||||||
|
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
|
||||||
|
The actual filename of the saved video is returned in @filename_used.
|
||||||
|
The set of optional parameters in @options currently consists of:
|
||||||
|
'draw-cursor'(b): whether the cursor should be included in the
|
||||||
|
recording (true)
|
||||||
|
'framerate'(i): the number of frames per second that should be
|
||||||
|
recorded if possible (30)
|
||||||
|
'pipeline'(s): the GStreamer pipeline used to encode recordings
|
||||||
|
in gst-launch format; if not specified, the
|
||||||
|
recorder will produce vp8 (webm) video (unset)
|
||||||
|
-->
|
||||||
|
<method name="Screencast">
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreencastArea:
|
||||||
|
@x: the X coordinate of the area to capture
|
||||||
|
@y: the Y coordinate of the area to capture
|
||||||
|
@width: the width of the area to capture
|
||||||
|
@height: the height of the area to capture
|
||||||
|
@file_template: the template for the filename to use
|
||||||
|
@options: a dictionary of optional parameters
|
||||||
|
@success: whether the screencast was started successfully
|
||||||
|
@filename_used: the file where the screencast is being saved
|
||||||
|
|
||||||
|
Records a screencast of the passed in area and saves it
|
||||||
|
(by default) as webm video under a filename derived from
|
||||||
|
@file_template. The template is either a relative or absolute
|
||||||
|
filename which may contain some escape sequences - %d and %t
|
||||||
|
will be replaced by the start date and time of the recording.
|
||||||
|
If a relative name is used, the screencast will be saved in the
|
||||||
|
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
|
||||||
|
The actual filename of the saved video is returned in @filename_used.
|
||||||
|
The set of optional parameters in @options currently consists of:
|
||||||
|
'draw-cursor'(b): whether the cursor should be included in the
|
||||||
|
recording (true)
|
||||||
|
'framerate'(i): the number of frames per second that should be
|
||||||
|
recorded if possible (30)
|
||||||
|
'pipeline'(s): the GStreamer pipeline used to encode recordings
|
||||||
|
in gst-launch format; if not specified, the
|
||||||
|
recorder will produce vp8 (webm) video (unset)
|
||||||
|
-->
|
||||||
|
<method name="ScreencastArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
StopScreencast:
|
||||||
|
@success: whether stopping the recording was successful
|
||||||
|
|
||||||
|
Stop the recording started by either Screencast or ScreencastArea.
|
||||||
|
-->
|
||||||
|
<method name="StopScreencast">
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
@ -46,7 +46,7 @@
|
|||||||
<!--
|
<!--
|
||||||
GetResultMetas:
|
GetResultMetas:
|
||||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||||
|
|
||||||
Return an array of meta data used to display each given result
|
Return an array of meta data used to display each given result
|
||||||
-->
|
-->
|
||||||
|
@ -21,16 +21,6 @@
|
|||||||
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
|
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="enable-app-monitoring" type="b">
|
|
||||||
<default>true</default>
|
|
||||||
<_summary>Whether to collect stats about applications usage</_summary>
|
|
||||||
<_description>
|
|
||||||
The shell normally monitors active applications in order to present
|
|
||||||
the most used ones (e.g. in launchers). While this data will be
|
|
||||||
kept private, you may want to disable this for privacy reasons.
|
|
||||||
Please note that doing so won't remove already saved data.
|
|
||||||
</_description>
|
|
||||||
</key>
|
|
||||||
<key name="favorite-apps" type="as">
|
<key name="favorite-apps" type="as">
|
||||||
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
|
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
|
||||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||||
@ -117,6 +107,13 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
Overview.
|
Overview.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="toggle-overview" type="as">
|
||||||
|
<default>["<Super>s"]</default>
|
||||||
|
<_summary>Keybinding to open the overview</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to open the Activities Overview.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
<key name="toggle-message-tray" type="as">
|
<key name="toggle-message-tray" type="as">
|
||||||
<default>["<Super>m"]</default>
|
<default>["<Super>m"]</default>
|
||||||
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
||||||
@ -189,6 +186,19 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
|
<schema id="org.gnome.shell.app-switcher"
|
||||||
|
path="/org/gnome/shell/app-switcher/"
|
||||||
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
|
<key type="b" name="current-workspace-only">
|
||||||
|
<default>false</default>
|
||||||
|
<summary>Limit switcher to current workspace.</summary>
|
||||||
|
<description>
|
||||||
|
If true, only applications that have windows on the current workspace are shown in the switcher.
|
||||||
|
Otherwise, all applications are included.
|
||||||
|
</description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
|
||||||
<enum id="org.gnome.shell.window-switcher.AppIconMode">
|
<enum id="org.gnome.shell.window-switcher.AppIconMode">
|
||||||
<value value="1" nick="thumbnail-only"/>
|
<value value="1" nick="thumbnail-only"/>
|
||||||
<value value="2" nick="app-icon-only"/>
|
<value value="2" nick="app-icon-only"/>
|
||||||
@ -207,7 +217,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key type="b" name="current-workspace-only">
|
<key type="b" name="current-workspace-only">
|
||||||
<default>false</default>
|
<default>true</default>
|
||||||
<summary>Limit switcher to current workspace.</summary>
|
<summary>Limit switcher to current workspace.</summary>
|
||||||
<description>
|
<description>
|
||||||
If true, only windows from the current workspace are shown in the switcher.
|
If true, only windows from the current workspace are shown in the switcher.
|
||||||
|
@ -123,8 +123,26 @@ StScrollBar StButton#vhandle:active {
|
|||||||
background-image: url("checkbox-focused.svg");
|
background-image: url("checkbox-focused.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Slider */
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
height: 1em;
|
||||||
|
min-width: 15em;
|
||||||
|
-slider-height: 0.3em;
|
||||||
|
-slider-background-color: #333333;
|
||||||
|
-slider-border-color: #5f5f5f;
|
||||||
|
-slider-active-background-color: #76b0ec;
|
||||||
|
-slider-active-border-color: #1f6dbc;
|
||||||
|
-slider-border-width: 1px;
|
||||||
|
-slider-handle-radius: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
/* PopupMenu */
|
/* PopupMenu */
|
||||||
|
|
||||||
|
.popup-menu-ornament {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.popup-menu-boxpointer,
|
.popup-menu-boxpointer,
|
||||||
.candidate-popup-boxpointer {
|
.candidate-popup-boxpointer {
|
||||||
-arrow-border-radius: 8px;
|
-arrow-border-radius: 8px;
|
||||||
@ -171,13 +189,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
border-width: 0px;
|
border-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-combo-menu {
|
|
||||||
background-color: rgba(0,0,0,0.9);
|
|
||||||
padding: 1em 0em;
|
|
||||||
border: 1px solid #5f5f5f;
|
|
||||||
border-radius: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The remaining popup-menu sizing is all done in ems, so that if you
|
/* The remaining popup-menu sizing is all done in ems, so that if you
|
||||||
* override .popup-menu.font-size, everything else will scale with it.
|
* override .popup-menu.font-size, everything else will scale with it.
|
||||||
*/
|
*/
|
||||||
@ -201,10 +212,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
.popup-image-menu-item {
|
.popup-image-menu-item {
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-combobox-item {
|
|
||||||
spacing: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-separator-menu-item {
|
.popup-separator-menu-item {
|
||||||
-gradient-height: 1px;
|
-gradient-height: 1px;
|
||||||
-gradient-start: rgba(255,255,255,0.0);
|
-gradient-start: rgba(255,255,255,0.0);
|
||||||
@ -218,22 +225,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-slider-menu-item {
|
|
||||||
height: 1em;
|
|
||||||
min-width: 15em;
|
|
||||||
-slider-height: 0.3em;
|
|
||||||
-slider-background-color: #333333;
|
|
||||||
-slider-border-color: #5f5f5f;
|
|
||||||
-slider-active-background-color: #76b0ec;
|
|
||||||
-slider-active-border-color: #1f6dbc;
|
|
||||||
-slider-border-width: 1px;
|
|
||||||
-slider-handle-radius: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-device-menu-item {
|
|
||||||
spacing: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-status-menu-item {
|
.popup-status-menu-item {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: #999;
|
color: #999;
|
||||||
@ -243,19 +234,10 @@ StScrollBar StButton#vhandle:active {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-menu-icon {
|
.popup-menu-icon {
|
||||||
icon-size: 1.09em;
|
icon-size: 1.09em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-battery-percentage {
|
|
||||||
padding-left: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Switches */
|
/* Switches */
|
||||||
.toggle-switch {
|
.toggle-switch {
|
||||||
width: 65px;
|
width: 65px;
|
||||||
@ -280,10 +262,51 @@ StScrollBar StButton#vhandle:active {
|
|||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nm-menu-item-icons {
|
/* Network */
|
||||||
|
|
||||||
|
.nm-dialog {
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-content {
|
||||||
|
spacing: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-header-hbox {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-header-icon {
|
||||||
|
icon-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-scroll-view {
|
||||||
|
border: 2px solid #666;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-header {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-item {
|
||||||
|
font-size: 12pt;
|
||||||
|
border-bottom: 1px solid #666;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-item:checked {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nm-dialog-icons {
|
||||||
spacing: .5em;
|
spacing: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nm-dialog-icon {
|
||||||
|
icon-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
|
|
||||||
.candidate-page-button,
|
.candidate-page-button,
|
||||||
@ -321,10 +344,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-view-control:focus {
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-view-control:first-child:ltr:focus,
|
.app-view-control:first-child:ltr:focus,
|
||||||
.app-view-control:last-child:rtl:focus {
|
.app-view-control:last-child:rtl:focus {
|
||||||
border-right-width: 1px;
|
border-right-width: 1px;
|
||||||
@ -359,7 +378,8 @@ StScrollBar StButton#vhandle:active {
|
|||||||
.modal-dialog-button,
|
.modal-dialog-button,
|
||||||
.notification-button,
|
.notification-button,
|
||||||
.hotplug-notification-item,
|
.hotplug-notification-item,
|
||||||
.app-view-controls {
|
.app-view-controls,
|
||||||
|
#screenShieldNotifications {
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +397,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
/* Entries */
|
/* Entries */
|
||||||
|
|
||||||
#searchEntry,
|
#searchEntry,
|
||||||
|
.login-dialog StEntry,
|
||||||
.notification StEntry,
|
.notification StEntry,
|
||||||
.modal-dialog StEntry {
|
.modal-dialog StEntry {
|
||||||
color: rgb(64, 64, 64);
|
color: rgb(64, 64, 64);
|
||||||
@ -388,6 +409,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#searchEntry,
|
#searchEntry,
|
||||||
|
.login-dialog StEntry,
|
||||||
.run-dialog-entry,
|
.run-dialog-entry,
|
||||||
.notification StEntry {
|
.notification StEntry {
|
||||||
border: 2px solid rgba(245,245,245,0.2);
|
border: 2px solid rgba(245,245,245,0.2);
|
||||||
@ -400,6 +422,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
|
|
||||||
#searchEntry:focus,
|
#searchEntry:focus,
|
||||||
#searchEntry:hover,
|
#searchEntry:hover,
|
||||||
|
.login-dialog StEntry:focus,
|
||||||
.notification StEntry:focus,
|
.notification StEntry:focus,
|
||||||
.modal-dialog StEntry {
|
.modal-dialog StEntry {
|
||||||
border: 2px solid rgb(136,138,133);
|
border: 2px solid rgb(136,138,133);
|
||||||
@ -409,6 +432,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-dialog StEntry:focus,
|
||||||
.notification StEntry:focus,
|
.notification StEntry:focus,
|
||||||
.modal-dialog StEntry:focus {
|
.modal-dialog StEntry:focus {
|
||||||
border: 2px solid #3465a4;
|
border: 2px solid #3465a4;
|
||||||
@ -432,6 +456,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
transition-duration: 0ms;
|
transition-duration: 0ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-dialog StEntry,
|
||||||
.notification StEntry,
|
.notification StEntry,
|
||||||
.modal-dialog StEntry {
|
.modal-dialog StEntry {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -445,6 +470,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-dialog StEntry:insensitive,
|
||||||
.modal-dialog StEntry:insensitive {
|
.modal-dialog StEntry:insensitive {
|
||||||
border-color: #666666;
|
border-color: #666666;
|
||||||
color: #9f9f9f;
|
color: #9f9f9f;
|
||||||
@ -462,10 +488,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
height: 1.86em;
|
height: 1.86em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#panel.lock-screen {
|
|
||||||
background-color: rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel.unlock-screen,
|
#panel.unlock-screen,
|
||||||
#panel.login-screen {
|
#panel.login-screen {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@ -600,58 +622,30 @@ StScrollBar StButton#vhandle:active {
|
|||||||
spacing: 8px;
|
spacing: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User Menu */
|
|
||||||
|
|
||||||
#panelUserMenu {
|
|
||||||
spacing: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser {
|
|
||||||
spacing: .4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser .popup-menu-item,
|
|
||||||
.status-chooser-combo .popup-menu-item {
|
|
||||||
padding: .4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser-user-icon {
|
|
||||||
border: 2px solid #8b8b8b;
|
|
||||||
border-radius: 5px;
|
|
||||||
width: 48pt;
|
|
||||||
height: 48pt;
|
|
||||||
background-size: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser-user-icon:hover {
|
|
||||||
border: 2px solid #bbbbbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser-user-name {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.3em;
|
|
||||||
min-width: 120pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser-combo {
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser-combo.popup-combo-menu {
|
|
||||||
padding: .4em 0em;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #5f5f5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-chooser-status-item,
|
|
||||||
.status-chooser-combo .popup-combobox-item {
|
|
||||||
spacing: .4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.system-status-icon {
|
.system-status-icon {
|
||||||
icon-size: 1.09em;
|
icon-size: 1.09em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.system-switch-user-submenu-icon {
|
||||||
|
icon-size: 24px;
|
||||||
|
border: 1px solid #8b8b8b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-menu-action {
|
||||||
|
color: #e6e6e6;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-menu-action:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-menu-action > StIcon {
|
||||||
|
icon-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Overview */
|
/* Overview */
|
||||||
|
|
||||||
#overview {
|
#overview {
|
||||||
@ -800,6 +794,11 @@ StScrollBar StButton#vhandle:active {
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-dash-drop-target {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Search Box */
|
/* Search Box */
|
||||||
|
|
||||||
#searchEntry {
|
#searchEntry {
|
||||||
@ -887,14 +886,18 @@ StScrollBar StButton#vhandle:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-view-controls {
|
.app-view-controls {
|
||||||
width: 250px;
|
|
||||||
padding-bottom: 32px;
|
padding-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-view-control {
|
.app-view-control {
|
||||||
padding: 4px 16px;
|
padding: 4px 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-view-control:focus {
|
||||||
|
padding: 3px 31px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.search-display > StBoxLayout,
|
.search-display > StBoxLayout,
|
||||||
.all-apps > StBoxLayout,
|
.all-apps > StBoxLayout,
|
||||||
.frequent-apps > StBoxLayout {
|
.frequent-apps > StBoxLayout {
|
||||||
@ -1116,7 +1119,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lg-extension-list {
|
.lg-extensions-list {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
spacing: 6px;
|
spacing: 6px;
|
||||||
}
|
}
|
||||||
@ -1144,11 +1147,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
|
|
||||||
/* Calendar popup */
|
/* Calendar popup */
|
||||||
|
|
||||||
#calendarEventsArea {
|
|
||||||
/* this is the width of the second column of the popup */
|
|
||||||
min-width: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-vertical-separator {
|
.calendar-vertical-separator {
|
||||||
-stipple-width: 1px;
|
-stipple-width: 1px;
|
||||||
-stipple-color: #505050;
|
-stipple-color: #505050;
|
||||||
@ -1184,7 +1182,8 @@ StScrollBar StButton#vhandle:active {
|
|||||||
background-image: url("calendar-arrow-right.svg");
|
background-image: url("calendar-arrow-right.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-change-month-back:hover {
|
.calendar-change-month-back:hover,
|
||||||
|
.calendar-change-month-back:focus {
|
||||||
background-color: #999999;
|
background-color: #999999;
|
||||||
}
|
}
|
||||||
.calendar-change-month-back:active {
|
.calendar-change-month-back:active {
|
||||||
@ -1202,7 +1201,8 @@ StScrollBar StButton#vhandle:active {
|
|||||||
background-image: url("calendar-arrow-left.svg");
|
background-image: url("calendar-arrow-left.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-change-month-forward:hover {
|
.calendar-change-month-forward:hover,
|
||||||
|
.calendar-change-month-forward:focus {
|
||||||
background-color: #999999;
|
background-color: #999999;
|
||||||
}
|
}
|
||||||
.calendar-change-month-forward:active {
|
.calendar-change-month-forward:active {
|
||||||
@ -1223,7 +1223,8 @@ StScrollBar StButton#vhandle:active {
|
|||||||
height: 2.4em;
|
height: 2.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-day-base:hover {
|
.calendar-day-base:hover,
|
||||||
|
.calendar-day-base:focus {
|
||||||
background-color: #777777;
|
background-color: #777777;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1282,32 +1283,40 @@ StScrollBar StButton#vhandle:active {
|
|||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-header-vbox {
|
.events-table {
|
||||||
spacing: 6pt;
|
width: 320px;
|
||||||
padding-right: .5em;
|
spacing-columns: 6pt;
|
||||||
|
padding: 0 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-header-vbox:rtl {
|
.events-table:ltr {
|
||||||
padding-left: .5em;
|
padding-right: 1.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-header-hbox {
|
.events-table:rtl {
|
||||||
padding: 0.3em 1.4em;
|
padding-left: 1.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-day-header {
|
.events-day-header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
padding: 0.4em 1.4em 0em 1.4em;
|
padding-left: 0.4em;
|
||||||
|
padding-top: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-day-header:first-child {
|
||||||
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-day-header:rtl {
|
.events-day-header:rtl {
|
||||||
padding: 0em 1.4em 0.4em 1.4em;
|
padding-left: 0;
|
||||||
|
padding-right: 0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-day-dayname {
|
.events-day-dayname {
|
||||||
color: rgba(153, 153, 153, 1.0);
|
color: rgba(153, 153, 153, 1.0);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
min-width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-day-dayname:rtl {
|
.events-day-dayname:rtl {
|
||||||
@ -1325,23 +1334,12 @@ StScrollBar StButton#vhandle:active {
|
|||||||
|
|
||||||
.events-day-task {
|
.events-day-task {
|
||||||
color: rgba(153, 153, 153, 1.0);
|
color: rgba(153, 153, 153, 1.0);
|
||||||
|
padding-left: 8pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.events-day-name-box {
|
.events-day-task:rtl {
|
||||||
min-width: 15pt;
|
padding-left: 0px;
|
||||||
}
|
padding-right: 8pt;
|
||||||
|
|
||||||
.events-time-box {
|
|
||||||
min-width: 48pt;
|
|
||||||
padding-right: 12pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.events-time-box:rtl {
|
|
||||||
padding-right: 0px;
|
|
||||||
padding-left: 12pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.events-event-box {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.url-highlighter {
|
.url-highlighter {
|
||||||
@ -2198,6 +2196,18 @@ StScrollBar StButton#vhandle:active {
|
|||||||
|
|
||||||
/* Login Dialog */
|
/* Login Dialog */
|
||||||
|
|
||||||
|
.framed-user-icon {
|
||||||
|
border: 2px solid #8b8b8b;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 48pt;
|
||||||
|
height: 48pt;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framed-user-icon:hover {
|
||||||
|
border: 2px solid #bbbbbb;
|
||||||
|
}
|
||||||
|
|
||||||
.login-dialog-banner {
|
.login-dialog-banner {
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -2227,6 +2237,10 @@ StScrollBar StButton#vhandle:active {
|
|||||||
min-width: 350px;
|
min-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-dialog-button-box {
|
||||||
|
spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.login-dialog-prompt-login-hint-message {
|
.login-dialog-prompt-login-hint-message {
|
||||||
font-size: 10.5pt;
|
font-size: 10.5pt;
|
||||||
}
|
}
|
||||||
@ -2310,6 +2324,10 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-selection-box .login-dialog-not-listed-label {
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.login-dialog-not-listed-button:focus .login-dialog-not-listed-label,
|
.login-dialog-not-listed-button:focus .login-dialog-not-listed-label,
|
||||||
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
||||||
color: #E8E8E8;
|
color: #E8E8E8;
|
||||||
@ -2327,6 +2345,7 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
spacing: 8px;
|
spacing: 8px;
|
||||||
|
width: 23em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-dialog-prompt-label {
|
.login-dialog-prompt-label {
|
||||||
@ -2334,56 +2353,21 @@ StScrollBar StButton#vhandle:active {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-dialog-prompt-entry {
|
.login-dialog-session-list-button StIcon {
|
||||||
width: 15em;
|
icon-size: 1.25em;
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list {
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 10.5pt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-dialog-session-list-button {
|
.login-dialog-session-list-button {
|
||||||
padding: 4px;
|
color: #8b8b8b;
|
||||||
}
|
|
||||||
|
|
||||||
.login-dialog-session-list-button:focus {
|
|
||||||
background-color: #4c4c4c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-button:hover,
|
||||||
.login-dialog-session-list-button:active {
|
.login-dialog-session-list-button:active {
|
||||||
background-color: #4c4c4c;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-dialog-session-list-button:hover {
|
.login-dialog-logo-bin {
|
||||||
font-weight: bold;
|
padding: 24px 0px;
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-dialog .modal-dialog-button-box {
|
.login-dialog .modal-dialog-button-box {
|
||||||
@ -2446,8 +2430,14 @@ StScrollBar StButton#vhandle:active {
|
|||||||
|
|
||||||
/* Screen shield */
|
/* Screen shield */
|
||||||
|
|
||||||
|
#panel.lock-screen,
|
||||||
|
#screenShieldNotifications {
|
||||||
|
background-color: rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
.screen-shield-background {
|
.screen-shield-background {
|
||||||
background: black;
|
background: black;
|
||||||
|
box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
#lockDialogGroup {
|
#lockDialogGroup {
|
||||||
@ -2489,33 +2479,27 @@ StScrollBar StButton#vhandle:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#screenShieldNotifications {
|
#screenShieldNotifications {
|
||||||
border-radius: 8px;
|
|
||||||
background-color: rgba(0.0, 0.0, 0.0, 0.9);
|
|
||||||
border: 2px solid #868686;
|
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
padding: 18px 0;
|
padding: 12px;
|
||||||
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-shield-notifications-box {
|
.screen-shield-notifications-box {
|
||||||
spacing: 18px;
|
spacing: 12px;
|
||||||
max-width: 34em;
|
width: 30em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-shield-notification-source {
|
.screen-shield-notification-source {
|
||||||
padding: 13px 24px;
|
padding: 3px 6px;
|
||||||
spacing: 5px;
|
spacing: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-shield-notification-label {
|
.screen-shield-notification-label {
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0px 18px;
|
padding: 0px 0px 0px 12px;
|
||||||
color: #babdb6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-shield-notification-count-text {
|
.screen-shield-notification-count-text {
|
||||||
padding: 0px 18px;
|
padding: 0px 0px 0px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove background from notifications, otherwise
|
/* Remove background from notifications, otherwise
|
||||||
@ -2533,6 +2517,31 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#screenShieldNotifications .notification-button,
|
||||||
|
#screenShieldNotifications .notification-icon-button {
|
||||||
|
border: 1px rgba(255,255,255,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#screenShieldNotifications StScrollBar StBin#trough {
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#screenShieldNotifications StScrollBar StButton#vhandle,
|
||||||
|
#screenShieldNotifications StScrollBar StButton#hhandle {
|
||||||
|
background-color: rgba(0,0,0,0.3);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screenShieldNotifications StScrollBar StButton#vhandle:hover,
|
||||||
|
#screenShieldNotifications StScrollBar StButton#hhandle {
|
||||||
|
background-color: rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#screenShieldNotifications StScrollBar StButton#vhandle:active,
|
||||||
|
#screenShieldNotifications StScrollBar StButton#hhandle {
|
||||||
|
background-color: rgba(0,0,0,0.8);
|
||||||
|
}
|
||||||
|
|
||||||
.input-source-switcher-symbol {
|
.input-source-switcher-symbol {
|
||||||
font-size: 34pt;
|
font-size: 34pt;
|
||||||
width: 96px;
|
width: 96px;
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
|
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
|
||||||
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
|
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
|
||||||
<xi:include href="xml/shell-global.xml"/>
|
<xi:include href="xml/shell-global.xml"/>
|
||||||
|
<xi:include href="xml/shell-keybinding-modes.xml"/>
|
||||||
<xi:include href="xml/shell-wm.xml"/>
|
<xi:include href="xml/shell-wm.xml"/>
|
||||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
||||||
<xi:include href="xml/shell-util.xml"/>
|
<xi:include href="xml/shell-util.xml"/>
|
||||||
|
@ -17,6 +17,7 @@ misc/config.js: misc/config.js.in Makefile
|
|||||||
jsdir = $(pkgdatadir)/js
|
jsdir = $(pkgdatadir)/js
|
||||||
|
|
||||||
nobase_dist_js_DATA = \
|
nobase_dist_js_DATA = \
|
||||||
|
gdm/authPrompt.js \
|
||||||
gdm/batch.js \
|
gdm/batch.js \
|
||||||
gdm/fingerprint.js \
|
gdm/fingerprint.js \
|
||||||
gdm/loginDialog.js \
|
gdm/loginDialog.js \
|
||||||
@ -33,10 +34,13 @@ nobase_dist_js_DATA = \
|
|||||||
misc/jsParse.js \
|
misc/jsParse.js \
|
||||||
misc/loginManager.js \
|
misc/loginManager.js \
|
||||||
misc/modemManager.js \
|
misc/modemManager.js \
|
||||||
|
misc/objectManager.js \
|
||||||
misc/params.js \
|
misc/params.js \
|
||||||
|
misc/smartcardManager.js \
|
||||||
misc/util.js \
|
misc/util.js \
|
||||||
perf/core.js \
|
perf/core.js \
|
||||||
ui/altTab.js \
|
ui/altTab.js \
|
||||||
|
ui/animation.js \
|
||||||
ui/appDisplay.js \
|
ui/appDisplay.js \
|
||||||
ui/appFavorites.js \
|
ui/appFavorites.js \
|
||||||
ui/backgroundMenu.js \
|
ui/backgroundMenu.js \
|
||||||
@ -68,6 +72,7 @@ nobase_dist_js_DATA = \
|
|||||||
ui/sessionMode.js \
|
ui/sessionMode.js \
|
||||||
ui/shellEntry.js \
|
ui/shellEntry.js \
|
||||||
ui/shellMountOperation.js \
|
ui/shellMountOperation.js \
|
||||||
|
ui/slider.js \
|
||||||
ui/notificationDaemon.js \
|
ui/notificationDaemon.js \
|
||||||
ui/osdWindow.js \
|
ui/osdWindow.js \
|
||||||
ui/overview.js \
|
ui/overview.js \
|
||||||
@ -77,7 +82,9 @@ nobase_dist_js_DATA = \
|
|||||||
ui/pointerWatcher.js \
|
ui/pointerWatcher.js \
|
||||||
ui/popupMenu.js \
|
ui/popupMenu.js \
|
||||||
ui/remoteSearch.js \
|
ui/remoteSearch.js \
|
||||||
|
ui/remoteMenu.js \
|
||||||
ui/runDialog.js \
|
ui/runDialog.js \
|
||||||
|
ui/screencast.js \
|
||||||
ui/screenshot.js \
|
ui/screenshot.js \
|
||||||
ui/screenShield.js \
|
ui/screenShield.js \
|
||||||
ui/scripting.js \
|
ui/scripting.js \
|
||||||
@ -91,10 +98,10 @@ nobase_dist_js_DATA = \
|
|||||||
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/status/system.js \
|
||||||
ui/switcherPopup.js \
|
ui/switcherPopup.js \
|
||||||
ui/tweener.js \
|
ui/tweener.js \
|
||||||
ui/unlockDialog.js \
|
ui/unlockDialog.js \
|
||||||
ui/userMenu.js \
|
|
||||||
ui/userWidget.js \
|
ui/userWidget.js \
|
||||||
ui/viewSelector.js \
|
ui/viewSelector.js \
|
||||||
ui/wanda.js \
|
ui/wanda.js \
|
||||||
|
506
js/gdm/authPrompt.js
Normal file
506
js/gdm/authPrompt.js
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
|
const Batch = imports.gdm.batch;
|
||||||
|
const GdmUtil = imports.gdm.util;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
|
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
|
||||||
|
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
|
||||||
|
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
|
||||||
|
|
||||||
|
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
|
||||||
|
|
||||||
|
const AuthPromptMode = {
|
||||||
|
UNLOCK_ONLY: 0,
|
||||||
|
UNLOCK_OR_LOG_IN: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const AuthPromptStatus = {
|
||||||
|
NOT_VERIFYING: 0,
|
||||||
|
VERIFYING: 1,
|
||||||
|
VERIFICATION_FAILED: 2,
|
||||||
|
VERIFICATION_SUCCEEDED: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const BeginRequestType = {
|
||||||
|
PROVIDE_USERNAME: 0,
|
||||||
|
DONT_PROVIDE_USERNAME: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const AuthPrompt = new Lang.Class({
|
||||||
|
Name: 'AuthPrompt',
|
||||||
|
|
||||||
|
_init: function(gdmClient, mode) {
|
||||||
|
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||||
|
|
||||||
|
this._gdmClient = gdmClient;
|
||||||
|
this._mode = mode;
|
||||||
|
|
||||||
|
let reauthenticationOnly;
|
||||||
|
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
|
||||||
|
reauthenticationOnly = true;
|
||||||
|
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
|
||||||
|
reauthenticationOnly = false;
|
||||||
|
|
||||||
|
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
|
||||||
|
|
||||||
|
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||||
|
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
|
||||||
|
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||||
|
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||||
|
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||||
|
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint));
|
||||||
|
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint));
|
||||||
|
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
|
||||||
|
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||||
|
|
||||||
|
this.connect('next', Lang.bind(this, function() {
|
||||||
|
this.updateSensitivity(false);
|
||||||
|
this.startSpinning();
|
||||||
|
if (this._queryingService) {
|
||||||
|
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
|
||||||
|
} else {
|
||||||
|
this._preemptiveAnswer = this._entry.text;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||||
|
vertical: true });
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
this.actor.connect('key-press-event',
|
||||||
|
Lang.bind(this, function(actor, event) {
|
||||||
|
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._userWell = new St.Bin({ x_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
this.actor.add(this._userWell,
|
||||||
|
{ x_align: St.Align.START,
|
||||||
|
x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
expand: true });
|
||||||
|
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||||
|
|
||||||
|
this.actor.add(this._label,
|
||||||
|
{ expand: true,
|
||||||
|
x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||||
|
can_focus: true });
|
||||||
|
ShellEntry.addContextMenu(this._entry, { isPassword: true });
|
||||||
|
|
||||||
|
this.actor.add(this._entry,
|
||||||
|
{ expand: true,
|
||||||
|
x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
|
||||||
|
this._message = new St.Label({ opacity: 0 });
|
||||||
|
this._message.clutter_text.line_wrap = true;
|
||||||
|
this.actor.add(this._message, { x_fill: true });
|
||||||
|
|
||||||
|
this._loginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
||||||
|
this.actor.add(this._loginHint);
|
||||||
|
|
||||||
|
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
|
||||||
|
vertical: false });
|
||||||
|
this.actor.add(this._buttonBox,
|
||||||
|
{ expand: true,
|
||||||
|
x_align: St.Align.MIDDLE,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
this._defaultButtonWell = new St.Widget();
|
||||||
|
this._defaultButtonWellActor = null;
|
||||||
|
|
||||||
|
this._initButtons();
|
||||||
|
|
||||||
|
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||||
|
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
|
||||||
|
this._spinner.actor.opacity = 0;
|
||||||
|
this._spinner.actor.show();
|
||||||
|
this._defaultButtonWell.add_child(this._spinner.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this._userVerifier.clear();
|
||||||
|
this._userVerifier.disconnectAll();
|
||||||
|
this._userVerifier = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_initButtons: function() {
|
||||||
|
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
label: _("Cancel") });
|
||||||
|
this.cancelButton.connect('clicked',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.cancel();
|
||||||
|
}));
|
||||||
|
this._buttonBox.add(this.cancelButton,
|
||||||
|
{ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
this._buttonBox.add(this._defaultButtonWell,
|
||||||
|
{ expand: true,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
label: _("Next") });
|
||||||
|
this.nextButton.connect('clicked',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.emit('next');
|
||||||
|
}));
|
||||||
|
this.nextButton.add_style_pseudo_class('default');
|
||||||
|
this._buttonBox.add(this.nextButton,
|
||||||
|
{ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||||
|
|
||||||
|
this._entry.clutter_text.connect('text-changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
if (!this._userVerifier.hasPendingMessages)
|
||||||
|
this._fadeOutMessage();
|
||||||
|
|
||||||
|
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||||
|
}));
|
||||||
|
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
|
||||||
|
this.emit('next');
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||||
|
if (this._preemptiveAnswer) {
|
||||||
|
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||||
|
this._preemptiveAnswer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._queryingService)
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
this._queryingService = serviceName;
|
||||||
|
this.setPasswordChar(passwordChar);
|
||||||
|
this.setQuestion(question);
|
||||||
|
|
||||||
|
if (passwordChar) {
|
||||||
|
if (this._userVerifier.reauthenticating)
|
||||||
|
this.nextButton.label = _("Unlock");
|
||||||
|
else
|
||||||
|
this.nextButton.label = C_("button", "Sign In");
|
||||||
|
} else {
|
||||||
|
this.nextButton.label = _("Next");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSensitivity(true);
|
||||||
|
this.emit('prompted');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSmartcardStatusChanged: function() {
|
||||||
|
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||||
|
|
||||||
|
// Don't reset on smartcard insertion if we're already verifying
|
||||||
|
// and the smartcard is the main service
|
||||||
|
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
|
||||||
|
this.verificationStatus == AuthPromptStatus.VERIFYING &&
|
||||||
|
this.smartcardDetected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShowMessage: function(userVerifier, message, styleClass) {
|
||||||
|
this.setMessage(message, styleClass);
|
||||||
|
this.emit('prompted');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVerificationFailed: function() {
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
this.updateSensitivity(true);
|
||||||
|
this.setActorInDefaultButtonWell(null);
|
||||||
|
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVerificationComplete: function() {
|
||||||
|
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onReset: function() {
|
||||||
|
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
|
||||||
|
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShowLoginHint: function(verifier, message) {
|
||||||
|
this.setHint(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onHideLoginHint: function() {
|
||||||
|
this.setHint(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
addActorToDefaultButtonWell: function(actor) {
|
||||||
|
this._defaultButtonWell.add_child(actor);
|
||||||
|
|
||||||
|
actor.add_constraint(new Clutter.AlignConstraint({ source: this._spinner.actor,
|
||||||
|
align_axis: Clutter.AlignAxis.BOTH,
|
||||||
|
factor: 0.5 }));
|
||||||
|
},
|
||||||
|
|
||||||
|
setActorInDefaultButtonWell: function(actor, animate) {
|
||||||
|
if (!this._defaultButtonWellActor &&
|
||||||
|
!actor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let oldActor = this._defaultButtonWellActor;
|
||||||
|
|
||||||
|
if (oldActor)
|
||||||
|
Tweener.removeTweens(oldActor);
|
||||||
|
|
||||||
|
let isSpinner;
|
||||||
|
if (actor == this._spinner.actor)
|
||||||
|
isSpinner = true;
|
||||||
|
else
|
||||||
|
isSpinner = false;
|
||||||
|
|
||||||
|
if (this._defaultButtonWellActor != actor && oldActor) {
|
||||||
|
if (!animate) {
|
||||||
|
oldActor.opacity = 0;
|
||||||
|
} else {
|
||||||
|
Tweener.addTween(oldActor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||||||
|
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||||||
|
transition: 'linear',
|
||||||
|
onCompleteScope: this,
|
||||||
|
onComplete: function() {
|
||||||
|
if (isSpinner) {
|
||||||
|
if (this._spinner)
|
||||||
|
this._spinner.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor) {
|
||||||
|
if (isSpinner)
|
||||||
|
this._spinner.play();
|
||||||
|
|
||||||
|
if (!animate)
|
||||||
|
actor.opacity = 255;
|
||||||
|
else
|
||||||
|
Tweener.addTween(actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||||||
|
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||||||
|
transition: 'linear' });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._defaultButtonWellActor = actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
startSpinning: function() {
|
||||||
|
this.setActorInDefaultButtonWell(this._spinner.actor, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopSpinning: function() {
|
||||||
|
this.setActorInDefaultButtonWell(null, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function() {
|
||||||
|
this._entry.text = '';
|
||||||
|
this.stopSpinning();
|
||||||
|
},
|
||||||
|
|
||||||
|
setPasswordChar: function(passwordChar) {
|
||||||
|
this._entry.clutter_text.set_password_char(passwordChar);
|
||||||
|
this._entry.menu.isPassword = passwordChar != '';
|
||||||
|
},
|
||||||
|
|
||||||
|
setQuestion: function(question) {
|
||||||
|
this._label.set_text(question);
|
||||||
|
|
||||||
|
this._label.show();
|
||||||
|
this._entry.show();
|
||||||
|
|
||||||
|
this._loginHint.opacity = 0;
|
||||||
|
this._loginHint.show();
|
||||||
|
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
getAnswer: function() {
|
||||||
|
let text = this._entry.get_text();
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
|
||||||
|
_fadeOutMessage: function() {
|
||||||
|
if (this._message.opacity == 0)
|
||||||
|
return;
|
||||||
|
Tweener.removeTweens(this._message);
|
||||||
|
Tweener.addTween(this._message,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: MESSAGE_FADE_OUT_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setMessage: function(message, styleClass) {
|
||||||
|
if (message) {
|
||||||
|
Tweener.removeTweens(this._message);
|
||||||
|
this._message.text = message;
|
||||||
|
this._message.styleClass = styleClass;
|
||||||
|
this._message.opacity = 255;
|
||||||
|
} else {
|
||||||
|
this._message.opacity = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateNextButtonSensitivity: function(sensitive) {
|
||||||
|
this.nextButton.reactive = sensitive;
|
||||||
|
this.nextButton.can_focus = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSensitivity: function(sensitive) {
|
||||||
|
this._updateNextButtonSensitivity(sensitive);
|
||||||
|
this._entry.reactive = sensitive;
|
||||||
|
this._entry.clutter_text.editable = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.setActorInDefaultButtonWell(null, true);
|
||||||
|
this.actor.hide();
|
||||||
|
this._loginHint.opacity = 0;
|
||||||
|
|
||||||
|
this.setUser(null);
|
||||||
|
|
||||||
|
this.updateSensitivity(true);
|
||||||
|
this._entry.set_text('');
|
||||||
|
},
|
||||||
|
|
||||||
|
setUser: function(user) {
|
||||||
|
if (user) {
|
||||||
|
let userWidget = new UserWidget.UserWidget(user);
|
||||||
|
this._userWell.set_child(userWidget.actor);
|
||||||
|
} else {
|
||||||
|
this._userWell.set_child(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setHint: function(message) {
|
||||||
|
if (message) {
|
||||||
|
this._loginHint.set_text(message)
|
||||||
|
this._loginHint.opacity = 255;
|
||||||
|
} else {
|
||||||
|
this._loginHint.opacity = 0;
|
||||||
|
this._loginHint.set_text('');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
let oldStatus = this.verificationStatus;
|
||||||
|
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||||
|
|
||||||
|
if (oldStatus == AuthPromptStatus.VERIFYING)
|
||||||
|
this._userVerifier.cancel();
|
||||||
|
|
||||||
|
this._queryingService = null;
|
||||||
|
this.clear();
|
||||||
|
this._message.opacity = 0;
|
||||||
|
this.setUser(null);
|
||||||
|
this.stopSpinning();
|
||||||
|
this.setHint(null);
|
||||||
|
|
||||||
|
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
|
||||||
|
this.emit('failed');
|
||||||
|
|
||||||
|
let beginRequestType;
|
||||||
|
|
||||||
|
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||||||
|
// The user is constant at the unlock screen
|
||||||
|
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||||
|
} else if (this.smartcardDetected &&
|
||||||
|
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
|
||||||
|
// We don't need to know the username if the user preempted the login screen
|
||||||
|
// with a smartcard.
|
||||||
|
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
|
||||||
|
} else {
|
||||||
|
// In all other cases, we should get the username up front.
|
||||||
|
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('reset', beginRequestType);
|
||||||
|
},
|
||||||
|
|
||||||
|
addCharacter: function(unichar) {
|
||||||
|
if (!this._entry.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
this._entry.clutter_text.insert_unichar(unichar);
|
||||||
|
},
|
||||||
|
|
||||||
|
begin: function(params) {
|
||||||
|
params = Params.parse(params, { userName: null,
|
||||||
|
hold: null });
|
||||||
|
|
||||||
|
this.updateSensitivity(false);
|
||||||
|
|
||||||
|
let hold = params.hold;
|
||||||
|
if (!hold)
|
||||||
|
hold = new Batch.Hold();
|
||||||
|
|
||||||
|
this._userVerifier.begin(params.userName, hold);
|
||||||
|
this.verificationStatus = AuthPromptStatus.VERIFYING;
|
||||||
|
},
|
||||||
|
|
||||||
|
finish: function(onComplete) {
|
||||||
|
if (!this._userVerifier.hasPendingMessages) {
|
||||||
|
onComplete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let signalId = this._userVerifier.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._userVerifier.disconnect(signalId);
|
||||||
|
onComplete();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this.reset();
|
||||||
|
this.emit('cancelled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(AuthPrompt.prototype);
|
@ -19,73 +19,38 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const AccountsService = imports.gi.AccountsService;
|
const AccountsService = imports.gi.AccountsService;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const Gdm = imports.gi.Gdm;
|
||||||
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 Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Lang = imports.lang;
|
|
||||||
const Pango = imports.gi.Pango;
|
|
||||||
const Signals = imports.signals;
|
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gdm = imports.gi.Gdm;
|
|
||||||
|
|
||||||
|
const AuthPrompt = imports.gdm.authPrompt;
|
||||||
const Batch = imports.gdm.batch;
|
const Batch = imports.gdm.batch;
|
||||||
const Fprint = imports.gdm.fingerprint;
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const GdmUtil = imports.gdm.util;
|
const GdmUtil = imports.gdm.util;
|
||||||
const Lightbox = imports.ui.lightbox;
|
const Layout = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Panel = imports.ui.panel;
|
const Realmd = imports.gdm.realmd;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const UserMenu = imports.ui.userMenu;
|
|
||||||
const UserWidget = imports.ui.userWidget;
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
const _FADE_ANIMATION_TIME = 0.25;
|
const _FADE_ANIMATION_TIME = 0.25;
|
||||||
const _SCROLL_ANIMATION_TIME = 0.5;
|
const _SCROLL_ANIMATION_TIME = 0.5;
|
||||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||||
const _LOGO_ICON_HEIGHT = 16;
|
const _LOGO_ICON_HEIGHT = 48;
|
||||||
|
|
||||||
const WORK_SPINNER_ICON_SIZE = 24;
|
|
||||||
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
|
|
||||||
const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
|
||||||
|
|
||||||
let _loginDialog = null;
|
let _loginDialog = null;
|
||||||
|
|
||||||
const LogoMenuButton = new Lang.Class({
|
|
||||||
Name: 'LogoMenuButton',
|
|
||||||
Extends: PanelMenu.Button,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.parent(0.0, null, true);
|
|
||||||
|
|
||||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
|
||||||
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
|
|
||||||
Lang.bind(this, this._updateLogo));
|
|
||||||
|
|
||||||
this._iconBin = new St.Bin();
|
|
||||||
this.actor.add_actor(this._iconBin);
|
|
||||||
|
|
||||||
this._updateLogo();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateLogo: function() {
|
|
||||||
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
|
||||||
let icon = null;
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
let file = Gio.file_new_for_path(path);
|
|
||||||
let cache = St.TextureCache.get_default();
|
|
||||||
icon = cache.load_uri_async(file.get_uri(), -1, _LOGO_ICON_HEIGHT);
|
|
||||||
}
|
|
||||||
this._iconBin.set_child(icon);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const UserListItem = new Lang.Class({
|
const UserListItem = new Lang.Class({
|
||||||
Name: 'UserListItem',
|
Name: 'UserListItem',
|
||||||
|
|
||||||
@ -103,8 +68,8 @@ const UserListItem = new Lang.Class({
|
|||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
|
|
||||||
this._userAvatar = new UserMenu.UserAvatarWidget(this.user,
|
this._userAvatar = new UserWidget.Avatar(this.user,
|
||||||
{ styleClass: 'login-dialog-user-list-item-icon' });
|
{ styleClass: 'login-dialog-user-list-item-icon' });
|
||||||
layout.add(this._userAvatar.actor);
|
layout.add(this._userAvatar.actor);
|
||||||
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 });
|
||||||
@ -322,209 +287,124 @@ const UserList = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(UserList.prototype);
|
Signals.addSignalMethods(UserList.prototype);
|
||||||
|
|
||||||
const SessionListItem = new Lang.Class({
|
const SessionMenuButton = new Lang.Class({
|
||||||
Name: 'SessionListItem',
|
Name: 'SessionMenuButton',
|
||||||
|
|
||||||
_init: function(id, name) {
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
|
|
||||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
|
||||||
can_focus: true,
|
|
||||||
reactive: true,
|
|
||||||
x_fill: true,
|
|
||||||
x_align: St.Align.START });
|
|
||||||
|
|
||||||
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
|
|
||||||
|
|
||||||
this.actor.add_actor(this._box);
|
|
||||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
|
||||||
|
|
||||||
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
|
|
||||||
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
|
|
||||||
this._box.add_actor(this._dot);
|
|
||||||
this.setShowDot(false);
|
|
||||||
|
|
||||||
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
|
|
||||||
text: name });
|
|
||||||
this.actor.label_actor = label;
|
|
||||||
|
|
||||||
this._box.add_actor(label);
|
|
||||||
},
|
|
||||||
|
|
||||||
setShowDot: function(show) {
|
|
||||||
if (show)
|
|
||||||
this._dot.opacity = 255;
|
|
||||||
else
|
|
||||||
this._dot.opacity = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onRepaintDot: function(area) {
|
|
||||||
let cr = area.get_context();
|
|
||||||
let [width, height] = area.get_surface_size();
|
|
||||||
let color = area.get_theme_node().get_foreground_color();
|
|
||||||
|
|
||||||
cr.setSourceRGBA (color.red / 255,
|
|
||||||
color.green / 255,
|
|
||||||
color.blue / 255,
|
|
||||||
color.alpha / 255);
|
|
||||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
|
||||||
cr.fill();
|
|
||||||
cr.$dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onClicked: function() {
|
|
||||||
this.emit('activate');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Signals.addSignalMethods(SessionListItem.prototype);
|
|
||||||
|
|
||||||
const SessionList = new Lang.Class({
|
|
||||||
Name: 'SessionList',
|
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new St.Bin();
|
let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
|
||||||
|
|
||||||
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list',
|
|
||||||
vertical: true});
|
|
||||||
this.actor.child = this._box;
|
|
||||||
|
|
||||||
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
|
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
|
||||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
reactive: true,
|
||||||
|
track_hover: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
x_fill: true,
|
accessible_name: _("Choose Session"),
|
||||||
y_fill: true });
|
accessible_role: Atk.Role.MENU,
|
||||||
let box = new St.BoxLayout();
|
child: gearIcon });
|
||||||
this._button.add_actor(box);
|
|
||||||
|
|
||||||
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
|
this.actor = new St.Bin({ child: this._button });
|
||||||
text: '\u25B8' });
|
|
||||||
box.add_actor(this._triangle);
|
|
||||||
|
|
||||||
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
|
this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP);
|
||||||
text: _("Session…") });
|
Main.uiGroup.add_actor(this._menu.actor);
|
||||||
box.add_actor(label);
|
this._menu.actor.hide();
|
||||||
|
|
||||||
this._button.connect('clicked',
|
this._menu.connect('open-state-changed',
|
||||||
Lang.bind(this, this._onClicked));
|
Lang.bind(this, function(menu, isOpen) {
|
||||||
this._box.add_actor(this._button);
|
if (isOpen)
|
||||||
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
|
this._button.add_style_pseudo_class('active');
|
||||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
|
else
|
||||||
Gtk.PolicyType.AUTOMATIC);
|
this._button.remove_style_pseudo_class('active');
|
||||||
this._box.add_actor(this._scrollView);
|
}));
|
||||||
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
|
|
||||||
vertical: true });
|
let subtitle = new PopupMenu.PopupMenuItem(_("Session"), { style_class: 'popup-subtitle-menu-item',
|
||||||
this._scrollView.add_actor(this._itemList);
|
reactive: false });
|
||||||
this._scrollView.hide();
|
this._menu.addMenuItem(subtitle);
|
||||||
this.isOpen = false;
|
|
||||||
|
this._manager = new PopupMenu.PopupMenuManager({ actor: this._button });
|
||||||
|
this._manager.addMenu(this._menu);
|
||||||
|
|
||||||
|
this._button.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this._menu.toggle();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._items = {};
|
||||||
|
this._activeSessionId = null;
|
||||||
this._populate();
|
this._populate();
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function() {
|
|
||||||
if (this.isOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._button.add_style_pseudo_class('open');
|
|
||||||
this._scrollView.show();
|
|
||||||
this._triangle.set_text('\u25BE');
|
|
||||||
|
|
||||||
this.isOpen = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
close: function() {
|
|
||||||
if (!this.isOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._button.remove_style_pseudo_class('open');
|
|
||||||
this._scrollView.hide();
|
|
||||||
this._triangle.set_text('\u25B8');
|
|
||||||
|
|
||||||
this.isOpen = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onClicked: function() {
|
|
||||||
if (!this.isOpen)
|
|
||||||
this.open();
|
|
||||||
else
|
|
||||||
this.close();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSensitivity: function(sensitive) {
|
updateSensitivity: function(sensitive) {
|
||||||
this._button.reactive = sensitive;
|
this._button.reactive = sensitive;
|
||||||
this._button.can_focus = sensitive;
|
this._button.can_focus = sensitive;
|
||||||
|
this._menu.close(BoxPointer.PopupAnimation.NONE);
|
||||||
|
},
|
||||||
|
|
||||||
for (let id in this._items)
|
_updateOrnament: function() {
|
||||||
this._items[id].actor.reactive = sensitive;
|
let itemIds = Object.keys(this._items);
|
||||||
|
for (let i = 0; i < itemIds.length; i++) {
|
||||||
|
if (itemIds[i] == this._activeSessionId)
|
||||||
|
this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.DOT);
|
||||||
|
else
|
||||||
|
this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.NONE);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setActiveSession: function(sessionId) {
|
setActiveSession: function(sessionId) {
|
||||||
if (sessionId == this._activeSessionId)
|
if (sessionId == this._activeSessionId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._activeSessionId)
|
|
||||||
this._items[this._activeSessionId].setShowDot(false);
|
|
||||||
|
|
||||||
this._items[sessionId].setShowDot(true);
|
|
||||||
this._activeSessionId = sessionId;
|
this._activeSessionId = sessionId;
|
||||||
|
this._updateOrnament();
|
||||||
|
|
||||||
this.emit('session-activated', this._activeSessionId);
|
this.emit('session-activated', this._activeSessionId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_populate: function() {
|
close: function() {
|
||||||
this._itemList.destroy_all_children();
|
this._menu.close();
|
||||||
this._activeSessionId = null;
|
},
|
||||||
this._items = {};
|
|
||||||
|
|
||||||
|
_populate: function() {
|
||||||
let ids = Gdm.get_session_ids();
|
let ids = Gdm.get_session_ids();
|
||||||
ids.sort();
|
ids.sort();
|
||||||
|
|
||||||
if (ids.length <= 1) {
|
if (ids.length <= 1) {
|
||||||
this._box.hide();
|
|
||||||
this._button.hide();
|
this._button.hide();
|
||||||
} else {
|
return;
|
||||||
this._button.show();
|
|
||||||
this._box.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < ids.length; i++) {
|
for (let i = 0; i < ids.length; i++) {
|
||||||
let [sessionName, sessionDescription] = Gdm.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 id = ids[i];
|
||||||
this._itemList.add_actor(item.actor);
|
let item = new PopupMenu.PopupMenuItem(sessionName);
|
||||||
this._items[ids[i]] = item;
|
this._menu.addMenuItem(item);
|
||||||
|
this._items[id] = item;
|
||||||
|
|
||||||
if (!this._activeSessionId)
|
if (!this._activeSessionId)
|
||||||
this.setActiveSession(ids[i]);
|
this.setActiveSession(id);
|
||||||
|
|
||||||
item.connect('activate',
|
item.connect('activate', Lang.bind(this, function() {
|
||||||
Lang.bind(this, function() {
|
this.setActiveSession(id);
|
||||||
this.setActiveSession(item.id);
|
}));
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(SessionList.prototype);
|
Signals.addSignalMethods(SessionMenuButton.prototype);
|
||||||
|
|
||||||
const LoginDialog = new Lang.Class({
|
const LoginDialog = new Lang.Class({
|
||||||
Name: 'LoginDialog',
|
Name: 'LoginDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
|
||||||
|
|
||||||
_init: function(parentActor) {
|
_init: function(parentActor) {
|
||||||
this.parent({ shellReactive: true,
|
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
|
||||||
styleClass: 'login-dialog',
|
style_class: 'login-dialog',
|
||||||
parentActor: parentActor,
|
visible: false });
|
||||||
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
|
|
||||||
shouldFadeIn: false });
|
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||||
this.connect('destroy',
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
Lang.bind(this, this._onDestroy));
|
parentActor.add_child(this.actor);
|
||||||
this.connect('opened',
|
|
||||||
Lang.bind(this, this._onOpened));
|
|
||||||
|
|
||||||
this._userManager = AccountsService.UserManager.get_default()
|
this._userManager = AccountsService.UserManager.get_default()
|
||||||
this._greeterClient = new Gdm.Client();
|
let gdmClient = new Gdm.Client();
|
||||||
|
|
||||||
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
|
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
|
||||||
this._greeter = this._greeterClient.get_greeter_sync(null);
|
this._greeter = gdmClient.get_greeter_sync(null);
|
||||||
|
|
||||||
this._greeter.connect('default-session-name-changed',
|
this._greeter.connect('default-session-name-changed',
|
||||||
Lang.bind(this, this._onDefaultSessionChanged));
|
Lang.bind(this, this._onDefaultSessionChanged));
|
||||||
@ -535,15 +415,6 @@ const LoginDialog = new Lang.Class({
|
|||||||
Lang.bind(this, this._onTimedLoginRequested));
|
Lang.bind(this, this._onTimedLoginRequested));
|
||||||
}
|
}
|
||||||
|
|
||||||
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('verification-failed', Lang.bind(this, this._verificationFailed));
|
|
||||||
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
|
|
||||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
|
||||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
|
||||||
this._verifyingUser = false;
|
|
||||||
|
|
||||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||||
|
|
||||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
||||||
@ -552,10 +423,20 @@ const LoginDialog = new Lang.Class({
|
|||||||
Lang.bind(this, this._updateBanner));
|
Lang.bind(this, this._updateBanner));
|
||||||
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
|
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
|
||||||
Lang.bind(this, this._updateDisableUserList));
|
Lang.bind(this, this._updateDisableUserList));
|
||||||
|
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
|
||||||
|
Lang.bind(this, this._updateLogo));
|
||||||
|
|
||||||
|
this._textureCache = St.TextureCache.get_default();
|
||||||
|
this._textureCache.connect('texture-file-changed',
|
||||||
|
Lang.bind(this, this._updateLogoTexture));
|
||||||
|
|
||||||
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
|
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
|
||||||
vertical: true });
|
vertical: true,
|
||||||
this.contentLayout.add(this._userSelectionBox);
|
visible: false });
|
||||||
|
this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||||
|
align_axis: Clutter.AlignAxis.BOTH,
|
||||||
|
factor: 0.5 }));
|
||||||
|
this.actor.add_child(this._userSelectionBox);
|
||||||
|
|
||||||
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||||
text: '' });
|
text: '' });
|
||||||
@ -568,61 +449,18 @@ const LoginDialog = new Lang.Class({
|
|||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
|
|
||||||
this.setInitialKeyFocus(this._userList.actor);
|
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
|
||||||
|
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
|
||||||
|
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||||
|
this._authPrompt.hide();
|
||||||
|
|
||||||
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||||
vertical: true });
|
align_axis: Clutter.AlignAxis.BOTH,
|
||||||
this.contentLayout.add(this._promptBox,
|
factor: 0.5 }));
|
||||||
{ expand: true,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START });
|
|
||||||
this._promptUser = new St.Bin({ x_fill: true,
|
|
||||||
x_align: St.Align.START });
|
|
||||||
this._promptBox.add(this._promptUser,
|
|
||||||
{ x_align: St.Align.START,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
|
||||||
|
|
||||||
this._promptBox.add(this._promptLabel,
|
this.actor.add_child(this._authPrompt.actor);
|
||||||
{ expand: true,
|
this._userList.actor.add_constraint(new Clutter.BindConstraint({ source: this._authPrompt.actor,
|
||||||
x_fill: true,
|
coordinate: Clutter.BindCoordinate.WIDTH }));
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START });
|
|
||||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
|
||||||
can_focus: true });
|
|
||||||
this._promptEntryTextChangedId = 0;
|
|
||||||
this._promptEntryActivateId = 0;
|
|
||||||
this._promptBox.add(this._promptEntry,
|
|
||||||
{ expand: true,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START });
|
|
||||||
|
|
||||||
this._promptMessage = new St.Label({ visible: false });
|
|
||||||
this._promptBox.add(this._promptMessage, { x_fill: true });
|
|
||||||
|
|
||||||
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
|
||||||
this._promptLoginHint.hide();
|
|
||||||
this._promptBox.add(this._promptLoginHint);
|
|
||||||
|
|
||||||
this._signInButton = null;
|
|
||||||
this._workSpinner = null;
|
|
||||||
|
|
||||||
this._sessionList = new SessionList();
|
|
||||||
this._sessionList.connect('session-activated',
|
|
||||||
Lang.bind(this, function(list, sessionId) {
|
|
||||||
this._greeter.call_select_session_sync (sessionId, null);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._promptBox.add(this._sessionList.actor,
|
|
||||||
{ expand: true,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: true,
|
|
||||||
x_align: St.Align.START });
|
|
||||||
this._promptBox.hide();
|
|
||||||
|
|
||||||
// translators: this message is shown below the user list on the
|
// translators: this message is shown below the user list on the
|
||||||
// login screen. It can be activated to reveal an entry for
|
// login screen. It can be activated to reveal an entry for
|
||||||
@ -637,13 +475,30 @@ const LoginDialog = new Lang.Class({
|
|||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
|
|
||||||
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
|
this._notListedButton.connect('clicked',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._authPrompt.cancelButton.show();
|
||||||
|
this._hideUserListAskForUsernameAndBeginVerification();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._notListedButton.hide();
|
||||||
|
|
||||||
this._userSelectionBox.add(this._notListedButton,
|
this._userSelectionBox.add(this._notListedButton,
|
||||||
{ expand: false,
|
{ expand: false,
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
|
|
||||||
|
this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
|
||||||
|
this._logoBin.set_y_align(Clutter.ActorAlign.END);
|
||||||
|
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||||
|
align_axis: Clutter.AlignAxis.X_AXIS,
|
||||||
|
factor: 0.5 }));
|
||||||
|
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||||
|
align_axis: Clutter.AlignAxis.Y_AXIS,
|
||||||
|
factor: 1.0 }));
|
||||||
|
this.actor.add_child(this._logoBin);
|
||||||
|
this._updateLogo();
|
||||||
|
|
||||||
if (!this._userManager.is_loaded)
|
if (!this._userManager.is_loaded)
|
||||||
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
|
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
@ -661,20 +516,26 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._onUserListActivated(item);
|
this._onUserListActivated(item);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
this._sessionMenuButton = new SessionMenuButton();
|
||||||
|
this._sessionMenuButton.connect('session-activated',
|
||||||
|
Lang.bind(this, function(list, sessionId) {
|
||||||
|
this._greeter.call_select_session_sync (sessionId, null);
|
||||||
|
}));
|
||||||
|
this._sessionMenuButton.actor.opacity = 0;
|
||||||
|
this._sessionMenuButton.actor.show();
|
||||||
|
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateDisableUserList: function() {
|
_updateDisableUserList: function() {
|
||||||
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
|
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
|
||||||
|
|
||||||
// If this is the first time around, set initial focus
|
|
||||||
if (this._disableUserList == undefined && disableUserList)
|
|
||||||
this.setInitialKeyFocus(this._promptEntry);
|
|
||||||
|
|
||||||
if (disableUserList != this._disableUserList) {
|
if (disableUserList != this._disableUserList) {
|
||||||
this._disableUserList = disableUserList;
|
this._disableUserList = disableUserList;
|
||||||
|
|
||||||
if (!this._verifyingUser)
|
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
|
||||||
this._reset();
|
this._authPrompt.reset();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -690,249 +551,118 @@ const LoginDialog = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_reset: function() {
|
_updateLogoTexture: function(cache, uri) {
|
||||||
this._userVerifier.clear();
|
if (this._logoFileUri != uri)
|
||||||
|
return;
|
||||||
|
|
||||||
this._updateSensitivity(true);
|
let icon = null;
|
||||||
this._promptMessage.hide();
|
if (this._logoFileUri)
|
||||||
this._user = null;
|
icon = this._textureCache.load_uri_async(this._logoFileUri,
|
||||||
this._verifyingUser = false;
|
-1, _LOGO_ICON_HEIGHT);
|
||||||
|
this._logoBin.set_child(icon);
|
||||||
if (this._disableUserList)
|
|
||||||
this._hideUserListAndLogIn();
|
|
||||||
else
|
|
||||||
this._showUserList();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_verificationFailed: function() {
|
_updateLogo: function() {
|
||||||
this._promptEntry.text = '';
|
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
||||||
|
|
||||||
this._updateSensitivity(true);
|
this._logoFileUri = path ? Gio.file_new_for_path(path).get_uri() : null;
|
||||||
this._setWorking(false);
|
this._updateLogoTexture(this._textureCache, this._logoFileUri);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPrompted: function() {
|
||||||
|
this._sessionMenuButton.updateSensitivity(true);
|
||||||
|
|
||||||
|
if (this._shouldShowSessionMenuButton())
|
||||||
|
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
|
||||||
|
this._showPrompt();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onReset: function(authPrompt, beginRequest) {
|
||||||
|
this._sessionMenuButton.updateSensitivity(true);
|
||||||
|
|
||||||
|
this._user = null;
|
||||||
|
|
||||||
|
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||||
|
if (this._disableUserList) {
|
||||||
|
this._authPrompt.cancelButton.hide();
|
||||||
|
this._hideUserListAskForUsernameAndBeginVerification();
|
||||||
|
} else {
|
||||||
|
this._showUserList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._authPrompt.cancelButton.hide();
|
||||||
|
this._hideUserListAndBeginVerification();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDefaultSessionChanged: function(client, sessionId) {
|
_onDefaultSessionChanged: function(client, sessionId) {
|
||||||
this._sessionList.setActiveSession(sessionId);
|
this._sessionMenuButton.setActiveSession(sessionId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_showMessage: function(userVerifier, message, styleClass) {
|
_shouldShowSessionMenuButton: function() {
|
||||||
if (message) {
|
if (this._authPrompt.verifyingUser)
|
||||||
this._promptMessage.text = message;
|
return true;
|
||||||
this._promptMessage.styleClass = styleClass;
|
|
||||||
this._promptMessage.show();
|
if (!this._user)
|
||||||
} else {
|
return false;
|
||||||
this._promptMessage.hide();
|
|
||||||
}
|
if (this._user.is_logged_in)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_showLoginHint: function(verifier, message) {
|
_showPrompt: function() {
|
||||||
this._promptLoginHint.set_text(message)
|
if (this._authPrompt.actor.visible)
|
||||||
this._promptLoginHint.show();
|
return;
|
||||||
this._promptLoginHint.opacity = 255;
|
this._authPrompt.actor.opacity = 0;
|
||||||
},
|
this._authPrompt.actor.show();
|
||||||
|
Tweener.addTween(this._authPrompt.actor,
|
||||||
_hideLoginHint: function() {
|
|
||||||
this._promptLoginHint.hide();
|
|
||||||
this._promptLoginHint.set_text('');
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel: function() {
|
|
||||||
if (this._verifyingUser)
|
|
||||||
this._userVerifier.cancel();
|
|
||||||
else
|
|
||||||
this._reset();
|
|
||||||
},
|
|
||||||
|
|
||||||
_showPrompt: function(forSecret) {
|
|
||||||
this._sessionList.actor.hide();
|
|
||||||
this._promptLabel.show();
|
|
||||||
this._promptEntry.show();
|
|
||||||
this._promptLoginHint.opacity = 0;
|
|
||||||
this._promptLoginHint.show();
|
|
||||||
this._promptBox.opacity = 0;
|
|
||||||
this._promptBox.show();
|
|
||||||
Tweener.addTween(this._promptBox,
|
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
time: _FADE_ANIMATION_TIME,
|
time: _FADE_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad' });
|
transition: 'easeOutQuad' });
|
||||||
|
|
||||||
if ((this._user && !this._user.is_logged_in()) || this._verifyingUser)
|
|
||||||
this._sessionList.actor.show();
|
|
||||||
|
|
||||||
this._promptEntry.grab_key_focus();
|
|
||||||
|
|
||||||
let hold = new Batch.Hold();
|
|
||||||
let tasks = [function() {
|
|
||||||
this._prepareDialog(forSecret, hold);
|
|
||||||
},
|
|
||||||
|
|
||||||
hold];
|
|
||||||
|
|
||||||
let batch = new Batch.ConcurrentBatch(this, tasks);
|
|
||||||
|
|
||||||
return batch.run();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_prepareDialog: function(forSecret, hold) {
|
_showRealmLoginHint: function(realmManager, hint) {
|
||||||
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
if (!hint)
|
||||||
this._workSpinner = new Panel.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
|
||||||
this._workSpinner.actor.opacity = 0;
|
|
||||||
this._workSpinner.actor.show();
|
|
||||||
|
|
||||||
this.buttonLayout.visible = true;
|
|
||||||
this.clearButtons();
|
|
||||||
|
|
||||||
if (!this._disableUserList || this._verifyingUser)
|
|
||||||
this.addButton({ action: Lang.bind(this, this.cancel),
|
|
||||||
label: _("Cancel"),
|
|
||||||
key: Clutter.Escape },
|
|
||||||
{ expand: true,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
this.buttonLayout.add(this._workSpinner.actor,
|
|
||||||
{ expand: false,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.END,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
this._signInButton = this.addButton({ action: Lang.bind(this, function() {
|
|
||||||
hold.release();
|
|
||||||
}),
|
|
||||||
label: forSecret ? C_("button", "Sign In") : _("Next"),
|
|
||||||
default: true },
|
|
||||||
{ expand: false,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.END,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
|
|
||||||
|
|
||||||
this._promptEntryTextChangedId =
|
|
||||||
this._promptEntry.clutter_text.connect('text-changed',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._promptEntryActivateId =
|
|
||||||
this._promptEntry.clutter_text.connect('activate', function() {
|
|
||||||
hold.release();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSensitivity: function(sensitive) {
|
|
||||||
this._promptEntry.reactive = sensitive;
|
|
||||||
this._promptEntry.clutter_text.editable = sensitive;
|
|
||||||
this._sessionList.updateSensitivity(sensitive);
|
|
||||||
this._updateSignInButtonSensitivity(sensitive);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSignInButtonSensitivity: function(sensitive) {
|
|
||||||
if (this._signInButton) {
|
|
||||||
this._signInButton.reactive = sensitive;
|
|
||||||
this._signInButton.can_focus = sensitive;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_hidePrompt: function() {
|
|
||||||
this.setButtons([]);
|
|
||||||
|
|
||||||
if (this._promptEntryTextChangedId > 0) {
|
|
||||||
this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
|
|
||||||
this._promptEntryTextChangedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._promptEntryActivateId > 0) {
|
|
||||||
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
|
|
||||||
this._promptEntryActivateId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setWorking(false);
|
|
||||||
this._promptBox.hide();
|
|
||||||
this._promptLoginHint.hide();
|
|
||||||
|
|
||||||
this._promptUser.set_child(null);
|
|
||||||
|
|
||||||
this._updateSensitivity(true);
|
|
||||||
this._promptEntry.set_text('');
|
|
||||||
|
|
||||||
this._sessionList.close();
|
|
||||||
this._promptLoginHint.hide();
|
|
||||||
|
|
||||||
this.clearButtons();
|
|
||||||
this._workSpinner = null;
|
|
||||||
this._signInButton = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setWorking: function(working) {
|
|
||||||
if (!this._workSpinner)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (working) {
|
hint = hint.replace(/%U/g, 'user');
|
||||||
this._workSpinner.play();
|
hint = hint.replace(/%D/g, 'DOMAIN');
|
||||||
Tweener.addTween(this._workSpinner.actor,
|
hint = hint.replace(/%[^UD]/g, '');
|
||||||
{ opacity: 255,
|
|
||||||
delay: WORK_SPINNER_ANIMATION_DELAY,
|
// Translators: this message is shown below the username entry field
|
||||||
time: WORK_SPINNER_ANIMATION_TIME,
|
// to clue the user in on how to login to the local network realm
|
||||||
transition: 'linear'
|
this._authPrompt.setHint(_("(e.g., user or %s)").format(hint));
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Tweener.addTween(this._workSpinner.actor,
|
|
||||||
{ opacity: 0,
|
|
||||||
time: WORK_SPINNER_ANIMATION_TIME,
|
|
||||||
transition: 'linear',
|
|
||||||
onCompleteScope: this,
|
|
||||||
onComplete: function() {
|
|
||||||
if (this._workSpinner)
|
|
||||||
this._workSpinner.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_askQuestion: function(verifier, serviceName, question, passwordChar) {
|
_askForUsernameAndBeginVerification: function() {
|
||||||
this._promptLabel.set_text(question);
|
this._authPrompt.setPasswordChar('');
|
||||||
|
this._authPrompt.setQuestion(_("Username: "));
|
||||||
|
|
||||||
this._updateSensitivity(true);
|
let realmManager = new Realmd.Manager();
|
||||||
this._promptEntry.set_text('');
|
let realmSignalId = realmManager.connect('login-format-changed',
|
||||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
Lang.bind(this, this._showRealmLoginHint));
|
||||||
|
this._showRealmLoginHint(realmManager.loginFormat);
|
||||||
|
|
||||||
let tasks = [function() {
|
let nextSignalId = this._authPrompt.connect('next',
|
||||||
return this._showPrompt(!!passwordChar);
|
Lang.bind(this, function() {
|
||||||
},
|
this._authPrompt.disconnect(nextSignalId);
|
||||||
|
this._authPrompt.updateSensitivity(false);
|
||||||
|
let answer = this._authPrompt.getAnswer();
|
||||||
|
this._authPrompt.clear();
|
||||||
|
this._authPrompt.cancelButton.show();
|
||||||
|
this._authPrompt.startSpinning();
|
||||||
|
this._authPrompt.begin({ userName: answer });
|
||||||
|
|
||||||
function() {
|
realmManager.disconnect(realmSignalId)
|
||||||
let text = this._promptEntry.get_text();
|
realmManager.release();
|
||||||
this._updateSensitivity(false);
|
}));
|
||||||
this._setWorking(true);
|
this._showPrompt();
|
||||||
this._userVerifier.answerQuery(serviceName, text);
|
|
||||||
}];
|
|
||||||
|
|
||||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
|
||||||
return batch.run();
|
|
||||||
},
|
|
||||||
|
|
||||||
_askForUsernameAndLogIn: function() {
|
|
||||||
this._promptLabel.set_text(_("Username: "));
|
|
||||||
this._promptEntry.set_text('');
|
|
||||||
this._promptEntry.clutter_text.set_password_char('');
|
|
||||||
|
|
||||||
let tasks = [this._showPrompt,
|
|
||||||
|
|
||||||
function() {
|
|
||||||
let userName = this._promptEntry.get_text();
|
|
||||||
this._promptEntry.reactive = false;
|
|
||||||
return this._beginVerificationForUser(userName);
|
|
||||||
}];
|
|
||||||
|
|
||||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
|
||||||
return batch.run();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_startSession: function(serviceName) {
|
_startSession: function(serviceName) {
|
||||||
Tweener.addTween(this.dialogLayout,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: _FADE_ANIMATION_TIME,
|
time: _FADE_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
@ -941,7 +671,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
if (children[i] != Main.layoutManager.screenShieldGroup)
|
if (children[i] != Main.layoutManager.screenShieldGroup)
|
||||||
children[i].opacity = this.dialogLayout.opacity;
|
children[i].opacity = this.actor.opacity;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onUpdateScope: this,
|
onUpdateScope: this,
|
||||||
@ -955,15 +685,9 @@ const LoginDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onSessionOpened: function(client, serviceName) {
|
_onSessionOpened: function(client, serviceName) {
|
||||||
if (!this._userVerifier.hasPendingMessages) {
|
this._authPrompt.finish(Lang.bind(this, function() {
|
||||||
this._startSession(serviceName);
|
this._startSession(serviceName);
|
||||||
} else {
|
}));
|
||||||
let signalId = this._userVerifier.connect('no-more-messages',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._userVerifier.disconnect(signalId);
|
|
||||||
this._startSession(serviceName);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_waitForItemForUser: function(userName) {
|
_waitForItemForUser: function(userName) {
|
||||||
@ -1095,38 +819,42 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._userSelectionBox.visible = expanded;
|
this._userSelectionBox.visible = expanded;
|
||||||
},
|
},
|
||||||
|
|
||||||
_hideUserListAndLogIn: function() {
|
_hideUserList: function() {
|
||||||
this._setUserListExpanded(false);
|
this._setUserListExpanded(false);
|
||||||
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
if (this._userSelectionBox.visible)
|
||||||
this._askForUsernameAndLogIn();
|
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideUserListAskForUsernameAndBeginVerification: function() {
|
||||||
|
this._hideUserList();
|
||||||
|
this._askForUsernameAndBeginVerification();
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideUserListAndBeginVerification: function() {
|
||||||
|
this._hideUserList();
|
||||||
|
this._authPrompt.begin();
|
||||||
},
|
},
|
||||||
|
|
||||||
_showUserList: function() {
|
_showUserList: function() {
|
||||||
this._hidePrompt();
|
this._authPrompt.hide();
|
||||||
|
this._sessionMenuButton.close();
|
||||||
|
this._authPrompt.cancelButton.show();
|
||||||
this._setUserListExpanded(true);
|
this._setUserListExpanded(true);
|
||||||
|
this._notListedButton.show();
|
||||||
this._userList.actor.grab_key_focus();
|
this._userList.actor.grab_key_focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
_beginVerificationForUser: function(userName) {
|
_beginVerificationForItem: function(item) {
|
||||||
|
this._authPrompt.setUser(item.user);
|
||||||
|
|
||||||
|
let userName = item.user.get_user_name();
|
||||||
let hold = new Batch.Hold();
|
let hold = new Batch.Hold();
|
||||||
|
|
||||||
this._userVerifier.begin(userName, hold);
|
this._authPrompt.begin({ userName: userName,
|
||||||
this._verifyingUser = true;
|
hold: hold });
|
||||||
return hold;
|
return hold;
|
||||||
},
|
},
|
||||||
|
|
||||||
_beginVerificationForItem: function(item) {
|
|
||||||
let userWidget = new UserWidget.UserWidget(item.user);
|
|
||||||
this._promptUser.set_child(userWidget.actor);
|
|
||||||
|
|
||||||
let tasks = [function() {
|
|
||||||
let userName = item.user.get_user_name();
|
|
||||||
return this._beginVerificationForUser(userName);
|
|
||||||
}];
|
|
||||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
|
||||||
return batch.run();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onUserListActivated: function(activatedItem) {
|
_onUserListActivated: function(activatedItem) {
|
||||||
let tasks = [function() {
|
let tasks = [function() {
|
||||||
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||||
@ -1169,21 +897,23 @@ const LoginDialog = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOpened: function() {
|
open: function() {
|
||||||
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
|
Main.ctrlAltTabManager.addGroup(this.actor,
|
||||||
_("Login Window"),
|
_("Login Window"),
|
||||||
'dialog-password-symbolic',
|
'dialog-password-symbolic',
|
||||||
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
|
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
|
||||||
|
this._userList.actor.grab_key_focus();
|
||||||
|
this.actor.show();
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
close: function() {
|
close: function() {
|
||||||
this.parent();
|
|
||||||
|
|
||||||
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
|
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
|
||||||
},
|
},
|
||||||
|
|
||||||
addCharacter: function(unichar) {
|
addCharacter: function(unichar) {
|
||||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
this._authPrompt.addCharacter(unichar);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(LoginDialog.prototype);
|
||||||
|
@ -63,7 +63,7 @@ const Manager = new Lang.Class({
|
|||||||
Lang.bind(this, this._reloadRealms))
|
Lang.bind(this, this._reloadRealms))
|
||||||
this._realms = {};
|
this._realms = {};
|
||||||
|
|
||||||
this._aggregateProvider.connect('g-properties-changed',
|
this._signalId = this._aggregateProvider.connect('g-properties-changed',
|
||||||
Lang.bind(this, function(proxy, properties) {
|
Lang.bind(this, function(proxy, properties) {
|
||||||
if ('Realms' in properties.deep_unpack())
|
if ('Realms' in properties.deep_unpack())
|
||||||
this._reloadRealms();
|
this._reloadRealms();
|
||||||
@ -106,7 +106,7 @@ const Manager = new Lang.Class({
|
|||||||
realm.connect('g-properties-changed',
|
realm.connect('g-properties-changed',
|
||||||
Lang.bind(this, function(proxy, properties) {
|
Lang.bind(this, function(proxy, properties) {
|
||||||
if ('Configured' in properties.deep_unpack())
|
if ('Configured' in properties.deep_unpack())
|
||||||
this._reloadRealm();
|
this._reloadRealm(realm);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -134,6 +134,18 @@ const Manager = new Lang.Class({
|
|||||||
this._updateLoginFormat();
|
this._updateLoginFormat();
|
||||||
|
|
||||||
return this._loginFormat;
|
return this._loginFormat;
|
||||||
|
},
|
||||||
|
|
||||||
|
release: function() {
|
||||||
|
Service(Gio.DBus.system,
|
||||||
|
'org.freedesktop.realmd',
|
||||||
|
'/org/freedesktop/realmd',
|
||||||
|
function(service) {
|
||||||
|
service.ReleaseRemote();
|
||||||
|
});
|
||||||
|
this._aggregateProvider.disconnect(this._signalId);
|
||||||
|
this._realms = { };
|
||||||
|
this._updateLoginFormat();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(Manager.prototype)
|
Signals.addSignalMethods(Manager.prototype)
|
||||||
|
175
js/gdm/util.js
175
js/gdm/util.js
@ -6,21 +6,26 @@ const GLib = imports.gi.GLib;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Batch = imports.gdm.batch;
|
const Batch = imports.gdm.batch;
|
||||||
const Fprint = imports.gdm.fingerprint;
|
const Fprint = imports.gdm.fingerprint;
|
||||||
const Realmd = imports.gdm.realmd;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
const SmartcardManager = imports.misc.smartcardManager;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||||
|
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
|
||||||
const FADE_ANIMATION_TIME = 0.16;
|
const FADE_ANIMATION_TIME = 0.16;
|
||||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
||||||
|
|
||||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||||
|
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
|
||||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||||
|
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
|
||||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||||
@ -115,12 +120,34 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._client = client;
|
this._client = client;
|
||||||
|
|
||||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||||
|
this._settings.connect('changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._updateDefaultService();
|
||||||
|
}));
|
||||||
|
this._updateDefaultService();
|
||||||
|
|
||||||
this._fprintManager = new Fprint.FprintManager();
|
this._fprintManager = new Fprint.FprintManager();
|
||||||
this._realmManager = new Realmd.Manager();
|
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||||
|
|
||||||
|
// We check for smartcards right away, since an inserted smartcard
|
||||||
|
// at startup should result in immediately initiating authentication.
|
||||||
|
// This is different than fingeprint readers, where we only check them
|
||||||
|
// after a user has been picked.
|
||||||
|
this._checkForSmartcard();
|
||||||
|
|
||||||
|
this._smartcardManager.connect('smartcard-inserted',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._checkForSmartcard();
|
||||||
|
}));
|
||||||
|
this._smartcardManager.connect('smartcard-removed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._checkForSmartcard();
|
||||||
|
}));
|
||||||
|
|
||||||
this._messageQueue = [];
|
this._messageQueue = [];
|
||||||
this._messageQueueTimeoutId = 0;
|
this._messageQueueTimeoutId = 0;
|
||||||
this.hasPendingMessages = false;
|
this.hasPendingMessages = false;
|
||||||
|
this.reauthenticating = false;
|
||||||
|
|
||||||
this._failCounter = 0;
|
this._failCounter = 0;
|
||||||
},
|
},
|
||||||
@ -129,6 +156,7 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._cancellable = new Gio.Cancellable();
|
this._cancellable = new Gio.Cancellable();
|
||||||
this._hold = hold;
|
this._hold = hold;
|
||||||
this._userName = userName;
|
this._userName = userName;
|
||||||
|
this.reauthenticating = false;
|
||||||
|
|
||||||
this._checkForFingerprintReader();
|
this._checkForFingerprintReader();
|
||||||
|
|
||||||
@ -146,8 +174,10 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
if (this._cancellable)
|
if (this._cancellable)
|
||||||
this._cancellable.cancel();
|
this._cancellable.cancel();
|
||||||
|
|
||||||
if (this._userVerifier)
|
if (this._userVerifier) {
|
||||||
this._userVerifier.call_cancel_sync(null);
|
this._userVerifier.call_cancel_sync(null);
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clear: function() {
|
clear: function() {
|
||||||
@ -165,14 +195,14 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
answerQuery: function(serviceName, answer) {
|
answerQuery: function(serviceName, answer) {
|
||||||
if (!this._userVerifier.hasPendingMessages) {
|
if (!this.hasPendingMessages) {
|
||||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||||
} else {
|
} else {
|
||||||
let signalId = this._userVerifier.connect('no-more-messages',
|
let signalId = this.connect('no-more-messages',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this._userVerifier.disconnect(signalId);
|
this.disconnect(signalId);
|
||||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -242,6 +272,28 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_checkForSmartcard: function() {
|
||||||
|
let smartcardDetected;
|
||||||
|
|
||||||
|
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||||
|
smartcardDetected = false;
|
||||||
|
else if (this.reauthenticating)
|
||||||
|
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
|
||||||
|
else
|
||||||
|
smartcardDetected = this._smartcardManager.hasInsertedTokens();
|
||||||
|
|
||||||
|
if (smartcardDetected != this.smartcardDetected) {
|
||||||
|
this.smartcardDetected = smartcardDetected;
|
||||||
|
|
||||||
|
if (this.smartcardDetected)
|
||||||
|
this._preemptingService = SMARTCARD_SERVICE_NAME;
|
||||||
|
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
|
||||||
|
this._preemptingService = null;
|
||||||
|
|
||||||
|
this.emit('smartcard-status-changed');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_reportInitError: function(where, error) {
|
_reportInitError: function(where, error) {
|
||||||
logError(error, where);
|
logError(error, where);
|
||||||
this._hold.release();
|
this._hold.release();
|
||||||
@ -267,6 +319,7 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.reauthenticating = true;
|
||||||
this._connectSignals();
|
this._connectSignals();
|
||||||
this._beginVerification();
|
this._beginVerification();
|
||||||
this._hold.release();
|
this._hold.release();
|
||||||
@ -297,11 +350,35 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getForegroundService: function() {
|
||||||
|
if (this._preemptingService)
|
||||||
|
return this._preemptingService;
|
||||||
|
|
||||||
|
return this._defaultService;
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceIsForeground: function(serviceName) {
|
||||||
|
return serviceName == this._getForegroundService();
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceIsDefault: function(serviceName) {
|
||||||
|
return serviceName == this._defaultService;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDefaultService: function() {
|
||||||
|
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
|
||||||
|
this._defaultService = PASSWORD_SERVICE_NAME;
|
||||||
|
else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||||
|
this._defaultService = SMARTCARD_SERVICE_NAME;
|
||||||
|
else if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
||||||
|
this._defaultService = FINGERPRINT_SERVICE_NAME;
|
||||||
|
},
|
||||||
|
|
||||||
_beginVerification: function() {
|
_beginVerification: function() {
|
||||||
this._hold.acquire();
|
this._hold.acquire();
|
||||||
|
|
||||||
if (this._userName) {
|
if (this._userName) {
|
||||||
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
this._userVerifier.call_begin_verification_for_user(this._getForegroundService(),
|
||||||
this._userName,
|
this._userName,
|
||||||
this._cancellable,
|
this._cancellable,
|
||||||
Lang.bind(this, function(obj, result) {
|
Lang.bind(this, function(obj, result) {
|
||||||
@ -317,7 +394,7 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._hold.release();
|
this._hold.release();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (this._haveFingerprintReader) {
|
if (this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) {
|
||||||
this._hold.acquire();
|
this._hold.acquire();
|
||||||
|
|
||||||
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
||||||
@ -337,7 +414,7 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
this._userVerifier.call_begin_verification(this._getForegroundService(),
|
||||||
this._cancellable,
|
this._cancellable,
|
||||||
Lang.bind(this, function(obj, result) {
|
Lang.bind(this, function(obj, result) {
|
||||||
try {
|
try {
|
||||||
@ -355,58 +432,36 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onInfo: function(client, serviceName, info) {
|
_onInfo: function(client, serviceName, info) {
|
||||||
// We don't display fingerprint messages, because they
|
if (this.serviceIsForeground(serviceName)) {
|
||||||
// have words like UPEK in them. Instead we use the messages
|
this._queueMessage(info, 'login-dialog-message-info');
|
||||||
// as a cue to display our own message.
|
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||||
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
|
||||||
this._haveFingerprintReader) {
|
this._haveFingerprintReader) {
|
||||||
|
// We don't show fingeprint messages directly since it's
|
||||||
|
// not the main auth service. Instead we use the messages
|
||||||
|
// as a cue to display our own message.
|
||||||
|
|
||||||
// Translators: this message is shown below the password entry field
|
// Translators: this message is shown below the password entry field
|
||||||
// to indicate the user can swipe their finger instead
|
// to indicate the user can swipe their finger instead
|
||||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
|
||||||
this._queueMessage(info, 'login-dialog-message-info');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onProblem: function(client, serviceName, problem) {
|
_onProblem: function(client, serviceName, problem) {
|
||||||
// we don't want to show auth failed messages to
|
if (!this.serviceIsForeground(serviceName))
|
||||||
// users who haven't enrolled their fingerprint.
|
|
||||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._queueMessage(problem, 'login-dialog-message-warning');
|
this._queueMessage(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) {
|
_onInfoQuery: function(client, serviceName, question) {
|
||||||
// We only expect questions to come from the main auth service
|
if (!this.serviceIsForeground(serviceName))
|
||||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._showRealmLoginHint();
|
|
||||||
this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
|
|
||||||
Lang.bind(this, this._showRealmLoginHint));
|
|
||||||
|
|
||||||
this.emit('ask-question', serviceName, question, '');
|
this.emit('ask-question', serviceName, question, '');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||||
// We only expect secret requests to come from the main auth service
|
if (!this.serviceIsForeground(serviceName))
|
||||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||||
@ -415,6 +470,7 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
_onReset: function() {
|
_onReset: function() {
|
||||||
// Clear previous attempts to authenticate
|
// Clear previous attempts to authenticate
|
||||||
this._failCounter = 0;
|
this._failCounter = 0;
|
||||||
|
this._updateDefaultService();
|
||||||
|
|
||||||
this.emit('reset');
|
this.emit('reset');
|
||||||
},
|
},
|
||||||
@ -443,24 +499,24 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||||
|
|
||||||
if (canRetry) {
|
if (canRetry) {
|
||||||
if (!this._userVerifier.hasPendingMessages) {
|
if (!this.hasPendingMessages) {
|
||||||
this._retry();
|
this._retry();
|
||||||
} else {
|
} else {
|
||||||
let signalId = this._userVerifier.connect('no-more-messages',
|
let signalId = this.connect('no-more-messages',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this._userVerifier.disconnect(signalId);
|
this.disconnect(signalId);
|
||||||
this._retry();
|
this._retry();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!this._userVerifier.hasPendingMessages) {
|
if (!this.hasPendingMessages) {
|
||||||
this._cancelAndReset();
|
this._cancelAndReset();
|
||||||
} else {
|
} else {
|
||||||
let signalId = this._userVerifier.connect('no-more-messages',
|
let signalId = this.connect('no-more-messages',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this._userVerifier.disconnect(signalId);
|
this.disconnect(signalId);
|
||||||
this._cancelAndReset();
|
this._cancelAndReset();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,16 +527,11 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
// if the password service fails, then cancel everything.
|
// if the password service fails, then cancel everything.
|
||||||
// But if, e.g., fingerprint fails, still give
|
// But if, e.g., fingerprint fails, still give
|
||||||
// password authentication a chance to succeed
|
// password authentication a chance to succeed
|
||||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
if (this.serviceIsForeground(serviceName)) {
|
||||||
this._verificationFailed(true);
|
this._verificationFailed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('hide-login-hint');
|
this.emit('hide-login-hint');
|
||||||
|
|
||||||
if (this._realmLoginHintSignalId) {
|
|
||||||
this._realmManager.disconnect(this._realmLoginHintSignalId);
|
|
||||||
this._realmLoginHintSignalId = 0;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
||||||
|
@ -58,6 +58,7 @@ const Map = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(iterable) {
|
_init: function(iterable) {
|
||||||
this._pool = { };
|
this._pool = { };
|
||||||
|
this._size = 0;
|
||||||
|
|
||||||
if (iterable) {
|
if (iterable) {
|
||||||
for (let i = 0; i < iterable.length; i++) {
|
for (let i = 0; i < iterable.length; i++) {
|
||||||
@ -99,6 +100,7 @@ const Map = new Lang.Class({
|
|||||||
node.value = value;
|
node.value = value;
|
||||||
} else {
|
} else {
|
||||||
this._pool[hash] = { key: key, value: value };
|
this._pool[hash] = { key: key, value: value };
|
||||||
|
this._size++;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -108,6 +110,7 @@ const Map = new Lang.Class({
|
|||||||
|
|
||||||
if (node && _sameValue(node.key, key)) {
|
if (node && _sameValue(node.key, key)) {
|
||||||
delete this._pool[hash];
|
delete this._pool[hash];
|
||||||
|
this._size--;
|
||||||
return [node.key, node.value];
|
return [node.key, node.value];
|
||||||
} else {
|
} else {
|
||||||
return [null, null];
|
return [null, null];
|
||||||
@ -136,6 +139,6 @@ const Map = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
size: function() {
|
size: function() {
|
||||||
return Object.getOwnPropertyNames(this._pool).length;
|
return this._size;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
275
js/misc/objectManager.js
Normal file
275
js/misc/objectManager.js
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
// Specified in the D-Bus specification here:
|
||||||
|
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
||||||
|
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
|
||||||
|
<method name="GetManagedObjects">
|
||||||
|
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<signal name="InterfacesAdded">
|
||||||
|
<arg name="objectPath" type="o"/>
|
||||||
|
<arg name="interfaces" type="a{sa{sv}}" />
|
||||||
|
</signal>
|
||||||
|
<signal name="InterfacesRemoved">
|
||||||
|
<arg name="objectPath" type="o"/>
|
||||||
|
<arg name="interfaces" type="as" />
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
|
||||||
|
|
||||||
|
const ObjectManager = new Lang.Class({
|
||||||
|
Name: 'ObjectManager',
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { connection: null,
|
||||||
|
name: null,
|
||||||
|
objectPath: null,
|
||||||
|
knownInterfaces: null,
|
||||||
|
cancellable: null,
|
||||||
|
onLoaded: null });
|
||||||
|
|
||||||
|
this._connection = params.connection;
|
||||||
|
this._serviceName = params.name;
|
||||||
|
this._managerPath = params.objectPath;
|
||||||
|
this._cancellable = params.cancellable;
|
||||||
|
|
||||||
|
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||||
|
g_interface_name: ObjectManagerInfo.name,
|
||||||
|
g_interface_info: ObjectManagerInfo,
|
||||||
|
g_name: this._serviceName,
|
||||||
|
g_object_path: this._managerPath,
|
||||||
|
g_flags: Gio.DBusProxyFlags.NONE });
|
||||||
|
|
||||||
|
this._interfaceInfos = {};
|
||||||
|
this._objects = {};
|
||||||
|
this._interfaces = {};
|
||||||
|
this._pendingProxies = [];
|
||||||
|
this._onLoaded = params.onLoaded;
|
||||||
|
|
||||||
|
if (params.knownInterfaces)
|
||||||
|
this._registerInterfaces(params.knownInterfaces);
|
||||||
|
|
||||||
|
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, this._onManagerProxyLoaded));
|
||||||
|
},
|
||||||
|
|
||||||
|
_addInterface: function(objectPath, interfaceName, onFinished) {
|
||||||
|
let info = this._interfaceInfos[interfaceName];
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||||
|
g_name: this._serviceName,
|
||||||
|
g_object_path: objectPath,
|
||||||
|
g_interface_name: interfaceName,
|
||||||
|
g_interface_info: info,
|
||||||
|
g_flags: Gio.DBusProxyFlags.NONE });
|
||||||
|
|
||||||
|
this._pendingProxies.push(proxy);
|
||||||
|
|
||||||
|
proxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, function(initable, result) {
|
||||||
|
let index = this._pendingProxies.indexOf(proxy);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
this._pendingProxies.splice(index, 1);
|
||||||
|
|
||||||
|
let error = null;
|
||||||
|
try {
|
||||||
|
initable.init_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, 'could not initialize proxy for interface ' + interfaceName);
|
||||||
|
|
||||||
|
if (onFinished)
|
||||||
|
onFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isNewObject;
|
||||||
|
|
||||||
|
if (!this._objects[objectPath]) {
|
||||||
|
this._objects[objectPath] = {};
|
||||||
|
isNewObject = true;
|
||||||
|
} else {
|
||||||
|
isNewObject = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._objects[objectPath][interfaceName] = proxy;
|
||||||
|
|
||||||
|
if (!this._interfaces[interfaceName])
|
||||||
|
this._interfaces[interfaceName] = [];
|
||||||
|
|
||||||
|
this._interfaces[interfaceName].push(proxy);
|
||||||
|
|
||||||
|
if (isNewObject)
|
||||||
|
this.emit('object-added', objectPath);
|
||||||
|
|
||||||
|
this.emit('interface-added', interfaceName, proxy);
|
||||||
|
|
||||||
|
if (onFinished)
|
||||||
|
onFinished();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeInterface: function(objectPath, interfaceName) {
|
||||||
|
if (!this._objects[objectPath])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let proxy = this._objects[objectPath][interfaceName];
|
||||||
|
|
||||||
|
if (this._interfaces[interfaceName]) {
|
||||||
|
let index = this._interfaces[interfaceName].indexOf(proxy);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
this._interfaces[interfaceName].splice(index, 1);
|
||||||
|
|
||||||
|
if (this._interfaces[interfaceName].length == 0)
|
||||||
|
delete this._interfaces[interfaceName];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('interface-removed', interfaceName, proxy);
|
||||||
|
|
||||||
|
this._objects[objectPath][interfaceName] = null;
|
||||||
|
|
||||||
|
if (Object.keys(this._objects[objectPath]).length == 0) {
|
||||||
|
delete this._objects[objectPath];
|
||||||
|
this.emit('object-removed', objectPath);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onManagerProxyLoaded: function(initable, result) {
|
||||||
|
let error = null;
|
||||||
|
try {
|
||||||
|
initable.init_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, 'could not initialize object manager for object ' + params.name);
|
||||||
|
|
||||||
|
if (this._onLoaded)
|
||||||
|
this._onLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._managerProxy.connectSignal('InterfacesAdded',
|
||||||
|
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
|
||||||
|
let interfaceNames = Object.keys(interfaces);
|
||||||
|
for (let i = 0; i < interfaceNames.length; i++)
|
||||||
|
this._addInterface(objectPath, interfaceNames[i]);
|
||||||
|
}));
|
||||||
|
this._managerProxy.connectSignal('InterfacesRemoved',
|
||||||
|
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
|
||||||
|
for (let i = 0; i < interfaceNames.length; i++)
|
||||||
|
this._removeInterface(objectPath, interfaceNames[i]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
|
||||||
|
if (!result) {
|
||||||
|
if (error) {
|
||||||
|
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onLoaded)
|
||||||
|
this._onLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [objects] = result;
|
||||||
|
|
||||||
|
if (Object.keys(this._interfaceInfos).length == 0) {
|
||||||
|
if (this._onLoaded)
|
||||||
|
this._onLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let numLoadInhibitors = 0;
|
||||||
|
|
||||||
|
// First inhibitor is to prevent onLoaded from getting
|
||||||
|
// called until all interfaces have started being added.
|
||||||
|
// Subsequent inhibitors are to prevent onLoaded from getting
|
||||||
|
// called until all interfaces finish getting added.
|
||||||
|
numLoadInhibitors++;
|
||||||
|
let objectPaths = Object.keys(objects);
|
||||||
|
for (let i = 0; i < objectPaths.length; i++) {
|
||||||
|
let objectPath = objectPaths[i];
|
||||||
|
let object = objects[objectPath];
|
||||||
|
|
||||||
|
let interfaceNames = Object.keys(object);
|
||||||
|
for (let j = 0; j < interfaceNames.length; j++) {
|
||||||
|
let interfaceName = interfaceNames[j];
|
||||||
|
|
||||||
|
numLoadInhibitors++;
|
||||||
|
this._addInterface(objectPath,
|
||||||
|
interfaceName,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
numLoadInhibitors--;
|
||||||
|
|
||||||
|
if (numLoadInhibitors == 0) {
|
||||||
|
if (this._onLoaded)
|
||||||
|
this._onLoaded();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numLoadInhibitors--;
|
||||||
|
|
||||||
|
if (numLoadInhibitors == 0) {
|
||||||
|
if (this._onLoaded)
|
||||||
|
this._onLoaded();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_registerInterfaces: function(interfaces) {
|
||||||
|
for (let i = 0; i < interfaces.length; i++) {
|
||||||
|
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
|
||||||
|
|
||||||
|
this._interfaceInfos[info.name] = info;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getProxy: function(objectPath, interfaceName) {
|
||||||
|
let object = this._objects[objectPath];
|
||||||
|
|
||||||
|
if (!object)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return object[interfaceName];
|
||||||
|
},
|
||||||
|
|
||||||
|
getProxiesForInterface: function(interfaceName) {
|
||||||
|
let proxyList = this._interfaces[interfaceName];
|
||||||
|
|
||||||
|
if (!proxyList)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return proxyList;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllProxies: function() {
|
||||||
|
let proxies = [];
|
||||||
|
|
||||||
|
let objectPaths = Object.keys(this._objects);
|
||||||
|
for (let i = 0; i < objectPaths.length; i++) {
|
||||||
|
let object = this._objects[objectPaths];
|
||||||
|
|
||||||
|
let interfaceNames = Object.keys(object);
|
||||||
|
for (let j = 0; i < interfaceNames.length; i++) {
|
||||||
|
let interfaceName = interfaceNames[i];
|
||||||
|
if (object[interfaceName])
|
||||||
|
proxies.push(object(interfaceName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxies;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ObjectManager.prototype);
|
142
js/misc/smartcardManager.js
Normal file
142
js/misc/smartcardManager.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// -*- 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 ObjectManager = imports.misc.objectManager;
|
||||||
|
|
||||||
|
const _SMARTCARD_SERVICE_DBUS_NAME = "org.gnome.SettingsDaemon.Smartcard";
|
||||||
|
|
||||||
|
const SmartcardManagerIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Manager">
|
||||||
|
<method name="GetLoginToken">
|
||||||
|
<arg name="token" type="o" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetInsertedTokens">
|
||||||
|
<arg name="tokens" type="ao" direction="out"/>
|
||||||
|
</method>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
|
||||||
|
<property name="Name" type="s" access="read"/>
|
||||||
|
<property name="Driver" type="o" access="read"/>
|
||||||
|
<property name="IsInserted" type="b" access="read"/>
|
||||||
|
<property name="UsedToLogin" type="b" access="read"/>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const SmartcardDriverIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Driver">
|
||||||
|
<property name="Library" type="s" access="read"/>
|
||||||
|
<property name="Description" type="s" access="read"/>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
let _smartcardManager = null;
|
||||||
|
|
||||||
|
function getSmartcardManager() {
|
||||||
|
if (_smartcardManager == null)
|
||||||
|
_smartcardManager = new SmartcardManager();
|
||||||
|
|
||||||
|
return _smartcardManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SmartcardManager = new Lang.Class({
|
||||||
|
Name: 'SmartcardManager',
|
||||||
|
_init: function() {
|
||||||
|
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
|
||||||
|
name: _SMARTCARD_SERVICE_DBUS_NAME,
|
||||||
|
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
|
||||||
|
knownInterfaces: [ SmartcardManagerIface,
|
||||||
|
SmartcardTokenIface,
|
||||||
|
SmartcardDriverIface ],
|
||||||
|
onLoaded: Lang.bind(this, this._onLoaded) });
|
||||||
|
this._insertedTokens = {};
|
||||||
|
this._removedTokens = {};
|
||||||
|
this._loginToken = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLoaded: function() {
|
||||||
|
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
|
||||||
|
|
||||||
|
for (let i = 0; i < tokens.length; i++)
|
||||||
|
this._addToken(tokens[i]);
|
||||||
|
|
||||||
|
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||||
|
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||||
|
this._addToken(proxy);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||||
|
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||||
|
this._removeToken(proxy);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateToken: function(token) {
|
||||||
|
let objectPath = token.get_object_path();
|
||||||
|
|
||||||
|
delete this._insertedTokens[objectPath];
|
||||||
|
delete this._removedTokens[objectPath];
|
||||||
|
|
||||||
|
if (token.IsInserted)
|
||||||
|
this._insertedTokens[objectPath] = token;
|
||||||
|
else
|
||||||
|
this._removedTokens[objectPath] = token;
|
||||||
|
|
||||||
|
if (token.UsedToLogin)
|
||||||
|
this._loginToken = token;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addToken: function(token) {
|
||||||
|
this._updateToken(token);
|
||||||
|
|
||||||
|
token.connect('g-properties-changed',
|
||||||
|
Lang.bind(this, function(proxy, properties) {
|
||||||
|
if ('IsInserted' in properties.deep_unpack()) {
|
||||||
|
this._updateToken(token);
|
||||||
|
|
||||||
|
if (token.IsInserted) {
|
||||||
|
this.emit('smartcard-inserted', token.Name);
|
||||||
|
} else {
|
||||||
|
this.emit('smartcard-removed', token.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Emit a smartcard-inserted at startup if it's already plugged in
|
||||||
|
if (token.IsInserted)
|
||||||
|
this.emit('smartcard-inserted', token.Name);
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeToken: function(token) {
|
||||||
|
let objectPath = token.get_object_path();
|
||||||
|
|
||||||
|
if (objectPath) {
|
||||||
|
if (this._removedTokens[objectPath] == token) {
|
||||||
|
delete this._removedTokens[objectPath];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._insertedTokens[objectPath] == token) {
|
||||||
|
delete this._insertedTokens[objectPath];
|
||||||
|
this.emit('smartcard-removed', token.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token.disconnectAll();
|
||||||
|
},
|
||||||
|
|
||||||
|
hasInsertedTokens: function() {
|
||||||
|
return Object.keys(this._insertedTokens).length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
hasInsertedLoginToken: function() {
|
||||||
|
if (!this._loginToken)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!this._loginToken.IsInserted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(SmartcardManager.prototype);
|
@ -18,7 +18,7 @@ const _urlRegexp = new RegExp(
|
|||||||
'(^|' + _leadingJunk + ')' +
|
'(^|' + _leadingJunk + ')' +
|
||||||
'(' +
|
'(' +
|
||||||
'(?:' +
|
'(?:' +
|
||||||
'[a-z][\\w-]+://' + // scheme://
|
'(?:http|https|ftp)://' + // scheme://
|
||||||
'|' +
|
'|' +
|
||||||
'www\\d{0,3}[.]' + // www.
|
'www\\d{0,3}[.]' + // www.
|
||||||
'|' +
|
'|' +
|
||||||
|
@ -234,7 +234,7 @@ const AppSwitcherPopup = new Lang.Class({
|
|||||||
_finish : function(timestamp) {
|
_finish : function(timestamp) {
|
||||||
let appIcon = this._items[this._selectedIndex];
|
let appIcon = this._items[this._selectedIndex];
|
||||||
if (this._currentWindow < 0)
|
if (this._currentWindow < 0)
|
||||||
appIcon.app.activate_full(-1, timestamp);
|
appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
|
||||||
else
|
else
|
||||||
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
|
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
|
||||||
|
|
||||||
@ -436,8 +436,11 @@ const AppSwitcher = new Lang.Class({
|
|||||||
this._arrows = [];
|
this._arrows = [];
|
||||||
|
|
||||||
let windowTracker = Shell.WindowTracker.get_default();
|
let windowTracker = Shell.WindowTracker.get_default();
|
||||||
|
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
|
||||||
|
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
|
||||||
|
: null;
|
||||||
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
|
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
|
||||||
global.screen, null);
|
global.screen, workspace);
|
||||||
|
|
||||||
// Construct the AppIcons, add to the popup
|
// Construct the AppIcons, add to the popup
|
||||||
for (let i = 0; i < apps.length; i++) {
|
for (let i = 0; i < apps.length; i++) {
|
||||||
@ -447,7 +450,9 @@ const AppSwitcher = new Lang.Class({
|
|||||||
appIcon.cachedWindows = allWindows.filter(function(w) {
|
appIcon.cachedWindows = allWindows.filter(function(w) {
|
||||||
return windowTracker.get_window_app (w) == appIcon.app;
|
return windowTracker.get_window_app (w) == appIcon.app;
|
||||||
});
|
});
|
||||||
this._addIcon(appIcon);
|
if (workspace == null || appIcon.cachedWindows.length > 0) {
|
||||||
|
this._addIcon(appIcon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._curApp = -1;
|
this._curApp = -1;
|
||||||
|
84
js/ui/animation.js
Normal file
84
js/ui/animation.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||||
|
|
||||||
|
const Animation = new Lang.Class({
|
||||||
|
Name: 'Animation',
|
||||||
|
|
||||||
|
_init: function(filename, width, height, speed) {
|
||||||
|
this.actor = new St.Bin();
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
this._speed = speed;
|
||||||
|
|
||||||
|
this._isLoaded = false;
|
||||||
|
this._isPlaying = false;
|
||||||
|
this._timeoutId = 0;
|
||||||
|
this._frame = 0;
|
||||||
|
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||||
|
Lang.bind(this, this._animationsLoaded));
|
||||||
|
this.actor.set_child(this._animations);
|
||||||
|
},
|
||||||
|
|
||||||
|
play: function() {
|
||||||
|
if (this._isLoaded && this._timeoutId == 0) {
|
||||||
|
if (this._frame == 0)
|
||||||
|
this._showFrame(0);
|
||||||
|
|
||||||
|
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isPlaying = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function() {
|
||||||
|
if (this._timeoutId > 0) {
|
||||||
|
Mainloop.source_remove(this._timeoutId);
|
||||||
|
this._timeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isPlaying = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_showFrame: function(frame) {
|
||||||
|
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
||||||
|
if (oldFrameActor)
|
||||||
|
oldFrameActor.hide();
|
||||||
|
|
||||||
|
this._frame = (frame % this._animations.get_n_children());
|
||||||
|
|
||||||
|
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
||||||
|
if (newFrameActor)
|
||||||
|
newFrameActor.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function() {
|
||||||
|
this._showFrame(this._frame + 1);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_animationsLoaded: function() {
|
||||||
|
this._isLoaded = true;
|
||||||
|
|
||||||
|
if (this._isPlaying)
|
||||||
|
this.play();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const AnimatedIcon = new Lang.Class({
|
||||||
|
Name: 'AnimatedIcon',
|
||||||
|
Extends: Animation,
|
||||||
|
|
||||||
|
_init: function(filename, size) {
|
||||||
|
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||||
|
}
|
||||||
|
});
|
@ -187,6 +187,9 @@ const AllView = new Lang.Class({
|
|||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent();
|
this.parent();
|
||||||
|
|
||||||
|
this._grid.actor.y_align = Clutter.ActorAlign.START;
|
||||||
|
this._grid.actor.y_expand = true;
|
||||||
|
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
|
this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
|
||||||
this._stack.add_actor(this._grid.actor);
|
this._stack.add_actor(this._grid.actor);
|
||||||
@ -276,8 +279,12 @@ const AllView = new Lang.Class({
|
|||||||
this._eventBlocker.reactive = isOpen;
|
this._eventBlocker.reactive = isOpen;
|
||||||
this._currentPopup = isOpen ? popup : null;
|
this._currentPopup = isOpen ? popup : null;
|
||||||
this._updateIconOpacities(isOpen);
|
this._updateIconOpacities(isOpen);
|
||||||
if (isOpen)
|
if (isOpen) {
|
||||||
this._ensureIconVisible(popup.actor);
|
this._ensureIconVisible(popup.actor);
|
||||||
|
this._grid.actor.y = popup.parentOffset;
|
||||||
|
} else {
|
||||||
|
this._grid.actor.y = 0;
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -316,6 +323,8 @@ const FrequentView = new Lang.Class({
|
|||||||
loadApps: function() {
|
loadApps: function() {
|
||||||
let mostUsed = this._usage.get_most_used ("");
|
let mostUsed = this._usage.get_most_used ("");
|
||||||
for (let i = 0; i < mostUsed.length; i++) {
|
for (let i = 0; i < mostUsed.length; i++) {
|
||||||
|
if (!mostUsed[i].get_app_info().should_show())
|
||||||
|
continue;
|
||||||
let appIcon = new AppIcon(mostUsed[i]);
|
let appIcon = new AppIcon(mostUsed[i]);
|
||||||
this._grid.addItem(appIcon.actor, -1);
|
this._grid.addItem(appIcon.actor, -1);
|
||||||
}
|
}
|
||||||
@ -327,6 +336,31 @@ const Views = {
|
|||||||
ALL: 1
|
ALL: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ControlsBoxLayout = Lang.Class({
|
||||||
|
Name: 'ControlsBoxLayout',
|
||||||
|
Extends: Clutter.BoxLayout,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the BoxLayout behavior to use the maximum preferred width of all
|
||||||
|
* buttons for each child
|
||||||
|
*/
|
||||||
|
vfunc_get_preferred_width: function(container, forHeight) {
|
||||||
|
let maxMinWidth = 0;
|
||||||
|
let maxNaturalWidth = 0;
|
||||||
|
for (let child = container.get_first_child();
|
||||||
|
child;
|
||||||
|
child = child.get_next_sibling()) {
|
||||||
|
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
|
||||||
|
maxMinWidth = Math.max(maxMinWidth, minWidth);
|
||||||
|
maxNaturalWidth = Math.max(maxNaturalWidth, natWidth);
|
||||||
|
}
|
||||||
|
let childrenCount = container.get_n_children();
|
||||||
|
let totalSpacing = this.spacing * (childrenCount - 1);
|
||||||
|
return [maxMinWidth * childrenCount + totalSpacing,
|
||||||
|
maxNaturalWidth * childrenCount + totalSpacing];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const AppDisplay = new Lang.Class({
|
const AppDisplay = new Lang.Class({
|
||||||
Name: 'AppDisplay',
|
Name: 'AppDisplay',
|
||||||
|
|
||||||
@ -341,6 +375,9 @@ const AppDisplay = new Lang.Class({
|
|||||||
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
|
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
|
||||||
Main.queueDeferredWork(this._allAppsWorkId);
|
Main.queueDeferredWork(this._allAppsWorkId);
|
||||||
}));
|
}));
|
||||||
|
this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
|
||||||
|
this._privacySettings.connect('changed::remember-app-usage',
|
||||||
|
Lang.bind(this, this._updateFrequentVisibility));
|
||||||
|
|
||||||
this._views = [];
|
this._views = [];
|
||||||
|
|
||||||
@ -367,9 +404,10 @@ const AppDisplay = new Lang.Class({
|
|||||||
x_expand: true, y_expand: true });
|
x_expand: true, y_expand: true });
|
||||||
this.actor.add(this._viewStack, { expand: true });
|
this.actor.add(this._viewStack, { expand: true });
|
||||||
|
|
||||||
let layout = new Clutter.BoxLayout({ homogeneous: true });
|
let layout = new ControlsBoxLayout({ homogeneous: true });
|
||||||
this._controls = new St.Widget({ style_class: 'app-view-controls',
|
this._controls = new St.Widget({ style_class: 'app-view-controls',
|
||||||
layout_manager: layout });
|
layout_manager: layout });
|
||||||
|
layout.hookup_style(this._controls);
|
||||||
this.actor.add(new St.Bin({ child: this._controls }));
|
this.actor.add(new St.Bin({ child: this._controls }));
|
||||||
|
|
||||||
|
|
||||||
@ -384,6 +422,7 @@ const AppDisplay = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
this._showView(Views.FREQUENT);
|
this._showView(Views.FREQUENT);
|
||||||
|
this._updateFrequentVisibility();
|
||||||
|
|
||||||
// We need a dummy actor to catch the keyboard focus if the
|
// We need a dummy actor to catch the keyboard focus if the
|
||||||
// user Ctrl-Alt-Tabs here before the deferred work creates
|
// user Ctrl-Alt-Tabs here before the deferred work creates
|
||||||
@ -413,6 +452,19 @@ const AppDisplay = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateFrequentVisibility: function() {
|
||||||
|
let enabled = this._privacySettings.get_boolean('remember-app-usage');
|
||||||
|
this._views[Views.FREQUENT].control.visible = enabled;
|
||||||
|
|
||||||
|
let visibleViews = this._views.filter(function(v) {
|
||||||
|
return v.control.visible;
|
||||||
|
});
|
||||||
|
this._controls.visible = visibleViews.length > 1;
|
||||||
|
|
||||||
|
if (!enabled && this._views[Views.FREQUENT].view.actor.visible)
|
||||||
|
this._showView(Views.ALL);
|
||||||
|
},
|
||||||
|
|
||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
this._redisplayFrequentApps();
|
this._redisplayFrequentApps();
|
||||||
this._redisplayAllApps();
|
this._redisplayAllApps();
|
||||||
@ -483,11 +535,11 @@ const AppSearchProvider = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
|
this.searchSystem.setResults(this, this._appSys.initial_search(terms));
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
|
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms));
|
||||||
},
|
},
|
||||||
|
|
||||||
activateResult: function(app) {
|
activateResult: function(app) {
|
||||||
@ -572,7 +624,11 @@ const FolderIcon = new Lang.Class({
|
|||||||
// Position the popup above or below the source icon
|
// Position the popup above or below the source icon
|
||||||
if (side == St.Side.BOTTOM) {
|
if (side == St.Side.BOTTOM) {
|
||||||
this._popup.actor.show();
|
this._popup.actor.show();
|
||||||
this._popup.actor.y = this.actor.y - this._popup.actor.height;
|
let closeButtonOffset = -this._popup.closeButton.translation_y;
|
||||||
|
let y = this.actor.y - this._popup.actor.height;
|
||||||
|
let yWithButton = y - closeButtonOffset;
|
||||||
|
this._popup.parentOffset = yWithButton < 0 ? -yWithButton : 0;
|
||||||
|
this._popup.actor.y = Math.max(y, closeButtonOffset);
|
||||||
this._popup.actor.hide();
|
this._popup.actor.hide();
|
||||||
} else {
|
} else {
|
||||||
this._popup.actor.y = this.actor.y + this.actor.height;
|
this._popup.actor.y = this.actor.y + this.actor.height;
|
||||||
@ -595,6 +651,7 @@ const AppFolderPopup = new Lang.Class({
|
|||||||
this._arrowSide = side;
|
this._arrowSide = side;
|
||||||
|
|
||||||
this._isOpen = false;
|
this._isOpen = false;
|
||||||
|
this.parentOffset = 0;
|
||||||
|
|
||||||
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||||
visible: false,
|
visible: false,
|
||||||
@ -618,17 +675,31 @@ const AppFolderPopup = new Lang.Class({
|
|||||||
this.actor.add_actor(this._boxPointer.actor);
|
this.actor.add_actor(this._boxPointer.actor);
|
||||||
this._boxPointer.bin.set_child(this._view.actor);
|
this._boxPointer.bin.set_child(this._view.actor);
|
||||||
|
|
||||||
let closeButton = Util.makeCloseButton();
|
this.closeButton = Util.makeCloseButton();
|
||||||
closeButton.connect('clicked', Lang.bind(this, this.popdown));
|
this.closeButton.connect('clicked', Lang.bind(this, this.popdown));
|
||||||
this.actor.add_actor(closeButton);
|
this.actor.add_actor(this.closeButton);
|
||||||
|
|
||||||
this._boxPointer.actor.bind_property('opacity', closeButton, 'opacity',
|
this._boxPointer.actor.bind_property('opacity', this.closeButton, 'opacity',
|
||||||
GObject.BindingFlags.SYNC_CREATE);
|
GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
|
global.focus_manager.add_group(this.actor);
|
||||||
|
|
||||||
source.actor.connect('destroy', Lang.bind(this,
|
source.actor.connect('destroy', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
}));
|
}));
|
||||||
|
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyPress: function(actor, event) {
|
||||||
|
if (!this._isOpen)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (event.get_key_symbol() != Clutter.KEY_Escape)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.popdown();
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
@ -643,6 +714,7 @@ const AppFolderPopup = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
|
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
|
|
||||||
this._boxPointer.setArrowActor(this._source.actor);
|
this._boxPointer.setArrowActor(this._source.actor);
|
||||||
this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
|
this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
|
||||||
|
@ -142,23 +142,20 @@ const BackgroundCache = new Lang.Class({
|
|||||||
cancellable: null,
|
cancellable: null,
|
||||||
onFinished: null });
|
onFinished: null });
|
||||||
|
|
||||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
let fileLoad = { filename: params.filename,
|
||||||
if (this._pendingFileLoads[i].filename == params.filename &&
|
style: params.style,
|
||||||
this._pendingFileLoads[i].style == params.style) {
|
shouldCopy: false,
|
||||||
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
|
monitorIndex: params.monitorIndex,
|
||||||
monitorIndex: params.monitorIndex,
|
effects: params.effects,
|
||||||
effects: params.effects,
|
onFinished: params.onFinished,
|
||||||
onFinished: params.onFinished });
|
cancellable: new Gio.Cancellable(), };
|
||||||
return;
|
this._pendingFileLoads.push(fileLoad);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._pendingFileLoads.push({ filename: params.filename,
|
if (params.cancellable) {
|
||||||
style: params.style,
|
params.cancellable.connect(Lang.bind(this, function(c) {
|
||||||
callers: [{ shouldCopy: false,
|
fileLoad.cancellable.cancel();
|
||||||
monitorIndex: params.monitorIndex,
|
}));
|
||||||
effects: params.effects,
|
}
|
||||||
onFinished: params.onFinished }] });
|
|
||||||
|
|
||||||
let content = new Meta.Background({ meta_screen: global.screen,
|
let content = new Meta.Background({ meta_screen: global.screen,
|
||||||
monitor: params.monitorIndex,
|
monitor: params.monitorIndex,
|
||||||
@ -166,9 +163,19 @@ const BackgroundCache = new Lang.Class({
|
|||||||
|
|
||||||
content.load_file_async(params.filename,
|
content.load_file_async(params.filename,
|
||||||
params.style,
|
params.style,
|
||||||
params.cancellable,
|
fileLoad.cancellable,
|
||||||
Lang.bind(this,
|
Lang.bind(this,
|
||||||
function(object, result) {
|
function(object, result) {
|
||||||
|
if (fileLoad.cancellable.is_cancelled()) {
|
||||||
|
if (params.cancellable && params.cancellable.is_cancelled()) {
|
||||||
|
if (params.onFinished)
|
||||||
|
params.onFinished(null);
|
||||||
|
this._removePendingFileLoad(fileLoad);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
content.load_file_finish(result);
|
content.load_file_finish(result);
|
||||||
|
|
||||||
@ -178,22 +185,25 @@ const BackgroundCache = new Lang.Class({
|
|||||||
content = null;
|
content = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let needsCopy = false;
|
||||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||||
let pendingLoad = this._pendingFileLoads[i];
|
let pendingLoad = this._pendingFileLoads[i];
|
||||||
if (pendingLoad.filename != params.filename ||
|
if (pendingLoad.filename != params.filename ||
|
||||||
pendingLoad.style != params.style)
|
pendingLoad.style != params.style)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (let j = 0; j < pendingLoad.callers.length; j++) {
|
if (pendingLoad.cancellable.is_cancelled())
|
||||||
if (pendingLoad.callers[j].onFinished) {
|
continue;
|
||||||
if (content && pendingLoad.callers[j].shouldCopy) {
|
|
||||||
content = object.copy(pendingLoad.callers[j].monitorIndex,
|
|
||||||
pendingLoad.callers[j].effects);
|
|
||||||
|
|
||||||
}
|
pendingLoad.cancellable.cancel();
|
||||||
|
if (pendingLoad.onFinished) {
|
||||||
pendingLoad.callers[j].onFinished(content);
|
if (content && needsCopy) {
|
||||||
|
content = object.copy(pendingLoad.monitorIndex,
|
||||||
|
pendingLoad.effects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
needsCopy = true;
|
||||||
|
pendingLoad.onFinished(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._pendingFileLoads.splice(i, 1);
|
this._pendingFileLoads.splice(i, 1);
|
||||||
@ -201,6 +211,15 @@ const BackgroundCache = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_removePendingFileLoad: function(fileLoad) {
|
||||||
|
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||||
|
if (this._pendingFileLoads[i].cancellable == fileLoad.cancellable) {
|
||||||
|
this._pendingFileLoads.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getImageContent: function(params) {
|
getImageContent: function(params) {
|
||||||
params = Params.parse(params, { monitorIndex: 0,
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
style: null,
|
style: null,
|
||||||
@ -571,7 +590,16 @@ const Background = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uri = this._settings.get_string(PICTURE_URI_KEY);
|
let uri = this._settings.get_string(PICTURE_URI_KEY);
|
||||||
let filename = Gio.File.new_for_uri(uri).get_path();
|
let filename;
|
||||||
|
if (GLib.uri_parse_scheme(uri) != null)
|
||||||
|
filename = Gio.File.new_for_uri(uri).get_path();
|
||||||
|
else
|
||||||
|
filename = uri;
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._loadFile(filename);
|
this._loadFile(filename);
|
||||||
},
|
},
|
||||||
|
@ -46,8 +46,10 @@ function addBackgroundMenu(actor) {
|
|||||||
clickAction.connect('long-press', function(action, actor, state) {
|
clickAction.connect('long-press', function(action, actor, state) {
|
||||||
if (state == Clutter.LongPressState.QUERY)
|
if (state == Clutter.LongPressState.QUERY)
|
||||||
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
|
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
|
||||||
if (state == Clutter.LongPressState.ACTIVATE)
|
if (state == Clutter.LongPressState.ACTIVATE) {
|
||||||
openMenu();
|
openMenu();
|
||||||
|
actor._backgroundManager.ignoreRelease();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
clickAction.connect('clicked', function(action) {
|
clickAction.connect('clicked', function(action) {
|
||||||
|
@ -589,12 +589,12 @@ const BoxPointer = new Lang.Class({
|
|||||||
return St.Side.TOP;
|
return St.Side.TOP;
|
||||||
break;
|
break;
|
||||||
case St.Side.LEFT:
|
case St.Side.LEFT:
|
||||||
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
|
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
|
||||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||||
return St.Side.RIGHT;
|
return St.Side.RIGHT;
|
||||||
break;
|
break;
|
||||||
case St.Side.RIGHT:
|
case St.Side.RIGHT:
|
||||||
if (sourceAllocation.y1 - boxWidth < monitor.x &&
|
if (sourceAllocation.x1 - boxWidth < monitor.x &&
|
||||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||||
return St.Side.LEFT;
|
return St.Side.LEFT;
|
||||||
break;
|
break;
|
||||||
|
@ -443,16 +443,18 @@ const Calendar = new Lang.Class({
|
|||||||
this.actor.add(this._topBox,
|
this.actor.add(this._topBox,
|
||||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||||
|
|
||||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
|
||||||
this._topBox.add(back);
|
can_focus: true });
|
||||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
this._topBox.add(this._backButton);
|
||||||
|
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||||
|
|
||||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
||||||
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
|
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
|
||||||
this._topBox.add(forward);
|
can_focus: true });
|
||||||
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
this._topBox.add(this._forwardButton);
|
||||||
|
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
||||||
|
|
||||||
// Add weekday labels...
|
// Add weekday labels...
|
||||||
//
|
//
|
||||||
@ -511,10 +513,12 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDate(newDate, false);
|
this._backButton.grab_key_focus();
|
||||||
},
|
|
||||||
|
|
||||||
_onNextMonthButtonClicked: function() {
|
this.setDate(newDate, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNextMonthButtonClicked: function() {
|
||||||
let newDate = new Date(this._selectedDate);
|
let newDate = new Date(this._selectedDate);
|
||||||
let oldMonth = newDate.getMonth();
|
let oldMonth = newDate.getMonth();
|
||||||
if (oldMonth == 11) {
|
if (oldMonth == 11) {
|
||||||
@ -533,7 +537,9 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDate(newDate, false);
|
this._forwardButton.grab_key_focus();
|
||||||
|
|
||||||
|
this.setDate(newDate, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSettingsChange: function() {
|
_onSettingsChange: function() {
|
||||||
@ -590,7 +596,8 @@ const Calendar = new Lang.Class({
|
|||||||
// nRows here means 6 weeks + one header + one navbar
|
// nRows here means 6 weeks + one header + one navbar
|
||||||
let nRows = 8;
|
let nRows = 8;
|
||||||
while (row < 8) {
|
while (row < 8) {
|
||||||
let button = new St.Button({ label: iter.getDate().toString() });
|
let button = new St.Button({ label: iter.getDate().toString(),
|
||||||
|
can_focus: true });
|
||||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||||
|
|
||||||
if (this._eventSource.isDummy)
|
if (this._eventSource.isDummy)
|
||||||
@ -598,8 +605,12 @@ const Calendar = new Lang.Class({
|
|||||||
|
|
||||||
let iterStr = iter.toUTCString();
|
let iterStr = iter.toUTCString();
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this._shouldDateGrabFocus = true;
|
||||||
|
|
||||||
let newlySelectedDate = new Date(iterStr);
|
let newlySelectedDate = new Date(iterStr);
|
||||||
this.setDate(newlySelectedDate, false);
|
this.setDate(newlySelectedDate, false);
|
||||||
|
|
||||||
|
this._shouldDateGrabFocus = false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let hasEvents = this._eventSource.hasEvents(iter);
|
let hasEvents = this._eventSource.hasEvents(iter);
|
||||||
@ -624,9 +635,6 @@ const Calendar = new Lang.Class({
|
|||||||
else if (iter.getMonth() != this._selectedDate.getMonth())
|
else if (iter.getMonth() != this._selectedDate.getMonth())
|
||||||
styleClass += ' calendar-other-month-day';
|
styleClass += ' calendar-other-month-day';
|
||||||
|
|
||||||
if (_sameDay(this._selectedDate, iter))
|
|
||||||
button.add_style_pseudo_class('active');
|
|
||||||
|
|
||||||
if (hasEvents)
|
if (hasEvents)
|
||||||
styleClass += ' calendar-day-with-events'
|
styleClass += ' calendar-day-with-events'
|
||||||
|
|
||||||
@ -636,6 +644,13 @@ const Calendar = new Lang.Class({
|
|||||||
this.actor.add(button,
|
this.actor.add(button,
|
||||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
||||||
|
|
||||||
|
if (_sameDay(this._selectedDate, iter)) {
|
||||||
|
button.add_style_pseudo_class('active');
|
||||||
|
|
||||||
|
if (this._shouldDateGrabFocus)
|
||||||
|
button.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
if (this._useWeekdate && iter.getDay() == 4) {
|
if (this._useWeekdate && iter.getDay() == 4) {
|
||||||
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
||||||
style_class: 'calendar-day-base calendar-week-number'});
|
style_class: 'calendar-day-base calendar-week-number'});
|
||||||
@ -660,7 +675,7 @@ const EventsList = new Lang.Class({
|
|||||||
Name: 'EventsList',
|
Name: 'EventsList',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
this.actor = new St.Table({ style_class: 'events-table' });
|
||||||
this._date = new Date();
|
this._date = new Date();
|
||||||
this._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));
|
||||||
@ -672,55 +687,72 @@ const EventsList = new Lang.Class({
|
|||||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||||
},
|
},
|
||||||
|
|
||||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
_addEvent: function(event, index, includeDayName) {
|
||||||
if (includeDayName) {
|
let dayString;
|
||||||
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
|
if (includeDayName)
|
||||||
text: day } ),
|
dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||||
{ x_fill: true } );
|
else
|
||||||
}
|
dayString = '';
|
||||||
timeBox.add(new St.Label( { style_class: 'events-day-time',
|
|
||||||
text: time} ),
|
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
|
||||||
{ x_fill: true } );
|
text: dayString });
|
||||||
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
|
dayLabel.clutter_text.line_wrap = false;
|
||||||
text: desc} ));
|
dayLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(dayLabel, { row: index, col: 0,
|
||||||
|
x_expand: false, x_align: St.Align.END,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
|
|
||||||
|
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||||
|
let timeString = _formatEventTime(event, clockFormat);
|
||||||
|
let timeLabel = new St.Label({ style_class: 'events-day-time',
|
||||||
|
text: timeString });
|
||||||
|
timeLabel.clutter_text.line_wrap = false;
|
||||||
|
timeLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(timeLabel, { row: index, col: 1,
|
||||||
|
x_expand: false, x_align: St.Align.MIDDLE,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
|
|
||||||
|
let titleLabel = new St.Label({ style_class: 'events-day-task',
|
||||||
|
text: event.summary });
|
||||||
|
titleLabel.clutter_text.line_wrap = true;
|
||||||
|
titleLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(titleLabel, { row: index, col: 2,
|
||||||
|
x_expand: true, x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
},
|
},
|
||||||
|
|
||||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
|
||||||
let events = this._eventSource.getEvents(begin, end);
|
let events = this._eventSource.getEvents(begin, end);
|
||||||
|
|
||||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
|
||||||
|
|
||||||
if (events.length == 0 && !showNothingScheduled)
|
if (events.length == 0 && !showNothingScheduled)
|
||||||
return;
|
return index;
|
||||||
|
|
||||||
let vbox = new St.BoxLayout( {vertical: true} );
|
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
|
||||||
this.actor.add(vbox);
|
{ row: index, col: 0, col_span: 3,
|
||||||
|
// In theory, x_expand should be true here, but x_expand
|
||||||
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
|
// is a property of the column for StTable, ie all day cells
|
||||||
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
|
// get it too
|
||||||
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
|
x_expand: false, x_align: St.Align.START,
|
||||||
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
|
y_fill: false, y_align: St.Align.START });
|
||||||
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
|
index++;
|
||||||
box.add(dayNameBox, {x_fill: false});
|
|
||||||
box.add(timeBox, {x_fill: false});
|
|
||||||
box.add(eventTitleBox, {expand: true});
|
|
||||||
vbox.add(box);
|
|
||||||
|
|
||||||
for (let n = 0; n < events.length; n++) {
|
for (let n = 0; n < events.length; n++) {
|
||||||
let event = events[n];
|
this._addEvent(events[n], index, includeDayName);
|
||||||
let dayString = _getEventDayAbbreviation(event.date.getDay());
|
index++;
|
||||||
let timeString = _formatEventTime(event, clockFormat);
|
|
||||||
let summaryString = event.summary;
|
|
||||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events.length == 0 && showNothingScheduled) {
|
if (events.length == 0 && showNothingScheduled) {
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
/* Translators: Text to show if there are no events */
|
/* Translators: Text to show if there are no events */
|
||||||
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
||||||
let timeString = _formatEventTime(nothingEvent, clockFormat);
|
this._addEvent(nothingEvent, index, false);
|
||||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
},
|
},
|
||||||
|
|
||||||
_showOtherDay: function(day) {
|
_showOtherDay: function(day) {
|
||||||
@ -737,20 +769,21 @@ const EventsList = new Lang.Class({
|
|||||||
else
|
else
|
||||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
||||||
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
|
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_showToday: function() {
|
_showToday: function() {
|
||||||
this.actor.destroy_all_children();
|
this.actor.destroy_all_children();
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
let dayBegin = _getBeginningOfDay(now);
|
let dayBegin = _getBeginningOfDay(now);
|
||||||
let dayEnd = _getEndOfDay(now);
|
let dayEnd = _getEndOfDay(now);
|
||||||
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
|
index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
|
||||||
|
|
||||||
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
||||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
|
||||||
|
|
||||||
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
||||||
|
|
||||||
@ -761,7 +794,7 @@ const EventsList = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
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 - dayInWeek) * 86400 * 1000);
|
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
|
||||||
} else {
|
} else {
|
||||||
/* otherwise it's one of the two last days of the week ... show
|
/* otherwise it's one of the two last days of the week ... show
|
||||||
* "Next week" and include events up until and including *next*
|
* "Next week" and include events up until and including *next*
|
||||||
@ -769,7 +802,7 @@ const EventsList = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
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 - dayInWeek) * 86400 * 1000);
|
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
|
|||||||
if (!volume || (!volume.allowAutorun && forTransient))
|
if (!volume || (!volume.allowAutorun && forTransient))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!root.is_native() || isMountRootHidden(root))
|
if (root.is_native() && isMountRootHidden(root))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -25,7 +25,7 @@ const KeyringDialog = new Lang.Class({
|
|||||||
this.prompt = new Shell.KeyringPrompt();
|
this.prompt = new Shell.KeyringPrompt();
|
||||||
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
||||||
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
||||||
this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
|
this.prompt.connect('prompt-close', Lang.bind(this, this._onHidePrompt));
|
||||||
|
|
||||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
@ -63,11 +63,17 @@ const KeyringDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._cancelButton = this.addButton({ label: '',
|
this._cancelButton = this.addButton({ label: '',
|
||||||
action: Lang.bind(this, this._onCancelButton),
|
action: Lang.bind(this, this._onCancelButton),
|
||||||
key: Clutter.Escape });
|
key: Clutter.Escape },
|
||||||
|
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
this.placeSpinner({ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
this._continueButton = this.addButton({ label: '',
|
this._continueButton = this.addButton({ label: '',
|
||||||
action: Lang.bind(this, this._onContinueButton),
|
action: Lang.bind(this, this._onContinueButton),
|
||||||
default: true },
|
default: true },
|
||||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||||
|
|
||||||
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||||
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||||
@ -143,11 +149,19 @@ const KeyringDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateSensitivity: function(sensitive) {
|
_updateSensitivity: function(sensitive) {
|
||||||
this._passwordEntry.reactive = sensitive;
|
if (this._passwordEntry) {
|
||||||
this._passwordEntry.clutter_text.editable = sensitive;
|
this._passwordEntry.reactive = sensitive;
|
||||||
|
this._passwordEntry.clutter_text.editable = sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._confirmEntry) {
|
||||||
|
this._confirmEntry.reactive = sensitive;
|
||||||
|
this._confirmEntry.clutter_text.editable = sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
this._continueButton.can_focus = sensitive;
|
this._continueButton.can_focus = sensitive;
|
||||||
this._continueButton.reactive = sensitive;
|
this._continueButton.reactive = sensitive;
|
||||||
|
this.setWorking(!sensitive);
|
||||||
},
|
},
|
||||||
|
|
||||||
_ensureOpen: function() {
|
_ensureOpen: function() {
|
||||||
|
@ -16,7 +16,7 @@ const PolkitAgent = imports.gi.PolkitAgent;
|
|||||||
const Components = imports.ui.components;
|
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 UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
const DIALOG_ICON_SIZE = 48;
|
const DIALOG_ICON_SIZE = 48;
|
||||||
|
|
||||||
@ -31,7 +31,6 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
this.userNames = userNames;
|
this.userNames = userNames;
|
||||||
this._wasDismissed = false;
|
this._wasDismissed = false;
|
||||||
this._completed = false;
|
|
||||||
|
|
||||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
@ -101,9 +100,9 @@ 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._userAvatar = new UserMenu.UserAvatarWidget(this._user,
|
this._userAvatar = new UserWidget.Avatar(this._user,
|
||||||
{ iconSize: DIALOG_ICON_SIZE,
|
{ iconSize: DIALOG_ICON_SIZE,
|
||||||
styleClass: 'polkit-dialog-user-icon' });
|
styleClass: 'polkit-dialog-user-icon' });
|
||||||
this._userAvatar.actor.hide();
|
this._userAvatar.actor.hide();
|
||||||
userBox.add(this._userAvatar.actor,
|
userBox.add(this._userAvatar.actor,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
@ -161,26 +160,32 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._cancelButton = this.addButton({ label: _("Cancel"),
|
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||||
action: Lang.bind(this, this.cancel),
|
action: Lang.bind(this, this.cancel),
|
||||||
key: Clutter.Escape });
|
key: Clutter.Escape },
|
||||||
|
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
this.placeSpinner({ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
this._okButton = this.addButton({ label: _("Authenticate"),
|
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||||
default: true },
|
default: true },
|
||||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||||
|
|
||||||
this._doneEmitted = false;
|
this._doneEmitted = false;
|
||||||
|
|
||||||
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
||||||
this._cookie = cookie;
|
this._cookie = cookie;
|
||||||
|
},
|
||||||
|
|
||||||
|
performAuthentication: function() {
|
||||||
|
this.destroySession();
|
||||||
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||||
cookie: this._cookie });
|
cookie: this._cookie });
|
||||||
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
||||||
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
||||||
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
||||||
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
||||||
},
|
|
||||||
|
|
||||||
startAuthentication: function() {
|
|
||||||
this._session.initiate();
|
this._session.initiate();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -202,14 +207,14 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
||||||
' Dismissing authentication request for action-id ' + this.actionId +
|
' Dismissing authentication request for action-id ' + this.actionId +
|
||||||
' cookie ' + this._cookie);
|
' cookie ' + this._cookie);
|
||||||
this._emitDone(false, true);
|
this._emitDone(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_emitDone: function(keepVisible, dismissed) {
|
_emitDone: function(dismissed) {
|
||||||
if (!this._doneEmitted) {
|
if (!this._doneEmitted) {
|
||||||
this._doneEmitted = true;
|
this._doneEmitted = true;
|
||||||
this.emit('done', keepVisible, dismissed);
|
this.emit('done', dismissed);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -219,6 +224,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._okButton.can_focus = sensitive;
|
this._okButton.can_focus = sensitive;
|
||||||
this._okButton.reactive = sensitive;
|
this._okButton.reactive = sensitive;
|
||||||
|
this.setWorking(!sensitive);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEntryActivate: function() {
|
_onEntryActivate: function() {
|
||||||
@ -237,12 +243,16 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onSessionCompleted: function(session, gainedAuthorization) {
|
_onSessionCompleted: function(session, gainedAuthorization) {
|
||||||
if (this._completed)
|
if (this._completed || this._doneEmitted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._completed = true;
|
this._completed = true;
|
||||||
|
|
||||||
if (!gainedAuthorization) {
|
/* Yay, all done */
|
||||||
|
if (gainedAuthorization) {
|
||||||
|
this._emitDone(false);
|
||||||
|
|
||||||
|
} else {
|
||||||
/* Unless we are showing an existing error message from the PAM
|
/* Unless we are showing an existing error message from the PAM
|
||||||
* module (the PAM module could be reporting the authentication
|
* module (the PAM module could be reporting the authentication
|
||||||
* error providing authentication-method specific information),
|
* error providing authentication-method specific information),
|
||||||
@ -258,8 +268,10 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._infoMessageLabel.hide();
|
this._infoMessageLabel.hide();
|
||||||
this._nullMessageLabel.hide();
|
this._nullMessageLabel.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try and authenticate again */
|
||||||
|
this.performAuthentication();
|
||||||
}
|
}
|
||||||
this._emitDone(!gainedAuthorization, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSessionRequest: function(session, request, echo_on) {
|
_onSessionRequest: function(session, request, echo_on) {
|
||||||
@ -303,6 +315,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
if (this._session) {
|
if (this._session) {
|
||||||
if (!this._completed)
|
if (!this._completed)
|
||||||
this._session.cancel();
|
this._session.cancel();
|
||||||
|
this._completed = false;
|
||||||
this._session = null;
|
this._session = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -317,7 +330,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
cancel: function() {
|
cancel: function() {
|
||||||
this._wasDismissed = true;
|
this._wasDismissed = true;
|
||||||
this.close(global.get_current_time());
|
this.close(global.get_current_time());
|
||||||
this._emitDone(false, true);
|
this._emitDone(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
||||||
@ -327,7 +340,6 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._currentDialog = null;
|
this._currentDialog = null;
|
||||||
this._isCompleting = false;
|
|
||||||
this._handle = null;
|
this._handle = null;
|
||||||
this._native = new Shell.PolkitAuthenticationAgent();
|
this._native = new Shell.PolkitAuthenticationAgent();
|
||||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||||
@ -364,45 +376,24 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
// discussion.
|
// discussion.
|
||||||
|
|
||||||
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
||||||
this._currentDialog.startAuthentication();
|
this._currentDialog.performAuthentication();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancel: function(nativeAgent) {
|
_onCancel: function(nativeAgent) {
|
||||||
this._completeRequest(false, false);
|
this._completeRequest(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDialogDone: function(dialog, keepVisible, dismissed) {
|
_onDialogDone: function(dialog, dismissed) {
|
||||||
this._completeRequest(keepVisible, dismissed);
|
this._completeRequest(dismissed);
|
||||||
},
|
},
|
||||||
|
|
||||||
_reallyCompleteRequest: function(dismissed) {
|
_completeRequest: function(dismissed) {
|
||||||
this._currentDialog.close();
|
this._currentDialog.close();
|
||||||
this._currentDialog.destroySession();
|
this._currentDialog.destroySession();
|
||||||
this._currentDialog = null;
|
this._currentDialog = null;
|
||||||
this._isCompleting = false;
|
|
||||||
|
|
||||||
this._native.complete(dismissed)
|
this._native.complete(dismissed);
|
||||||
},
|
},
|
||||||
|
|
||||||
_completeRequest: function(keepVisible, wasDismissed) {
|
|
||||||
if (this._isCompleting)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._isCompleting = true;
|
|
||||||
|
|
||||||
if (keepVisible) {
|
|
||||||
// Give the user 2 seconds to read 'Authentication Failure' before
|
|
||||||
// dismissing the dialog
|
|
||||||
Mainloop.timeout_add(2000,
|
|
||||||
Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._reallyCompleteRequest(wasDismissed);
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._reallyCompleteRequest(wasDismissed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Component = AuthenticationAgent;
|
const Component = AuthenticationAgent;
|
||||||
|
@ -18,7 +18,7 @@ const Params = imports.misc.params;
|
|||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
// See Notification.appendMessage
|
// See Notification.appendMessage
|
||||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
|
||||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
||||||
const SCROLLBACK_RECENT_LENGTH = 20;
|
const SCROLLBACK_RECENT_LENGTH = 20;
|
||||||
const SCROLLBACK_IDLE_LENGTH = 5;
|
const SCROLLBACK_IDLE_LENGTH = 5;
|
||||||
@ -967,7 +967,8 @@ const ChatNotification = new Lang.Class({
|
|||||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||||
group: 'meta',
|
group: 'meta',
|
||||||
styles: ['chat-meta-message'],
|
styles: ['chat-meta-message'],
|
||||||
childProps: { expand: true, x_fill: false },
|
childProps: { expand: true, x_fill: false,
|
||||||
|
x_align: St.Align.END },
|
||||||
noTimestamp: true,
|
noTimestamp: true,
|
||||||
timestamp: lastMessageTime });
|
timestamp: lastMessageTime });
|
||||||
|
|
||||||
|
@ -58,15 +58,10 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
focusGroup: function(item, timestamp) {
|
focusGroup: function(item, timestamp) {
|
||||||
if (item.focusCallback) {
|
if (item.focusCallback)
|
||||||
item.focusCallback(timestamp);
|
item.focusCallback(timestamp);
|
||||||
} else {
|
else
|
||||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
|
||||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
|
||||||
|
|
||||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sort the items into a consistent order; panel first, tray last,
|
// Sort the items into a consistent order; panel first, tray last,
|
||||||
@ -89,19 +84,25 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
||||||
|
|
||||||
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
||||||
if (!Main.overview.visible) {
|
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
|
||||||
let screen = global.screen;
|
let screen = global.screen;
|
||||||
let display = screen.get_display();
|
let display = screen.get_display();
|
||||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||||
let windowTracker = Shell.WindowTracker.get_default();
|
let windowTracker = Shell.WindowTracker.get_default();
|
||||||
let textureCache = St.TextureCache.get_default();
|
let textureCache = St.TextureCache.get_default();
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
let icon;
|
let icon = null;
|
||||||
let app = windowTracker.get_window_app(windows[i]);
|
let iconName = null;
|
||||||
if (app)
|
if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
|
||||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
iconName = 'video-display-symbolic';
|
||||||
else
|
} else {
|
||||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
let app = windowTracker.get_window_app(windows[i]);
|
||||||
|
if (app)
|
||||||
|
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||||
|
else
|
||||||
|
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||||
|
}
|
||||||
|
|
||||||
items.push({ name: windows[i].title,
|
items.push({ name: windows[i].title,
|
||||||
proxy: windows[i].get_compositor_private(),
|
proxy: windows[i].get_compositor_private(),
|
||||||
focusCallback: Lang.bind(windows[i],
|
focusCallback: Lang.bind(windows[i],
|
||||||
@ -109,6 +110,7 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
Main.activateWindow(this, timestamp);
|
Main.activateWindow(this, timestamp);
|
||||||
}),
|
}),
|
||||||
iconActor: icon,
|
iconActor: icon,
|
||||||
|
iconName: iconName,
|
||||||
sortGroup: SortGroup.MIDDLE });
|
sortGroup: SortGroup.MIDDLE });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,8 +132,6 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_focusWindows: function(timestamp) {
|
_focusWindows: function(timestamp) {
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
|
||||||
global.stage.key_focus = null;
|
|
||||||
global.screen.focus_default_window(timestamp);
|
global.screen.focus_default_window(timestamp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -287,13 +287,7 @@ const ShowAppsIcon = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleDragOver: function(source, actor, x, y, time) {
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
let app = getAppFromSource(source);
|
if (!this._canRemoveApp(getAppFromSource(source)))
|
||||||
if (app == null)
|
|
||||||
return DND.DragMotionResult.NO_DROP;
|
|
||||||
|
|
||||||
let id = app.get_id();
|
|
||||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
|
||||||
if (!isFavorite)
|
|
||||||
return DND.DragMotionResult.NO_DROP;
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
return DND.DragMotionResult.MOVE_DROP;
|
return DND.DragMotionResult.MOVE_DROP;
|
||||||
@ -301,7 +295,7 @@ const ShowAppsIcon = new Lang.Class({
|
|||||||
|
|
||||||
acceptDrop: function(source, actor, x, y, time) {
|
acceptDrop: function(source, actor, x, y, time) {
|
||||||
let app = getAppFromSource(source);
|
let app = getAppFromSource(source);
|
||||||
if (app == null)
|
if (!this._canRemoveApp(app))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let id = app.get_id();
|
let id = app.get_id();
|
||||||
@ -326,6 +320,16 @@ const DragPlaceholderItem = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const EmptyDropTargetItem = new Lang.Class({
|
||||||
|
Name: 'EmptyDropTargetItem',
|
||||||
|
Extends: DashItemContainer,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent();
|
||||||
|
this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const DashActor = new Lang.Class({
|
const DashActor = new Lang.Class({
|
||||||
Name: 'DashActor',
|
Name: 'DashActor',
|
||||||
Extends: St.Widget,
|
Extends: St.Widget,
|
||||||
@ -441,6 +445,12 @@ const Dash = new Lang.Class({
|
|||||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||||
};
|
};
|
||||||
DND.addDragMonitor(this._dragMonitor);
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
|
|
||||||
|
if (this._box.get_n_children() == 0) {
|
||||||
|
this._emptyDropTarget = new EmptyDropTargetItem();
|
||||||
|
this._box.insert_child_at_index(this._emptyDropTarget, 0);
|
||||||
|
this._emptyDropTarget.show(true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragCancelled: function() {
|
_onDragCancelled: function() {
|
||||||
@ -457,6 +467,7 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
_endDrag: function() {
|
_endDrag: function() {
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
|
this._clearEmptyDropTarget();
|
||||||
this._showAppsIcon.setDragApp(null);
|
this._showAppsIcon.setDragApp(null);
|
||||||
DND.removeDragMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
},
|
},
|
||||||
@ -797,9 +808,21 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
_clearDragPlaceholder: function() {
|
_clearDragPlaceholder: function() {
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
|
this._animatingPlaceholdersCount++;
|
||||||
this._dragPlaceholder.animateOutAndDestroy();
|
this._dragPlaceholder.animateOutAndDestroy();
|
||||||
|
this._dragPlaceholder.connect('destroy',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._animatingPlaceholdersCount--;
|
||||||
|
}));
|
||||||
this._dragPlaceholder = null;
|
this._dragPlaceholder = null;
|
||||||
this._dragPlaceholderPos = -1;
|
}
|
||||||
|
this._dragPlaceholderPos = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearEmptyDropTarget: function() {
|
||||||
|
if (this._emptyDropTarget) {
|
||||||
|
this._emptyDropTarget.animateOutAndDestroy();
|
||||||
|
this._emptyDropTarget = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -827,23 +850,18 @@ const Dash = new Lang.Class({
|
|||||||
numChildren--;
|
numChildren--;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = Math.floor(y * numChildren / boxHeight);
|
let pos;
|
||||||
|
if (!this._emptyDropTarget)
|
||||||
|
pos = Math.floor(y * numChildren / boxHeight);
|
||||||
|
else
|
||||||
|
pos = 0; // always insert at the top when dash is empty
|
||||||
|
|
||||||
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
||||||
this._dragPlaceholderPos = pos;
|
this._dragPlaceholderPos = pos;
|
||||||
|
|
||||||
// Don't allow positioning before or after self
|
// Don't allow positioning before or after self
|
||||||
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
||||||
if (this._dragPlaceholder) {
|
this._clearDragPlaceholder();
|
||||||
this._dragPlaceholder.animateOutAndDestroy();
|
|
||||||
this._animatingPlaceholdersCount++;
|
|
||||||
this._dragPlaceholder.connect('destroy',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._animatingPlaceholdersCount--;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this._dragPlaceholder = null;
|
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,9 +886,9 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
// Remove the drag placeholder if we are not in the
|
// Remove the drag placeholder if we are not in the
|
||||||
// "favorites zone"
|
// "favorites zone"
|
||||||
if (pos > numFavorites && this._dragPlaceholder) {
|
if (pos > numFavorites)
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
}
|
|
||||||
if (!this._dragPlaceholder)
|
if (!this._dragPlaceholder)
|
||||||
return DND.DragMotionResult.NO_DROP;
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
|
@ -49,11 +49,6 @@ const DateMenuButton = new Lang.Class({
|
|||||||
menuAlignment = 1.0 - menuAlignment;
|
menuAlignment = 1.0 - menuAlignment;
|
||||||
this.parent(menuAlignment);
|
this.parent(menuAlignment);
|
||||||
|
|
||||||
// At this moment calendar menu is not keyboard navigable at
|
|
||||||
// all (so not accessible), so it doesn't make sense to set as
|
|
||||||
// role ATK_ROLE_MENU like other elements of the panel.
|
|
||||||
this.actor.accessible_role = Atk.Role.LABEL;
|
|
||||||
|
|
||||||
this._clockDisplay = new St.Label();
|
this._clockDisplay = new St.Label();
|
||||||
this.actor.add_actor(this._clockDisplay);
|
this.actor.add_actor(this._clockDisplay);
|
||||||
|
|
||||||
@ -85,27 +80,22 @@ const DateMenuButton = new Lang.Class({
|
|||||||
vbox.add(this._calendar.actor);
|
vbox.add(this._calendar.actor);
|
||||||
|
|
||||||
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
separator.setColumnWidths(1);
|
vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
|
||||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
|
||||||
|
|
||||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||||
this._openCalendarItem.actor.can_focus = false;
|
|
||||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
|
||||||
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
|
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
|
||||||
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
|
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
|
||||||
this._openClocksItem.actor.can_focus = false;
|
|
||||||
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
|
||||||
Shell.AppSystem.get_default().connect('installed-changed',
|
Shell.AppSystem.get_default().connect('installed-changed',
|
||||||
Lang.bind(this, this._appInstalledChanged));
|
Lang.bind(this, this._appInstalledChanged));
|
||||||
this._appInstalledChanged();
|
|
||||||
|
|
||||||
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
|
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
|
||||||
if (item) {
|
if (item) {
|
||||||
item.actor.show_on_set_parent = false;
|
item.actor.show_on_set_parent = false;
|
||||||
item.actor.can_focus = false;
|
|
||||||
item.actor.reparent(vbox);
|
item.actor.reparent(vbox);
|
||||||
this._dateAndTimeSeparator = separator;
|
this._dateAndTimeSeparator = separator;
|
||||||
}
|
}
|
||||||
@ -116,12 +106,7 @@ const DateMenuButton = new Lang.Class({
|
|||||||
hbox.add(this._separator);
|
hbox.add(this._separator);
|
||||||
|
|
||||||
// Fill up the second column
|
// Fill up the second column
|
||||||
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
|
hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||||
vertical: true });
|
|
||||||
hbox.add(vbox, { expand: true });
|
|
||||||
|
|
||||||
// Event list
|
|
||||||
vbox.add(this._eventList.actor, { expand: true });
|
|
||||||
|
|
||||||
// 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,24 +142,25 @@ const DateMenuButton = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_appInstalledChanged: function() {
|
_appInstalledChanged: function() {
|
||||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
this._calendarApp = undefined;
|
||||||
this._openClocksItem.actor.visible = app !== null;
|
this._updateEventsVisibility();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateEventsVisibility: function() {
|
_updateEventsVisibility: function() {
|
||||||
let visible = this._eventSource.hasCalendars;
|
let visible = this._eventSource.hasCalendars;
|
||||||
this._openCalendarItem.actor.visible = visible;
|
this._openCalendarItem.actor.visible = visible &&
|
||||||
this._openClocksItem.actor.visible = visible;
|
(this._getCalendarApp() != null);
|
||||||
|
this._openClocksItem.actor.visible = visible &&
|
||||||
|
(this._getClockApp() != null);
|
||||||
this._separator.visible = visible;
|
this._separator.visible = visible;
|
||||||
|
this._eventList.actor.visible = visible;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
let alignment = 0.25;
|
let alignment = 0.25;
|
||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||||
alignment = 1.0 - alignment;
|
alignment = 1.0 - alignment;
|
||||||
this.menu._arrowAlignment = alignment;
|
this.menu._arrowAlignment = alignment;
|
||||||
this._eventList.actor.get_parent().show();
|
|
||||||
} else {
|
} else {
|
||||||
this.menu._arrowAlignment = 0.5;
|
this.menu._arrowAlignment = 0.5;
|
||||||
this._eventList.actor.get_parent().hide();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -217,18 +203,34 @@ const DateMenuButton = new Lang.Class({
|
|||||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getCalendarApp: function() {
|
||||||
|
if (this._calendarApp !== undefined)
|
||||||
|
return this._calendarApp;
|
||||||
|
|
||||||
|
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
|
||||||
|
if (apps && (apps.length > 0))
|
||||||
|
this._calendarApp = apps[0];
|
||||||
|
else
|
||||||
|
this._calendarApp = null;
|
||||||
|
return this._calendarApp;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getClockApp: function() {
|
||||||
|
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||||
|
},
|
||||||
|
|
||||||
_onOpenCalendarActivate: function() {
|
_onOpenCalendarActivate: function() {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
|
|
||||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
let app = this._getCalendarApp();
|
||||||
if (app.get_id() == 'evolution')
|
if (app.get_id() == 'evolution.desktop')
|
||||||
app = Gio.DesktopAppInfo.new('evolution-calendar');
|
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||||
app.launch([], global.create_app_launch_context());
|
app.launch([], global.create_app_launch_context());
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOpenClocksActivate: function() {
|
_onOpenClocksActivate: function() {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
let app = this._getClockApp();
|
||||||
app.activate();
|
app.activate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
121
js/ui/dnd.js
121
js/ui/dnd.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 GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
@ -43,9 +44,7 @@ let dragMonitors = [];
|
|||||||
|
|
||||||
function _getEventHandlerActor() {
|
function _getEventHandlerActor() {
|
||||||
if (!eventHandlerActor) {
|
if (!eventHandlerActor) {
|
||||||
eventHandlerActor = new Clutter.Rectangle();
|
eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
|
||||||
eventHandlerActor.width = 0;
|
|
||||||
eventHandlerActor.height = 0;
|
|
||||||
Main.uiGroup.add_actor(eventHandlerActor);
|
Main.uiGroup.add_actor(eventHandlerActor);
|
||||||
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
|
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
|
||||||
// when you've grabbed the pointer.
|
// when you've grabbed the pointer.
|
||||||
@ -149,16 +148,16 @@ const _Draggable = new Lang.Class({
|
|||||||
|
|
||||||
_grabEvents: function() {
|
_grabEvents: function() {
|
||||||
if (!this._eventsGrabbed) {
|
if (!this._eventsGrabbed) {
|
||||||
Clutter.grab_pointer(_getEventHandlerActor());
|
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
|
||||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
if (this._eventsGrabbed)
|
||||||
this._eventsGrabbed = true;
|
Clutter.grab_pointer(_getEventHandlerActor());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ungrabEvents: function() {
|
_ungrabEvents: function() {
|
||||||
if (this._eventsGrabbed) {
|
if (this._eventsGrabbed) {
|
||||||
Clutter.ungrab_pointer();
|
Clutter.ungrab_pointer();
|
||||||
Clutter.ungrab_keyboard();
|
Main.popModal(_getEventHandlerActor());
|
||||||
this._eventsGrabbed = false;
|
this._eventsGrabbed = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -360,60 +359,65 @@ const _Draggable = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateDragHover : function () {
|
||||||
|
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
||||||
|
this._dragX, this._dragY);
|
||||||
|
let dragEvent = {
|
||||||
|
x: this._dragX,
|
||||||
|
y: this._dragY,
|
||||||
|
dragActor: this._dragActor,
|
||||||
|
source: this.actor._delegate,
|
||||||
|
targetActor: target
|
||||||
|
};
|
||||||
|
for (let i = 0; i < dragMonitors.length; i++) {
|
||||||
|
let motionFunc = dragMonitors[i].dragMotion;
|
||||||
|
if (motionFunc) {
|
||||||
|
let result = motionFunc(dragEvent);
|
||||||
|
if (result != DragMotionResult.CONTINUE) {
|
||||||
|
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (target) {
|
||||||
|
if (target._delegate && target._delegate.handleDragOver) {
|
||||||
|
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
|
||||||
|
// We currently loop through all parents on drag-over even if one of the children has handled it.
|
||||||
|
// We can check the return value of the function and break the loop if it's true if we don't want
|
||||||
|
// to continue checking the parents.
|
||||||
|
let result = target._delegate.handleDragOver(this.actor._delegate,
|
||||||
|
this._dragActor,
|
||||||
|
targX,
|
||||||
|
targY,
|
||||||
|
0);
|
||||||
|
if (result != DragMotionResult.CONTINUE) {
|
||||||
|
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target = target.get_parent();
|
||||||
|
}
|
||||||
|
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueUpdateDragHover: function() {
|
||||||
|
if (this._updateHoverId)
|
||||||
|
GLib.source_remove(this._updateHoverId);
|
||||||
|
|
||||||
|
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
Lang.bind(this, this._updateDragHover));
|
||||||
|
},
|
||||||
|
|
||||||
_updateDragPosition : function (event) {
|
_updateDragPosition : function (event) {
|
||||||
let [stageX, stageY] = event.get_coords();
|
let [stageX, stageY] = event.get_coords();
|
||||||
this._dragX = stageX;
|
this._dragX = stageX;
|
||||||
this._dragY = stageY;
|
this._dragY = stageY;
|
||||||
|
this._dragActor.set_position(stageX + this._dragOffsetX,
|
||||||
|
stageY + this._dragOffsetY);
|
||||||
|
|
||||||
// If we are dragging, update the position
|
this._queueUpdateDragHover();
|
||||||
if (this._dragActor) {
|
|
||||||
this._dragActor.set_position(stageX + this._dragOffsetX,
|
|
||||||
stageY + this._dragOffsetY);
|
|
||||||
|
|
||||||
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
|
||||||
stageX, stageY);
|
|
||||||
|
|
||||||
// We call observers only once per motion with the innermost
|
|
||||||
// target actor. If necessary, the observer can walk the
|
|
||||||
// parent itself.
|
|
||||||
let dragEvent = {
|
|
||||||
x: stageX,
|
|
||||||
y: stageY,
|
|
||||||
dragActor: this._dragActor,
|
|
||||||
source: this.actor._delegate,
|
|
||||||
targetActor: target
|
|
||||||
};
|
|
||||||
for (let i = 0; i < dragMonitors.length; i++) {
|
|
||||||
let motionFunc = dragMonitors[i].dragMotion;
|
|
||||||
if (motionFunc) {
|
|
||||||
let result = motionFunc(dragEvent);
|
|
||||||
if (result != DragMotionResult.CONTINUE) {
|
|
||||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (target) {
|
|
||||||
if (target._delegate && target._delegate.handleDragOver) {
|
|
||||||
let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
|
|
||||||
// We currently loop through all parents on drag-over even if one of the children has handled it.
|
|
||||||
// We can check the return value of the function and break the loop if it's true if we don't want
|
|
||||||
// to continue checking the parents.
|
|
||||||
let result = target._delegate.handleDragOver(this.actor._delegate,
|
|
||||||
this._dragActor,
|
|
||||||
targX,
|
|
||||||
targY,
|
|
||||||
event.get_time());
|
|
||||||
if (result != DragMotionResult.CONTINUE) {
|
|
||||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target = target.get_parent();
|
|
||||||
}
|
|
||||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -513,6 +517,11 @@ const _Draggable = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_cancelDrag: function(eventTime) {
|
_cancelDrag: function(eventTime) {
|
||||||
|
if (this._updateHoverId) {
|
||||||
|
GLib.source_remove(this._updateHoverId);
|
||||||
|
this._updateHoverId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('drag-cancelled', eventTime);
|
this.emit('drag-cancelled', eventTime);
|
||||||
this._dragInProgress = false;
|
this._dragInProgress = false;
|
||||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||||
|
@ -35,7 +35,7 @@ const GnomeSession = imports.misc.gnomeSession;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const UserMenu = imports.ui.userMenu;
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
let _endSessionDialog = null;
|
let _endSessionDialog = null;
|
||||||
|
|
||||||
@ -360,9 +360,9 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
icon_size: _DIALOG_ICON_SIZE,
|
icon_size: _DIALOG_ICON_SIZE,
|
||||||
style_class: dialogContent.iconStyleClass });
|
style_class: dialogContent.iconStyleClass });
|
||||||
} else {
|
} else {
|
||||||
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
|
let avatarWidget = new UserWidget.Avatar(this._user,
|
||||||
{ iconSize: _DIALOG_ICON_SIZE,
|
{ iconSize: _DIALOG_ICON_SIZE,
|
||||||
styleClass: dialogContent.iconStyleClass });
|
styleClass: dialogContent.iconStyleClass });
|
||||||
this._iconBin.child = avatarWidget.actor;
|
this._iconBin.child = avatarWidget.actor;
|
||||||
avatarWidget.update();
|
avatarWidget.update();
|
||||||
}
|
}
|
||||||
@ -420,6 +420,7 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
_startTimer: function() {
|
_startTimer: function() {
|
||||||
let startTime = GLib.get_monotonic_time();
|
let startTime = GLib.get_monotonic_time();
|
||||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||||
|
this._updateDescription();
|
||||||
|
|
||||||
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
|
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
|
@ -10,6 +10,7 @@ const Clutter = imports.gi.Clutter;;
|
|||||||
const Gettext = imports.gettext;
|
const Gettext = imports.gettext;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
@ -39,6 +40,22 @@ function _patchContainerClass(containerClass) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _patchLayoutClass(layoutClass, styleProps) {
|
||||||
|
if (styleProps)
|
||||||
|
layoutClass.prototype.hookup_style = function(container) {
|
||||||
|
container.connect('style-changed', Lang.bind(this, function() {
|
||||||
|
let node = container.get_theme_node();
|
||||||
|
for (let prop in styleProps)
|
||||||
|
this[prop] = node.get_length(styleProps[prop]);
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
layoutClass.prototype.child_set = function(actor, props) {
|
||||||
|
let meta = this.get_child_meta(actor.get_parent(), actor);
|
||||||
|
for (let prop in props)
|
||||||
|
meta[prop] = props[prop];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function _makeLoggingFunc(func) {
|
function _makeLoggingFunc(func) {
|
||||||
return function() {
|
return function() {
|
||||||
return func([].join.call(arguments, ', '));
|
return func([].join.call(arguments, ', '));
|
||||||
@ -60,6 +77,12 @@ function init() {
|
|||||||
_patchContainerClass(St.BoxLayout);
|
_patchContainerClass(St.BoxLayout);
|
||||||
_patchContainerClass(St.Table);
|
_patchContainerClass(St.Table);
|
||||||
|
|
||||||
|
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
|
||||||
|
column_spacing: 'spacing-columns' });
|
||||||
|
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
|
||||||
|
column_spacing: 'spacing-columns' });
|
||||||
|
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
|
||||||
|
|
||||||
Clutter.Actor.prototype.toString = function() {
|
Clutter.Actor.prototype.toString = function() {
|
||||||
return St.describe_actor(this);
|
return St.describe_actor(this);
|
||||||
};
|
};
|
||||||
|
@ -292,7 +292,7 @@ function disableAllExtensions() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (initted) {
|
if (initted) {
|
||||||
enabledExtensions.forEach(function(uuid) {
|
extensionOrder.slice().reverse().forEach(function(uuid) {
|
||||||
disableExtension(uuid);
|
disableExtension(uuid);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,9 @@ const GrabHelper = new Lang.Class({
|
|||||||
|
|
||||||
this._actors = [];
|
this._actors = [];
|
||||||
this._capturedEventId = 0;
|
this._capturedEventId = 0;
|
||||||
this._keyFocusNotifyId = 0;
|
|
||||||
this._focusWindowChangedId = 0;
|
|
||||||
this._ignoreRelease = false;
|
this._ignoreRelease = false;
|
||||||
this._isUngrabbingCount = 0;
|
|
||||||
|
|
||||||
this._modalCount = 0;
|
this._modalCount = 0;
|
||||||
this._grabFocusCount = 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// addActor:
|
// addActor:
|
||||||
@ -118,38 +114,36 @@ const GrabHelper = new Lang.Class({
|
|||||||
// grab:
|
// grab:
|
||||||
// @params: A bunch of parameters, see below
|
// @params: A bunch of parameters, see below
|
||||||
//
|
//
|
||||||
// Grabs the mouse and keyboard, according to the GrabHelper's
|
// The general effect of a "grab" is to ensure that the passed in actor
|
||||||
// parameters. If @newFocus is not %null, then the keyboard focus
|
// and all actors inside the grab get exclusive control of the mouse and
|
||||||
// is moved to the first #StWidget:can-focus widget inside it.
|
// keyboard, with the grab automatically being dropped if the user tries
|
||||||
|
// to dismiss it. The actor is passed in through @params.actor.
|
||||||
//
|
//
|
||||||
// The grab will automatically be dropped if:
|
// grab() can be called multiple times, with the scope of the grab being
|
||||||
// - The user clicks outside the grabbed actors
|
// changed to a different actor every time. A nested grab does not have
|
||||||
// - The user types Escape
|
// to have its grabbed actor inside the parent grab actors.
|
||||||
// - 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
|
// Grabs can be automatically dropped if the user tries to dismiss it
|
||||||
// new actor. If you attempt to grab an already focused actor, the
|
// in one of two ways: the user clicking outside the currently grabbed
|
||||||
// request to be focused will be ignored. The actor will not be
|
// actor, or the user typing the Escape key.
|
||||||
// added to the grab stack, so do not call a paired ungrab().
|
|
||||||
//
|
//
|
||||||
// If @params contains { modal: true }, then grab() will push a modal
|
// If the user clicks outside the grabbed actors, and the clicked on
|
||||||
// on the owner of the GrabHelper. As long as there is at least one
|
// actor is part of a previous grab in the stack, grabs will be popped
|
||||||
// { modal: true } actor on the grab stack, the grab will be kept.
|
// until that grab is active. However, the click event will not be
|
||||||
// When the last { modal: true } actor is ungrabbed, then the modal
|
// replayed to the actor.
|
||||||
// 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()
|
// If the user types the Escape key, one grab from the grab stack will
|
||||||
// while the shell is outside the overview, it will set the stage
|
// be popped.
|
||||||
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
|
//
|
||||||
// revert it back, and re-focus the previously-focused window (if
|
// When a grab is popped by user interacting as described above, if you
|
||||||
// another window hasn't been explicitly focused before then).
|
// pass a callback as @params.onUngrab, it will be called with %true.
|
||||||
|
//
|
||||||
|
// If @params.focus is not null, we'll set the key focus directly
|
||||||
|
// to that actor instead of navigating in @params.actor. This is for
|
||||||
|
// use cases like menus, where we want to grab the menu actor, but keep
|
||||||
|
// focus on the clicked on menu item.
|
||||||
grab: function(params) {
|
grab: function(params) {
|
||||||
params = Params.parse(params, { actor: null,
|
params = Params.parse(params, { actor: null,
|
||||||
modal: false,
|
|
||||||
grabFocus: false,
|
|
||||||
focus: null,
|
focus: null,
|
||||||
onUngrab: null });
|
onUngrab: null });
|
||||||
|
|
||||||
@ -162,24 +156,18 @@ const GrabHelper = new Lang.Class({
|
|||||||
|
|
||||||
params.savedFocus = focus;
|
params.savedFocus = focus;
|
||||||
|
|
||||||
if (params.modal && !this._takeModalGrab())
|
if (!this._takeModalGrab())
|
||||||
return false;
|
|
||||||
|
|
||||||
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._grabStack.push(params);
|
this._grabStack.push(params);
|
||||||
|
|
||||||
if (params.focus) {
|
if (params.focus) {
|
||||||
params.focus.grab_key_focus();
|
params.focus.grab_key_focus();
|
||||||
} else if (newFocus && (hadFocus || params.grabFocus)) {
|
} else if (newFocus && hadFocus) {
|
||||||
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||||
newFocus.grab_key_focus();
|
newFocus.grab_key_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((params.grabFocus || params.modal) && !this._capturedEventId)
|
|
||||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -188,6 +176,8 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (firstGrab) {
|
if (firstGrab) {
|
||||||
if (!Main.pushModal(this._owner, this._modalParams))
|
if (!Main.pushModal(this._owner, this._modalParams))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._modalCount++;
|
this._modalCount++;
|
||||||
@ -199,58 +189,15 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (this._modalCount > 0)
|
if (this._modalCount > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
global.stage.disconnect(this._capturedEventId);
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
|
||||||
|
this._ignoreRelease = false;
|
||||||
|
|
||||||
Main.popModal(this._owner);
|
Main.popModal(this._owner);
|
||||||
global.sync_pointer();
|
global.sync_pointer();
|
||||||
},
|
},
|
||||||
|
|
||||||
_takeFocusGrab: function(hadFocus) {
|
|
||||||
let firstGrab = (this._grabFocusCount == 0);
|
|
||||||
if (firstGrab) {
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
|
|
||||||
this._grabbedFromKeynav = hadFocus;
|
|
||||||
this._preGrabInputMode = global.stage_input_mode;
|
|
||||||
|
|
||||||
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
|
|
||||||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
|
||||||
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._grabFocusCount++;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_releaseFocusGrab: function() {
|
|
||||||
this._grabFocusCount--;
|
|
||||||
if (this._grabFocusCount > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._keyFocusNotifyId > 0) {
|
|
||||||
global.stage.disconnect(this._keyFocusNotifyId);
|
|
||||||
this._keyFocusNotifyId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._focusWindowChangedId > 0) {
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
|
||||||
this._focusWindowChangedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let prePopInputMode = global.stage_input_mode;
|
|
||||||
|
|
||||||
if (this._grabbedFromKeynav) {
|
|
||||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
|
||||||
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
|
|
||||||
},
|
|
||||||
|
|
||||||
// ignoreRelease:
|
// ignoreRelease:
|
||||||
//
|
//
|
||||||
// Make sure that the next button release event evaluated by the
|
// Make sure that the next button release event evaluated by the
|
||||||
@ -264,10 +211,14 @@ const GrabHelper = new Lang.Class({
|
|||||||
// ungrab:
|
// ungrab:
|
||||||
// @params: The parameters for the grab; see below.
|
// @params: The parameters for the grab; see below.
|
||||||
//
|
//
|
||||||
// Pops an actor from the grab stack, potentially dropping the grab.
|
// Pops @params.actor from the grab stack, potentially dropping
|
||||||
|
// the grab. If the actor is not on the grab stack, this call is
|
||||||
|
// ignored with no ill effects.
|
||||||
//
|
//
|
||||||
// If the actor that was popped from the grab stack was not the actor
|
// If the actor is not at the top of the grab stack, grabs are
|
||||||
// That was passed in, this call is ignored.
|
// popped until the grabbed actor is at the top of the grab stack.
|
||||||
|
// The onUngrab callback for every grab is called for every popped
|
||||||
|
// grab with the parameter %false.
|
||||||
ungrab: function(params) {
|
ungrab: function(params) {
|
||||||
params = Params.parse(params, { actor: this.currentGrab.actor,
|
params = Params.parse(params, { actor: this.currentGrab.actor,
|
||||||
isUser: false });
|
isUser: false });
|
||||||
@ -276,14 +227,6 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (grabStackIndex < 0)
|
if (grabStackIndex < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We may get key focus changes when calling onUngrab, which
|
|
||||||
// would cause an extra ungrab() on the next actor in the
|
|
||||||
// stack, which is wrong. Ignore key focus changes during the
|
|
||||||
// ungrab, and restore the saved key focus ourselves afterwards.
|
|
||||||
// We use a count as ungrab() may be re-entrant, as onUngrab()
|
|
||||||
// may ungrab additional actors.
|
|
||||||
this._isUngrabbingCount++;
|
|
||||||
|
|
||||||
let focus = global.stage.key_focus;
|
let focus = global.stage.key_focus;
|
||||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||||
|
|
||||||
@ -298,18 +241,7 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (poppedGrab.onUngrab)
|
if (poppedGrab.onUngrab)
|
||||||
poppedGrab.onUngrab(params.isUser);
|
poppedGrab.onUngrab(params.isUser);
|
||||||
|
|
||||||
if (poppedGrab.modal)
|
this._releaseModalGrab();
|
||||||
this._releaseModalGrab();
|
|
||||||
|
|
||||||
if (poppedGrab.grabFocus)
|
|
||||||
this._releaseFocusGrab();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.grabbed && this._capturedEventId > 0) {
|
|
||||||
global.stage.disconnect(this._capturedEventId);
|
|
||||||
this._capturedEventId = 0;
|
|
||||||
|
|
||||||
this._ignoreRelease = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hadFocus) {
|
if (hadFocus) {
|
||||||
@ -317,8 +249,6 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (poppedGrab.savedFocus)
|
if (poppedGrab.savedFocus)
|
||||||
poppedGrab.savedFocus.grab_key_focus();
|
poppedGrab.savedFocus.grab_key_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._isUngrabbingCount--;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCapturedEvent: function(actor, event) {
|
_onCapturedEvent: function(actor, event) {
|
||||||
@ -339,9 +269,6 @@ const GrabHelper = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!button && this._modalCount == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (this._isWithinGrabbedActor(event.get_source()))
|
if (this._isWithinGrabbedActor(event.get_source()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -358,21 +285,6 @@ const GrabHelper = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._modalCount > 0;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyFocusChanged: function() {
|
|
||||||
if (this._isUngrabbingCount > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let focus = global.stage.key_focus;
|
|
||||||
if (!focus || !this._isWithinGrabbedActor(focus))
|
|
||||||
this.ungrab({ isUser: true });
|
|
||||||
},
|
|
||||||
|
|
||||||
_focusWindowChanged: function() {
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
if (metaDisplay.focus_window != null)
|
|
||||||
this.ungrab({ isUser: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
212
js/ui/layout.js
212
js/ui/layout.js
@ -118,10 +118,25 @@ const MonitorConstraint = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Monitor = new Lang.Class({
|
||||||
|
Name: 'Monitor',
|
||||||
|
|
||||||
|
_init: function(index, geometry) {
|
||||||
|
this.index = index;
|
||||||
|
this.x = geometry.x;
|
||||||
|
this.y = geometry.y;
|
||||||
|
this.width = geometry.width;
|
||||||
|
this.height = geometry.height;
|
||||||
|
},
|
||||||
|
|
||||||
|
get inFullscreen() {
|
||||||
|
return global.screen.get_monitor_in_fullscreen(this.index);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const defaultParams = {
|
const defaultParams = {
|
||||||
trackFullscreen: false,
|
trackFullscreen: false,
|
||||||
affectsStruts: false,
|
affectsStruts: false,
|
||||||
affectsInputRegion: true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const LayoutManager = new Lang.Class({
|
const LayoutManager = new Lang.Class({
|
||||||
@ -173,10 +188,12 @@ const LayoutManager = new Lang.Class({
|
|||||||
global.stage.remove_actor(global.window_group);
|
global.stage.remove_actor(global.window_group);
|
||||||
this.uiGroup.add_actor(global.window_group);
|
this.uiGroup.add_actor(global.window_group);
|
||||||
|
|
||||||
global.stage.remove_actor(global.overlay_group);
|
|
||||||
this.uiGroup.add_actor(global.overlay_group);
|
|
||||||
global.stage.add_child(this.uiGroup);
|
global.stage.add_child(this.uiGroup);
|
||||||
|
|
||||||
|
this.overviewGroup = new St.Widget({ name: 'overviewGroup',
|
||||||
|
visible: false });
|
||||||
|
this.addChrome(this.overviewGroup);
|
||||||
|
|
||||||
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
|
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
|
||||||
visible: false,
|
visible: false,
|
||||||
clip_to_allocation: true,
|
clip_to_allocation: true,
|
||||||
@ -227,24 +244,24 @@ const LayoutManager = new Lang.Class({
|
|||||||
this._monitorsChanged();
|
this._monitorsChanged();
|
||||||
},
|
},
|
||||||
|
|
||||||
// This is called by Main after everything else is constructed;
|
// This is called by Main after everything else is constructed
|
||||||
// it needs access to Main.overview, which didn't exist
|
|
||||||
// yet when the LayoutManager was constructed.
|
|
||||||
init: function() {
|
init: function() {
|
||||||
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
|
|
||||||
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
|
|
||||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
|
||||||
this._prepareStartupAnimation();
|
this._loadBackground();
|
||||||
},
|
},
|
||||||
|
|
||||||
_overviewShowing: function() {
|
showOverview: function() {
|
||||||
|
this.overviewGroup.show();
|
||||||
|
|
||||||
this._inOverview = true;
|
this._inOverview = true;
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
this._queueUpdateRegions();
|
this._queueUpdateRegions();
|
||||||
},
|
},
|
||||||
|
|
||||||
_overviewHidden: function() {
|
hideOverview: function() {
|
||||||
|
this.overviewGroup.hide();
|
||||||
|
|
||||||
this._inOverview = false;
|
this._inOverview = false;
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
this._queueUpdateRegions();
|
this._queueUpdateRegions();
|
||||||
@ -261,7 +278,7 @@ const LayoutManager = new Lang.Class({
|
|||||||
this.monitors = [];
|
this.monitors = [];
|
||||||
let nMonitors = screen.get_n_monitors();
|
let nMonitors = screen.get_n_monitors();
|
||||||
for (let i = 0; i < nMonitors; i++)
|
for (let i = 0; i < nMonitors; i++)
|
||||||
this.monitors.push(screen.get_monitor_geometry(i));
|
this.monitors.push(new Monitor(i, screen.get_monitor_geometry(i)));
|
||||||
|
|
||||||
if (nMonitors == 1) {
|
if (nMonitors == 1) {
|
||||||
this.primaryIndex = this.bottomIndex = 0;
|
this.primaryIndex = this.bottomIndex = 0;
|
||||||
@ -283,8 +300,10 @@ const LayoutManager = new Lang.Class({
|
|||||||
|
|
||||||
_updateHotCorners: function() {
|
_updateHotCorners: function() {
|
||||||
// destroy old hot corners
|
// destroy old hot corners
|
||||||
for (let i = 0; i < this.hotCorners.length; i++)
|
this.hotCorners.forEach(function(corner) {
|
||||||
this.hotCorners[i].destroy();
|
if (corner)
|
||||||
|
corner.destroy();
|
||||||
|
});
|
||||||
this.hotCorners = [];
|
this.hotCorners = [];
|
||||||
|
|
||||||
let size = this.panelBox.height;
|
let size = this.panelBox.height;
|
||||||
@ -295,9 +314,9 @@ const LayoutManager = new Lang.Class({
|
|||||||
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
|
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
|
||||||
let cornerY = monitor.y;
|
let cornerY = monitor.y;
|
||||||
|
|
||||||
if (i != this.primaryIndex) {
|
let haveTopLeftCorner = true;
|
||||||
let haveTopLeftCorner = true;
|
|
||||||
|
|
||||||
|
if (i != this.primaryIndex) {
|
||||||
// Check if we have a top left (right for RTL) corner.
|
// Check if we have a top left (right for RTL) corner.
|
||||||
// I.e. if there is no monitor directly above or to the left(right)
|
// I.e. if there is no monitor directly above or to the left(right)
|
||||||
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
|
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
|
||||||
@ -324,14 +343,15 @@ const LayoutManager = new Lang.Class({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!haveTopLeftCorner)
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let corner = new HotCorner(this, monitor, cornerX, cornerY);
|
if (haveTopLeftCorner) {
|
||||||
corner.setBarrierSize(size);
|
let corner = new HotCorner(this, monitor, cornerX, cornerY);
|
||||||
this.hotCorners.push(corner);
|
corner.setBarrierSize(size);
|
||||||
|
this.hotCorners.push(corner);
|
||||||
|
} else {
|
||||||
|
this.hotCorners.push(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('hot-corners-changed');
|
this.emit('hot-corners-changed');
|
||||||
@ -408,7 +428,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
|
|
||||||
let size = this.panelBox.height;
|
let size = this.panelBox.height;
|
||||||
this.hotCorners.forEach(function(corner) {
|
this.hotCorners.forEach(function(corner) {
|
||||||
corner.setBarrierSize(size);
|
if (corner)
|
||||||
|
corner.setBarrierSize(size);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -507,17 +528,10 @@ const LayoutManager = new Lang.Class({
|
|||||||
get focusIndex() {
|
get focusIndex() {
|
||||||
let i = Main.layoutManager.primaryIndex;
|
let i = Main.layoutManager.primaryIndex;
|
||||||
|
|
||||||
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
|
if (global.stage.key_focus != null)
|
||||||
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
|
i = this.findIndexForActor(global.stage.key_focus);
|
||||||
let focusActor = global.stage.key_focus;
|
else if (global.display.focus_window != null)
|
||||||
if (focusActor)
|
i = global.display.focus_window.get_monitor();
|
||||||
i = this.findIndexForActor(focusActor);
|
|
||||||
} else {
|
|
||||||
let focusWindow = global.display.focus_window;
|
|
||||||
if (focusWindow)
|
|
||||||
i = focusWindow.get_monitor();
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -536,6 +550,25 @@ const LayoutManager = new Lang.Class({
|
|||||||
return this._keyboardIndex;
|
return this._keyboardIndex;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_loadBackground: function() {
|
||||||
|
this._systemBackground = new Background.SystemBackground();
|
||||||
|
this._systemBackground.actor.hide();
|
||||||
|
|
||||||
|
global.stage.insert_child_below(this._systemBackground.actor, null);
|
||||||
|
|
||||||
|
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||||
|
coordinate: Clutter.BindCoordinate.ALL });
|
||||||
|
this._systemBackground.actor.add_constraint(constraint);
|
||||||
|
|
||||||
|
let signalId = this._systemBackground.connect('loaded', Lang.bind(this, function() {
|
||||||
|
this._systemBackground.disconnect(signalId);
|
||||||
|
this._systemBackground.actor.show();
|
||||||
|
global.stage.show();
|
||||||
|
|
||||||
|
this._prepareStartupAnimation();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
// Startup Animations
|
// Startup Animations
|
||||||
//
|
//
|
||||||
// We have two different animations, depending on whether we're a greeter
|
// We have two different animations, depending on whether we're a greeter
|
||||||
@ -556,9 +589,13 @@ const LayoutManager = new Lang.Class({
|
|||||||
// screen. So, we set no_clear_hint at the end of the animation.
|
// screen. So, we set no_clear_hint at the end of the animation.
|
||||||
|
|
||||||
_prepareStartupAnimation: function() {
|
_prepareStartupAnimation: function() {
|
||||||
// Set ourselves to FULLSCREEN input mode while the animation is running
|
// During the initial transition, add a simple actor to block all events,
|
||||||
// so events don't get delivered to X11 windows (which are distorted by the animation)
|
// so they don't get delivered to X11 windows that have been transformed.
|
||||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||||
|
width: global.screen_width,
|
||||||
|
height: global.screen_height,
|
||||||
|
reactive: true });
|
||||||
|
this.addChrome(this._coverPane);
|
||||||
|
|
||||||
if (Main.sessionMode.isGreeter) {
|
if (Main.sessionMode.isGreeter) {
|
||||||
this.panelBox.translation_y = -this.panelBox.height;
|
this.panelBox.translation_y = -this.panelBox.height;
|
||||||
@ -581,35 +618,18 @@ const LayoutManager = new Lang.Class({
|
|||||||
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
|
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._systemBackground = new Background.SystemBackground();
|
this.emit('startup-prepared');
|
||||||
this._systemBackground.actor.hide();
|
|
||||||
|
|
||||||
global.stage.insert_child_below(this._systemBackground.actor, null);
|
// We're mostly prepared for the startup animation
|
||||||
|
// now, but since a lot is going on asynchronously
|
||||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
// during startup, let's defer the startup animation
|
||||||
coordinate: Clutter.BindCoordinate.ALL });
|
// until the event loop is uncontended and idle.
|
||||||
this._systemBackground.actor.add_constraint(constraint);
|
// This helps to prevent us from running the animation
|
||||||
|
// when the system is bogged down
|
||||||
let signalId = this._systemBackground.connect('loaded',
|
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
|
||||||
Lang.bind(this, function() {
|
this._startupAnimation();
|
||||||
this._systemBackground.disconnect(signalId);
|
return false;
|
||||||
this._systemBackground.actor.show();
|
}));
|
||||||
global.stage.show();
|
|
||||||
|
|
||||||
this.emit('startup-prepared');
|
|
||||||
|
|
||||||
// We're mostly prepared for the startup animation
|
|
||||||
// now, but since a lot is going on asynchronously
|
|
||||||
// during startup, let's defer the startup animation
|
|
||||||
// until the event loop is uncontended and idle.
|
|
||||||
// This helps to prevent us from running the animation
|
|
||||||
// when the system is bogged down
|
|
||||||
GLib.idle_add(GLib.PRIORITY_LOW,
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._startupAnimation();
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_startupAnimation: function() {
|
_startupAnimation: function() {
|
||||||
@ -645,7 +665,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
// we no longer need to clear the stage
|
// we no longer need to clear the stage
|
||||||
global.stage.no_clear_hint = true;
|
global.stage.no_clear_hint = true;
|
||||||
|
|
||||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
this._coverPane.destroy();
|
||||||
|
this._coverPane = null;
|
||||||
|
|
||||||
this._systemBackground.actor.destroy();
|
this._systemBackground.actor.destroy();
|
||||||
this._systemBackground = null;
|
this._systemBackground = null;
|
||||||
@ -666,7 +687,6 @@ const LayoutManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
showKeyboard: function () {
|
showKeyboard: function () {
|
||||||
this.keyboardBox.raise_top();
|
|
||||||
Tweener.addTween(this.keyboardBox,
|
Tweener.addTween(this.keyboardBox,
|
||||||
{ anchor_y: this.keyboardBox.height,
|
{ anchor_y: this.keyboardBox.height,
|
||||||
time: KEYBOARD_ANIMATION_TIME,
|
time: KEYBOARD_ANIMATION_TIME,
|
||||||
@ -711,11 +731,10 @@ const LayoutManager = new Lang.Class({
|
|||||||
// @actor: an actor to add to the chrome
|
// @actor: an actor to add to the chrome
|
||||||
// @params: (optional) additional params
|
// @params: (optional) additional params
|
||||||
//
|
//
|
||||||
// Adds @actor to the chrome, and (unless %affectsInputRegion in
|
// Adds @actor to the chrome, and extends the input region
|
||||||
// @params is %false) extends the input region to include it.
|
// to include it. Changes in @actor's size, position, and
|
||||||
// Changes in @actor's size, position, and visibility will
|
// visibility will automatically result in appropriate changes
|
||||||
// automatically result in appropriate changes to the input
|
// to the input region.
|
||||||
// region.
|
|
||||||
//
|
//
|
||||||
// If %affectsStruts in @params is %true (and @actor is along a
|
// If %affectsStruts in @params is %true (and @actor is along a
|
||||||
// screen edge), then @actor's size and position will also affect
|
// screen edge), then @actor's size and position will also affect
|
||||||
@ -728,6 +747,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
// and shown otherwise)
|
// and shown otherwise)
|
||||||
addChrome: function(actor, params) {
|
addChrome: function(actor, params) {
|
||||||
this.uiGroup.add_actor(actor);
|
this.uiGroup.add_actor(actor);
|
||||||
|
if (this.uiGroup.contains(global.top_window_group))
|
||||||
|
this.uiGroup.set_child_below_sibling(actor, global.top_window_group);
|
||||||
this._trackActor(actor, params);
|
this._trackActor(actor, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -900,13 +921,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateFullscreen: function() {
|
_updateFullscreen: function() {
|
||||||
for (let i = 0; i < this.monitors.length; i++)
|
|
||||||
this.monitors[i].inFullscreen = global.screen.get_monitor_in_fullscreen (i);
|
|
||||||
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
this._queueUpdateRegions();
|
this._queueUpdateRegions();
|
||||||
|
|
||||||
this.emit('fullscreen-changed');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_windowsRestacked: function() {
|
_windowsRestacked: function() {
|
||||||
@ -934,7 +950,7 @@ const LayoutManager = new Lang.Class({
|
|||||||
|
|
||||||
for (i = 0; i < this._trackedActors.length; i++) {
|
for (i = 0; i < this._trackedActors.length; i++) {
|
||||||
let actorData = this._trackedActors[i];
|
let actorData = this._trackedActors[i];
|
||||||
if (!(actorData.affectsInputRegion && wantsInputRegion) && !actorData.affectsStruts)
|
if (!wantsInputRegion && !actorData.affectsStruts)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let [x, y] = actorData.actor.get_transformed_position();
|
let [x, y] = actorData.actor.get_transformed_position();
|
||||||
@ -944,13 +960,8 @@ const LayoutManager = new Lang.Class({
|
|||||||
w = Math.round(w);
|
w = Math.round(w);
|
||||||
h = Math.round(h);
|
h = Math.round(h);
|
||||||
|
|
||||||
if (actorData.affectsInputRegion && wantsInputRegion) {
|
if (wantsInputRegion && actorData.actor.get_paint_visibility())
|
||||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h }));
|
||||||
|
|
||||||
if (actorData.actor.get_paint_visibility() &&
|
|
||||||
!this.uiGroup.get_skip_paint(actorData.actor))
|
|
||||||
rects.push(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actorData.affectsStruts) {
|
if (actorData.affectsStruts) {
|
||||||
// Limit struts to the size of the screen
|
// Limit struts to the size of the screen
|
||||||
@ -1092,12 +1103,21 @@ const HotCorner = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
this._verticalBarrier = new Meta.Barrier({ display: global.display,
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||||
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
|
this._verticalBarrier = new Meta.Barrier({ display: global.display,
|
||||||
directions: Meta.BarrierDirection.POSITIVE_X });
|
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
|
||||||
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
|
directions: Meta.BarrierDirection.NEGATIVE_X });
|
||||||
x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
|
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
|
||||||
directions: Meta.BarrierDirection.POSITIVE_Y });
|
x1: this._x - size, x2: this._x, y1: this._y, y2: this._y,
|
||||||
|
directions: Meta.BarrierDirection.POSITIVE_Y });
|
||||||
|
} else {
|
||||||
|
this._verticalBarrier = new Meta.Barrier({ display: global.display,
|
||||||
|
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
|
||||||
|
directions: Meta.BarrierDirection.POSITIVE_X });
|
||||||
|
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
|
||||||
|
x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
|
||||||
|
directions: Meta.BarrierDirection.POSITIVE_Y });
|
||||||
|
}
|
||||||
|
|
||||||
this._pressureBarrier.addBarrier(this._verticalBarrier);
|
this._pressureBarrier.addBarrier(this._verticalBarrier);
|
||||||
this._pressureBarrier.addBarrier(this._horizontalBarrier);
|
this._pressureBarrier.addBarrier(this._horizontalBarrier);
|
||||||
@ -1112,11 +1132,11 @@ const HotCorner = new Lang.Class({
|
|||||||
height: 3,
|
height: 3,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
|
|
||||||
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
|
this._corner = new Clutter.Actor({ name: 'hot-corner',
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
this._corner._delegate = this;
|
this._corner._delegate = this;
|
||||||
|
|
||||||
this.actor.add_child(this._corner);
|
this.actor.add_child(this._corner);
|
||||||
|
@ -308,10 +308,6 @@ const Result = new Lang.Class({
|
|||||||
box.add(resultTxt);
|
box.add(resultTxt);
|
||||||
let objLink = new ObjLink(this._lookingGlass, o);
|
let objLink = new ObjLink(this._lookingGlass, o);
|
||||||
box.add(objLink.actor);
|
box.add(objLink.actor);
|
||||||
let line = new Clutter.Rectangle({ name: 'Separator' });
|
|
||||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
|
||||||
padBin.add_actor(line);
|
|
||||||
this.actor.add(padBin);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -853,8 +849,9 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._updateFont();
|
this._updateFont();
|
||||||
|
|
||||||
// We want it to appear to slide out from underneath the panel
|
// We want it to appear to slide out from underneath the panel
|
||||||
Main.layoutManager.panelBox.add_actor(this.actor);
|
Main.uiGroup.add_actor(this.actor);
|
||||||
this.actor.lower_bottom();
|
Main.uiGroup.set_child_below_sibling(this.actor,
|
||||||
|
Main.layoutManager.panelBox);
|
||||||
Main.layoutManager.panelBox.connect('allocation-changed',
|
Main.layoutManager.panelBox.connect('allocation-changed',
|
||||||
Lang.bind(this, this._queueResize));
|
Lang.bind(this, this._queueResize));
|
||||||
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
||||||
@ -989,28 +986,18 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
_showCompletions: function(completions) {
|
_showCompletions: function(completions) {
|
||||||
if (!this._completionActor) {
|
if (!this._completionActor) {
|
||||||
let actor = new St.BoxLayout({ vertical: true });
|
this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
|
||||||
|
this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
|
this._completionActor.clutter_text.line_wrap = true;
|
||||||
this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
|
||||||
this._completionText.clutter_text.line_wrap = true;
|
|
||||||
actor.add(this._completionText);
|
|
||||||
|
|
||||||
let line = new Clutter.Rectangle();
|
|
||||||
let padBin = new St.Bin({ x_fill: true, y_fill: true });
|
|
||||||
padBin.add_actor(line);
|
|
||||||
actor.add(padBin);
|
|
||||||
|
|
||||||
this._completionActor = actor;
|
|
||||||
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._completionText.set_text(completions.join(', '));
|
this._completionActor.set_text(completions.join(', '));
|
||||||
|
|
||||||
// Setting the height to -1 allows us to get its actual preferred height rather than
|
// Setting the height to -1 allows us to get its actual preferred height rather than
|
||||||
// whatever was last given in set_height by Tweener.
|
// whatever was last given in set_height by Tweener.
|
||||||
this._completionActor.set_height(-1);
|
this._completionActor.set_height(-1);
|
||||||
let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
|
let [minHeight, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
|
||||||
|
|
||||||
// Don't reanimate if we are already visible
|
// Don't reanimate if we are already visible
|
||||||
if (this._completionActor.visible) {
|
if (this._completionActor.visible) {
|
||||||
@ -1086,7 +1073,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
|
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
|
||||||
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
|
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
|
||||||
this.actor.x = (primary.width - myWidth) / 2;
|
this.actor.x = (primary.width - myWidth) / 2;
|
||||||
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
|
this._hiddenY = Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
|
||||||
this._targetY = this._hiddenY + myHeight;
|
this._targetY = this._hiddenY + myHeight;
|
||||||
this.actor.y = this._hiddenY;
|
this.actor.y = this._hiddenY;
|
||||||
this.actor.width = myWidth;
|
this.actor.width = myWidth;
|
||||||
|
234
js/ui/main.js
234
js/ui/main.js
@ -38,9 +38,11 @@ const Magnifier = imports.ui.magnifier;
|
|||||||
const XdndHandler = imports.ui.xdndHandler;
|
const XdndHandler = imports.ui.xdndHandler;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
|
||||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||||
|
|
||||||
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||||
|
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
|
||||||
|
|
||||||
let componentManager = null;
|
let componentManager = null;
|
||||||
let panel = null;
|
let panel = null;
|
||||||
let overview = null;
|
let overview = null;
|
||||||
@ -68,7 +70,8 @@ let layoutManager = null;
|
|||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
let _overridesSettings = null;
|
let _a11ySettings = null;
|
||||||
|
let dynamicWorkspacesSchema = null;
|
||||||
|
|
||||||
function _sessionUpdated() {
|
function _sessionUpdated() {
|
||||||
_loadDefaultStylesheet();
|
_loadDefaultStylesheet();
|
||||||
@ -106,6 +109,7 @@ function start() {
|
|||||||
|
|
||||||
function _sessionsLoaded() {
|
function _sessionsLoaded() {
|
||||||
sessionMode.connect('updated', _sessionUpdated);
|
sessionMode.connect('updated', _sessionUpdated);
|
||||||
|
_initializePrefs();
|
||||||
_initializeUI();
|
_initializeUI();
|
||||||
|
|
||||||
shellDBusService = new ShellDBus.GnomeShell();
|
shellDBusService = new ShellDBus.GnomeShell();
|
||||||
@ -114,6 +118,17 @@ function _sessionsLoaded() {
|
|||||||
_sessionUpdated();
|
_sessionUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _initializePrefs() {
|
||||||
|
let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
|
||||||
|
for (let i = 0; i < keys.length; i++)
|
||||||
|
Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
|
||||||
|
|
||||||
|
if (keys.indexOf('dynamic-workspaces') > -1)
|
||||||
|
dynamicWorkspacesSchema = sessionMode.overridesSchema;
|
||||||
|
else
|
||||||
|
dynamicWorkspacesSchema = 'org.gnome.mutter';
|
||||||
|
}
|
||||||
|
|
||||||
function _initializeUI() {
|
function _initializeUI() {
|
||||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||||
// also initialize ShellAppSystem first. ShellAppSystem
|
// also initialize ShellAppSystem first. ShellAppSystem
|
||||||
@ -123,11 +138,9 @@ function _initializeUI() {
|
|||||||
// and recalculate application associations, so to avoid
|
// and recalculate application associations, so to avoid
|
||||||
// races for now we initialize it here. It's better to
|
// races for now we initialize it here. It's better to
|
||||||
// be predictable anyways.
|
// be predictable anyways.
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
Shell.WindowTracker.get_default();
|
||||||
Shell.AppUsage.get_default();
|
Shell.AppUsage.get_default();
|
||||||
|
|
||||||
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
|
|
||||||
|
|
||||||
_loadDefaultStylesheet();
|
_loadDefaultStylesheet();
|
||||||
|
|
||||||
// Setup the stage hierarchy early
|
// Setup the stage hierarchy early
|
||||||
@ -157,9 +170,12 @@ function _initializeUI() {
|
|||||||
layoutManager.init();
|
layoutManager.init();
|
||||||
overview.init();
|
overview.init();
|
||||||
|
|
||||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
||||||
false, -1, 1);
|
|
||||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
global.display.connect('overlay-key', Lang.bind(overview, function () {
|
||||||
|
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
|
||||||
|
overview.toggle();
|
||||||
|
}));
|
||||||
|
|
||||||
// Provide the bus object for gnome-session to
|
// Provide the bus object for gnome-session to
|
||||||
// initiate logouts.
|
// initiate logouts.
|
||||||
@ -179,17 +195,6 @@ function _initializeUI() {
|
|||||||
Scripting.runPerfScript(module, perfOutput);
|
Scripting.runPerfScript(module, perfOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
_overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
|
|
||||||
_overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
|
|
||||||
|
|
||||||
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
|
||||||
|
|
||||||
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
|
||||||
global.screen.connect('window-left-monitor', _windowLeftMonitor);
|
|
||||||
global.screen.connect('restacked', _windowsRestacked);
|
|
||||||
|
|
||||||
_nWorkspacesChanged();
|
|
||||||
|
|
||||||
ExtensionDownloader.init();
|
ExtensionDownloader.init();
|
||||||
ExtensionSystem.init();
|
ExtensionSystem.init();
|
||||||
|
|
||||||
@ -203,190 +208,12 @@ function _initializeUI() {
|
|||||||
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
||||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
}
|
}
|
||||||
|
if (screenShield) {
|
||||||
|
screenShield.lockIfWasLocked();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let _workspaces = [];
|
|
||||||
let _checkWorkspacesId = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When the last window closed on a workspace is a dialog or splash
|
|
||||||
* screen, we assume that it might be an initial window shown before
|
|
||||||
* the main window of an application, and give the app a grace period
|
|
||||||
* where it can map another window before we remove the workspace.
|
|
||||||
*/
|
|
||||||
const LAST_WINDOW_GRACE_TIME = 1000;
|
|
||||||
|
|
||||||
function _checkWorkspaces() {
|
|
||||||
let i;
|
|
||||||
let emptyWorkspaces = [];
|
|
||||||
|
|
||||||
if (!Meta.prefs_get_dynamic_workspaces()) {
|
|
||||||
_checkWorkspacesId = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < _workspaces.length; i++) {
|
|
||||||
let lastRemoved = _workspaces[i]._lastRemovedWindow;
|
|
||||||
if ((lastRemoved &&
|
|
||||||
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
|
||||||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
|
||||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
|
||||||
_workspaces[i]._keepAliveId)
|
|
||||||
emptyWorkspaces[i] = false;
|
|
||||||
else
|
|
||||||
emptyWorkspaces[i] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
|
||||||
for (i = 0; i < sequences.length; i++) {
|
|
||||||
let index = sequences[i].get_workspace();
|
|
||||||
if (index >= 0 && index <= global.screen.n_workspaces)
|
|
||||||
emptyWorkspaces[index] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let windows = global.get_window_actors();
|
|
||||||
for (i = 0; i < windows.length; i++) {
|
|
||||||
let win = windows[i];
|
|
||||||
|
|
||||||
if (win.get_meta_window().is_on_all_workspaces())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let workspaceIndex = win.get_workspace();
|
|
||||||
emptyWorkspaces[workspaceIndex] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have an empty workspace at the end, add one
|
|
||||||
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
|
||||||
global.screen.append_new_workspace(false, global.get_current_time());
|
|
||||||
emptyWorkspaces.push(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
||||||
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
|
|
||||||
activeWorkspaceIndex < emptyWorkspaces.length - 1);
|
|
||||||
// Don't enter the overview when removing multiple empty workspaces at startup
|
|
||||||
let showOverview = (removingCurrentWorkspace &&
|
|
||||||
!emptyWorkspaces.every(function(x) { return x; }));
|
|
||||||
|
|
||||||
if (removingCurrentWorkspace) {
|
|
||||||
// "Merge" the empty workspace we are removing with the one at the end
|
|
||||||
wm.blockAnimations();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
|
||||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
|
||||||
if (emptyWorkspaces[i])
|
|
||||||
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removingCurrentWorkspace) {
|
|
||||||
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
|
||||||
wm.unblockAnimations();
|
|
||||||
|
|
||||||
if (!overview.visible && showOverview)
|
|
||||||
overview.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
_checkWorkspacesId = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keepWorkspaceAlive(workspace, duration) {
|
|
||||||
if (workspace._keepAliveId)
|
|
||||||
Mainloop.source_remove(workspace._keepAliveId);
|
|
||||||
|
|
||||||
workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
|
|
||||||
workspace._keepAliveId = 0;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowRemoved(workspace, window) {
|
|
||||||
workspace._lastRemovedWindow = window;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
|
|
||||||
if (workspace._lastRemovedWindow == window) {
|
|
||||||
workspace._lastRemovedWindow = null;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
|
|
||||||
// If the window left the primary monitor, that
|
|
||||||
// might make that workspace empty
|
|
||||||
if (monitorIndex == layoutManager.primaryIndex)
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
|
|
||||||
// If the window entered the primary monitor, that
|
|
||||||
// might make that workspace non-empty
|
|
||||||
if (monitorIndex == layoutManager.primaryIndex)
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowsRestacked() {
|
|
||||||
// Figure out where the pointer is in case we lost track of
|
|
||||||
// it during a grab. (In particular, if a trayicon popup menu
|
|
||||||
// is dismissed, see if we need to close the message tray.)
|
|
||||||
global.sync_pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _queueCheckWorkspaces() {
|
|
||||||
if (_checkWorkspacesId == 0)
|
|
||||||
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _nWorkspacesChanged() {
|
|
||||||
let oldNumWorkspaces = _workspaces.length;
|
|
||||||
let newNumWorkspaces = global.screen.n_workspaces;
|
|
||||||
|
|
||||||
if (oldNumWorkspaces == newNumWorkspaces)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
let lostWorkspaces = [];
|
|
||||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
||||||
let w;
|
|
||||||
|
|
||||||
// Assume workspaces are only added at the end
|
|
||||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
|
||||||
_workspaces[w] = global.screen.get_workspace_by_index(w);
|
|
||||||
|
|
||||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
||||||
let workspace = _workspaces[w];
|
|
||||||
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
|
|
||||||
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Assume workspaces are only removed sequentially
|
|
||||||
// (e.g. 2,3,4 - not 2,4,7)
|
|
||||||
let removedIndex;
|
|
||||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
||||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
|
||||||
let workspace = global.screen.get_workspace_by_index(w);
|
|
||||||
if (_workspaces[w] != workspace) {
|
|
||||||
removedIndex = w;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
|
|
||||||
lostWorkspaces.forEach(function(workspace) {
|
|
||||||
workspace.disconnect(workspace._windowAddedId);
|
|
||||||
workspace.disconnect(workspace._windowRemovedId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _loadDefaultStylesheet() {
|
function _loadDefaultStylesheet() {
|
||||||
if (!sessionMode.isPrimary)
|
if (!sessionMode.isPrimary)
|
||||||
return;
|
return;
|
||||||
@ -437,7 +264,8 @@ function loadTheme() {
|
|||||||
if (_cssStylesheet != null)
|
if (_cssStylesheet != null)
|
||||||
cssStylesheet = _cssStylesheet;
|
cssStylesheet = _cssStylesheet;
|
||||||
|
|
||||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
|
||||||
|
default_stylesheet: _defaultCssStylesheet });
|
||||||
|
|
||||||
if (previousTheme) {
|
if (previousTheme) {
|
||||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||||
@ -528,8 +356,6 @@ function pushModal(actor, params) {
|
|||||||
Meta.disable_unredirect_for_screen(global.screen);
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
|
||||||
|
|
||||||
modalCount += 1;
|
modalCount += 1;
|
||||||
let actorDestroyId = actor.connect('destroy', function() {
|
let actorDestroyId = actor.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
@ -578,7 +404,6 @@ function popModal(actor, timestamp) {
|
|||||||
if (focusIndex < 0) {
|
if (focusIndex < 0) {
|
||||||
global.stage.set_key_focus(null);
|
global.stage.set_key_focus(null);
|
||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
|
||||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
|
|
||||||
throw new Error('incorrect pop');
|
throw new Error('incorrect pop');
|
||||||
@ -626,7 +451,6 @@ function popModal(actor, timestamp) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,65 @@ function _fixMarkup(text, allowMarkup) {
|
|||||||
return GLib.markup_escape_text(text, -1);
|
return GLib.markup_escape_text(text, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FocusGrabber = new Lang.Class({
|
||||||
|
Name: 'FocusGrabber',
|
||||||
|
|
||||||
|
_init: function(actor) {
|
||||||
|
this._actor = actor;
|
||||||
|
this._prevKeyFocusActor = null;
|
||||||
|
this._focusActorChangedId = 0;
|
||||||
|
this._focused = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
grabFocus: function() {
|
||||||
|
if (this._focused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._prevFocusedWindow = global.display.focus_window;
|
||||||
|
this._prevKeyFocusActor = global.stage.get_key_focus();
|
||||||
|
|
||||||
|
this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
|
||||||
|
|
||||||
|
if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||||
|
this._actor.grab_key_focus();
|
||||||
|
|
||||||
|
this._focused = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_focusUngrabbed: function() {
|
||||||
|
if (!this._focused)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._focusActorChangedId > 0) {
|
||||||
|
global.stage.disconnect(this._focusActorChangedId);
|
||||||
|
this._focusActorChangedId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._focused = false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_focusActorChanged: function() {
|
||||||
|
let focusedActor = global.stage.get_key_focus();
|
||||||
|
if (!focusedActor || !this._actor.contains(focusedActor))
|
||||||
|
this._focusUngrabbed();
|
||||||
|
},
|
||||||
|
|
||||||
|
ungrabFocus: function() {
|
||||||
|
if (!this._focusUngrabbed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._prevKeyFocusActor) {
|
||||||
|
global.stage.set_key_focus(this._prevKeyFocusActor);
|
||||||
|
this._prevKeyFocusActor = null;
|
||||||
|
} else {
|
||||||
|
let focusedActor = global.stage.get_key_focus();
|
||||||
|
if (focusedActor && this._actor.contains(focusedActor))
|
||||||
|
global.stage.set_key_focus(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const URLHighlighter = new Lang.Class({
|
const URLHighlighter = new Lang.Class({
|
||||||
Name: 'URLHighlighter',
|
Name: 'URLHighlighter',
|
||||||
|
|
||||||
@ -1365,6 +1424,10 @@ const SummaryItem = new Lang.Class({
|
|||||||
this.notificationStackWidget.add_actor(this.notificationStackView);
|
this.notificationStackWidget.add_actor(this.notificationStackView);
|
||||||
|
|
||||||
this.closeButton = Util.makeCloseButton();
|
this.closeButton = Util.makeCloseButton();
|
||||||
|
this.closeButton.connect('clicked', Lang.bind(this, function() {
|
||||||
|
source.destroy();
|
||||||
|
source.emit('done-displaying-content');
|
||||||
|
}));
|
||||||
this.notificationStackWidget.add_actor(this.closeButton);
|
this.notificationStackWidget.add_actor(this.closeButton);
|
||||||
this._stackedNotifications = [];
|
this._stackedNotifications = [];
|
||||||
|
|
||||||
@ -1530,11 +1593,7 @@ const MessageTrayContextMenu = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateClearSensitivity: function() {
|
_updateClearSensitivity: function() {
|
||||||
let sources = this._tray.getSources();
|
this._clearItem.setSensitive(this._tray.clearableCount > 0);
|
||||||
sources = sources.filter(function(source) {
|
|
||||||
return !source.trayIcon && !source.isChat && !source.resident;
|
|
||||||
});
|
|
||||||
this._clearItem.setSensitive(sources.length > 0);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setPosition: function(x, y) {
|
setPosition: function(x, y) {
|
||||||
@ -1557,28 +1616,28 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
this.actor = new St.Widget({ name: 'message-tray',
|
this.actor = new St.Widget({ name: 'message-tray',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
track_hover: true,
|
|
||||||
layout_manager: new Clutter.BinLayout(),
|
layout_manager: new Clutter.BinLayout(),
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
y_expand: true,
|
y_expand: true,
|
||||||
y_align: Clutter.ActorAlign.START,
|
y_align: Clutter.ActorAlign.START,
|
||||||
});
|
});
|
||||||
this.actor.connect('notify::hover', Lang.bind(this, this._onTrayHoverChanged));
|
|
||||||
|
|
||||||
this._notificationWidget = new St.Widget({ name: 'notification-container',
|
this._notificationWidget = new St.Widget({ name: 'notification-container',
|
||||||
|
reactive: true,
|
||||||
|
track_hover: true,
|
||||||
y_align: Clutter.ActorAlign.START,
|
y_align: Clutter.ActorAlign.START,
|
||||||
x_align: Clutter.ActorAlign.CENTER,
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
y_expand: true,
|
y_expand: true,
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
layout_manager: new Clutter.BinLayout() });
|
layout_manager: new Clutter.BinLayout() });
|
||||||
this._notificationWidget.connect('key-release-event', Lang.bind(this, this._onNotificationKeyRelease));
|
this._notificationWidget.connect('key-release-event', Lang.bind(this, this._onNotificationKeyRelease));
|
||||||
|
this._notificationWidget.connect('notify::hover', Lang.bind(this, this._onNotificationHoverChanged));
|
||||||
this.actor.add_actor(this._notificationWidget);
|
|
||||||
|
|
||||||
this._notificationBin = new St.Bin({ y_expand: true });
|
this._notificationBin = new St.Bin({ y_expand: true });
|
||||||
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
|
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
|
||||||
this._notificationWidget.add_actor(this._notificationBin);
|
this._notificationWidget.add_actor(this._notificationBin);
|
||||||
this._notificationWidget.hide();
|
this._notificationWidget.hide();
|
||||||
|
this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
|
||||||
this._notificationQueue = [];
|
this._notificationQueue = [];
|
||||||
this._notification = null;
|
this._notification = null;
|
||||||
this._notificationClickedId = 0;
|
this._notificationClickedId = 0;
|
||||||
@ -1633,19 +1692,19 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
this._trayState = State.HIDDEN;
|
this._trayState = State.HIDDEN;
|
||||||
this._traySummoned = false;
|
this._traySummoned = false;
|
||||||
this._useLongerTrayLeftTimeout = false;
|
this._useLongerNotificationLeftTimeout = false;
|
||||||
this._trayLeftTimeoutId = 0;
|
this._trayLeftTimeoutId = 0;
|
||||||
|
|
||||||
// pointerInTray is sort of a misnomer -- it tracks whether
|
// pointerInNotification is sort of a misnomer -- it tracks whether
|
||||||
// a message tray notification should expand. The value is
|
// a message tray notification should expand. The value is
|
||||||
// partially driven by the hover state of the tray, but has
|
// partially driven by the hover state of the notification, but has
|
||||||
// a lot of complex state related to timeouts and the current
|
// a lot of complex state related to timeouts and the current
|
||||||
// state of the pointer when a notification pops up.
|
// state of the pointer when a notification pops up.
|
||||||
this._pointerInTray = false;
|
this._pointerInNotification = false;
|
||||||
|
|
||||||
// This tracks this.actor.hover and is used to fizzle
|
// This tracks this._notificationWidget.hover and is used to fizzle
|
||||||
// out non-changing hover notifications in onTrayHoverChanged.
|
// out non-changing hover notifications in onNotificationHoverChanged.
|
||||||
this._trayHovered = false;
|
this._notificationHovered = false;
|
||||||
|
|
||||||
this._keyboardVisible = false;
|
this._keyboardVisible = false;
|
||||||
this._notificationClosed = false;
|
this._notificationClosed = false;
|
||||||
@ -1657,23 +1716,29 @@ const MessageTray = new Lang.Class({
|
|||||||
this._desktopCloneState = State.HIDDEN;
|
this._desktopCloneState = State.HIDDEN;
|
||||||
this._notificationRemoved = false;
|
this._notificationRemoved = false;
|
||||||
this._reNotifyAfterHideNotification = null;
|
this._reNotifyAfterHideNotification = null;
|
||||||
this._inFullscreen = false;
|
|
||||||
this._desktopClone = null;
|
this._desktopClone = null;
|
||||||
this._inCtrlAltTab = false;
|
this._inCtrlAltTab = false;
|
||||||
|
|
||||||
this._lightbox = new Lightbox.Lightbox(global.overlay_group,
|
this.clearableCount = 0;
|
||||||
{ inhibitEvents: true,
|
|
||||||
fadeInTime: ANIMATION_TIME,
|
this._lightboxes = [];
|
||||||
fadeOutTime: ANIMATION_TIME,
|
let lightboxContainers = [global.window_group,
|
||||||
fadeFactor: 0.2
|
Main.layoutManager.overviewGroup];
|
||||||
});
|
for (let i = 0; i < lightboxContainers.length; i++)
|
||||||
|
this._lightboxes.push(new Lightbox.Lightbox(lightboxContainers[i],
|
||||||
|
{ inhibitEvents: true,
|
||||||
|
fadeInTime: ANIMATION_TIME,
|
||||||
|
fadeOutTime: ANIMATION_TIME,
|
||||||
|
fadeFactor: 0.2
|
||||||
|
}));
|
||||||
|
|
||||||
Main.layoutManager.trayBox.add_actor(this.actor);
|
Main.layoutManager.trayBox.add_actor(this.actor);
|
||||||
|
Main.layoutManager.trayBox.add_actor(this._notificationWidget);
|
||||||
Main.layoutManager.trackChrome(this.actor);
|
Main.layoutManager.trackChrome(this.actor);
|
||||||
Main.layoutManager.trackChrome(this._notificationWidget);
|
Main.layoutManager.trackChrome(this._notificationWidget);
|
||||||
Main.layoutManager.trackChrome(this._closeButton);
|
Main.layoutManager.trackChrome(this._closeButton);
|
||||||
|
|
||||||
Main.layoutManager.connect('fullscreen-changed', Lang.bind(this, this._updateState));
|
global.screen.connect('in-fullscreen-changed', Lang.bind(this, this._updateState));
|
||||||
Main.layoutManager.connect('hot-corners-changed', Lang.bind(this, this._hotCornersChanged));
|
Main.layoutManager.connect('hot-corners-changed', Lang.bind(this, this._hotCornersChanged));
|
||||||
|
|
||||||
// If the overview shows or hides while we're in
|
// If the overview shows or hides while we're in
|
||||||
@ -1721,9 +1786,6 @@ const MessageTray = new Lang.Class({
|
|||||||
this.actor.add_action(clickAction);
|
this.actor.add_action(clickAction);
|
||||||
|
|
||||||
clickAction.connect('clicked', Lang.bind(this, function(action) {
|
clickAction.connect('clicked', Lang.bind(this, function(action) {
|
||||||
if (this._trayState != State.SHOWN)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let button = action.get_button();
|
let button = action.get_button();
|
||||||
if (button == 3)
|
if (button == 3)
|
||||||
this._openContextMenu();
|
this._openContextMenu();
|
||||||
@ -1734,7 +1796,7 @@ const MessageTray = new Lang.Class({
|
|||||||
clickAction.connect('long-press', Lang.bind(this, function(action, actor, state) {
|
clickAction.connect('long-press', Lang.bind(this, function(action, actor, state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Clutter.LongPressState.QUERY:
|
case Clutter.LongPressState.QUERY:
|
||||||
return this._trayState == State.SHOWN;
|
return true;
|
||||||
case Clutter.LongPressState.ACTIVATE:
|
case Clutter.LongPressState.ACTIVATE:
|
||||||
this._openContextMenu();
|
this._openContextMenu();
|
||||||
}
|
}
|
||||||
@ -1750,7 +1812,6 @@ const MessageTray = new Lang.Class({
|
|||||||
let [x, y, mask] = global.get_pointer();
|
let [x, y, mask] = global.get_pointer();
|
||||||
this._contextMenu.setPosition(Math.round(x), Math.round(y));
|
this._contextMenu.setPosition(Math.round(x), Math.round(y));
|
||||||
this._grabHelper.grab({ actor: this._contextMenu.actor,
|
this._grabHelper.grab({ actor: this._contextMenu.actor,
|
||||||
grabFocus: true,
|
|
||||||
onUngrab: Lang.bind(this, function () {
|
onUngrab: Lang.bind(this, function () {
|
||||||
this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
|
this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
|
||||||
})
|
})
|
||||||
@ -1796,12 +1857,12 @@ const MessageTray = new Lang.Class({
|
|||||||
y == monitor.y + monitor.height - 1);
|
y == monitor.y + monitor.height - 1);
|
||||||
if (shouldDwell) {
|
if (shouldDwell) {
|
||||||
// We only set up dwell timeout when the user is not hovering over the tray
|
// We only set up dwell timeout when the user is not hovering over the tray
|
||||||
// (!this.actor.hover). This avoids bringing up the message tray after the
|
// (!this._notificationHovered). This avoids bringing up the message tray after the
|
||||||
// user clicks on a notification with the pointer on the bottom pixel
|
// user clicks on a notification with the pointer on the bottom pixel
|
||||||
// of the monitor. The _trayDwelling variable is used so that we only try to
|
// of the monitor. The _trayDwelling variable is used so that we only try to
|
||||||
// fire off one tray dwell - if it fails (because, say, the user has the mouse down),
|
// fire off one tray dwell - if it fails (because, say, the user has the mouse down),
|
||||||
// we don't try again until the user moves the mouse up and down again.
|
// we don't try again until the user moves the mouse up and down again.
|
||||||
if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0) {
|
if (!this._trayDwelling && !this._notificationHovered && this._trayDwellTimeoutId == 0) {
|
||||||
// Save the interaction timestamp so we can detect user input
|
// Save the interaction timestamp so we can detect user input
|
||||||
let focusWindow = global.display.focus_window;
|
let focusWindow = global.display.focus_window;
|
||||||
this._trayDwellUserTime = focusWindow ? focusWindow.user_time : 0;
|
this._trayDwellUserTime = focusWindow ? focusWindow.user_time : 0;
|
||||||
@ -1858,9 +1919,8 @@ const MessageTray = new Lang.Class({
|
|||||||
_closeNotification: function() {
|
_closeNotification: function() {
|
||||||
if (this._notificationState == State.SHOWN) {
|
if (this._notificationState == State.SHOWN) {
|
||||||
this._closeButton.hide();
|
this._closeButton.hide();
|
||||||
this._notificationClosed = true;
|
this._notification.emit('done-displaying');
|
||||||
this._updateState();
|
this._notification.destroy();
|
||||||
this._notificationClosed = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1899,6 +1959,9 @@ const MessageTray = new Lang.Class({
|
|||||||
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
|
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!source.trayIcon && !source.isChat && !source.resident)
|
||||||
|
this.clearableCount++;
|
||||||
|
|
||||||
this._sources.set(source, obj);
|
this._sources.set(source, obj);
|
||||||
|
|
||||||
obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify));
|
obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify));
|
||||||
@ -1940,6 +2003,9 @@ const MessageTray = new Lang.Class({
|
|||||||
if (source.isChat)
|
if (source.isChat)
|
||||||
this._chatSummaryItemsCount--;
|
this._chatSummaryItemsCount--;
|
||||||
|
|
||||||
|
if (!source.trayIcon && !source.isChat && !source.resident)
|
||||||
|
this.clearableCount--;
|
||||||
|
|
||||||
source.disconnect(obj.notifyId);
|
source.disconnect(obj.notifyId);
|
||||||
source.disconnect(obj.destroyId);
|
source.disconnect(obj.destroyId);
|
||||||
source.disconnect(obj.mutedChangedId);
|
source.disconnect(obj.mutedChangedId);
|
||||||
@ -1995,7 +2061,6 @@ const MessageTray = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let index = this._notificationQueue.indexOf(notification);
|
let index = this._notificationQueue.indexOf(notification);
|
||||||
notification.destroy();
|
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
this._notificationQueue.splice(index, 1);
|
this._notificationQueue.splice(index, 1);
|
||||||
},
|
},
|
||||||
@ -2024,7 +2089,6 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
this._traySummoned = false;
|
this._traySummoned = false;
|
||||||
this.actor.set_hover(false);
|
|
||||||
this._updateState();
|
this._updateState();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2091,25 +2155,21 @@ const MessageTray = new Lang.Class({
|
|||||||
this._grabHelper.addActor(corner.actor);
|
this._grabHelper.addActor(corner.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onTrayHoverChanged: function() {
|
_onNotificationHoverChanged: function() {
|
||||||
if (this.actor.hover == this._trayHovered)
|
if (this._notificationWidget.hover == this._notificationHovered)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._trayHovered = this.actor.hover;
|
this._notificationHovered = this._notificationWidget.hover;
|
||||||
if (this._trayHovered) {
|
if (this._notificationHovered) {
|
||||||
// No dwell inside notifications at the bottom of the screen
|
// No dwell inside notifications at the bottom of the screen
|
||||||
this._cancelTrayDwell();
|
this._cancelTrayDwell();
|
||||||
|
|
||||||
// Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden.
|
this._useLongerNotificationLeftTimeout = false;
|
||||||
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
|
if (this._notificationLeftTimeoutId) {
|
||||||
return;
|
Mainloop.source_remove(this._notificationLeftTimeoutId);
|
||||||
|
this._notificationLeftTimeoutId = 0;
|
||||||
this._useLongerTrayLeftTimeout = false;
|
this._notificationLeftMouseX = -1;
|
||||||
if (this._trayLeftTimeoutId) {
|
this._notificationLeftMouseY = -1;
|
||||||
Mainloop.source_remove(this._trayLeftTimeoutId);
|
|
||||||
this._trayLeftTimeoutId = 0;
|
|
||||||
this._trayLeftMouseX = -1;
|
|
||||||
this._trayLeftMouseY = -1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2118,32 +2178,32 @@ const MessageTray = new Lang.Class({
|
|||||||
global.stage.get_actor_at_pos(Clutter.PickMode.ALL, this._showNotificationMouseX, this._showNotificationMouseY);
|
global.stage.get_actor_at_pos(Clutter.PickMode.ALL, this._showNotificationMouseX, this._showNotificationMouseY);
|
||||||
this._showNotificationMouseX = -1;
|
this._showNotificationMouseX = -1;
|
||||||
this._showNotificationMouseY = -1;
|
this._showNotificationMouseY = -1;
|
||||||
// Don't set this._pointerInTray to true if the pointer was initially in the area where the notification
|
// Don't set this._pointerInNotification to true if the pointer was initially in the area where the notification
|
||||||
// popped up. That way we will not be expanding notifications that happen to pop up over the pointer
|
// popped up. That way we will not be expanding notifications that happen to pop up over the pointer
|
||||||
// automatically. Instead, the user is able to expand the notification by mousing away from it and then
|
// automatically. Instead, the user is able to expand the notification by mousing away from it and then
|
||||||
// mousing back in. Because this is an expected action, we set the boolean flag that indicates that a longer
|
// mousing back in. Because this is an expected action, we set the boolean flag that indicates that a longer
|
||||||
// timeout should be used before popping down the notification.
|
// timeout should be used before popping down the notification.
|
||||||
if (this.actor.contains(actorAtShowNotificationPosition)) {
|
if (this.actor.contains(actorAtShowNotificationPosition)) {
|
||||||
this._useLongerTrayLeftTimeout = true;
|
this._useLongerNotificationLeftTimeout = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._pointerInTray = true;
|
this._pointerInNotification = true;
|
||||||
this._updateState();
|
this._updateState();
|
||||||
} else {
|
} else {
|
||||||
// We record the position of the mouse the moment it leaves the tray. These coordinates are used in
|
// We record the position of the mouse the moment it leaves the tray. These coordinates are used in
|
||||||
// this._onTrayLeftTimeout() to determine if the mouse has moved far enough during the initial timeout for us
|
// this._onNotificationLeftTimeout() to determine if the mouse has moved far enough during the initial timeout for us
|
||||||
// to consider that the user intended to leave the tray and therefore hide the tray. If the mouse is still
|
// to consider that the user intended to leave the tray and therefore hide the tray. If the mouse is still
|
||||||
// close to its previous position, we extend the timeout once.
|
// close to its previous position, we extend the timeout once.
|
||||||
let [x, y, mods] = global.get_pointer();
|
let [x, y, mods] = global.get_pointer();
|
||||||
this._trayLeftMouseX = x;
|
this._notificationLeftMouseX = x;
|
||||||
this._trayLeftMouseY = y;
|
this._notificationLeftMouseY = y;
|
||||||
|
|
||||||
// We wait just a little before hiding the message tray in case the user quickly moves the mouse back into it.
|
// We wait just a little before hiding the message tray in case the user quickly moves the mouse back into it.
|
||||||
// We wait for a longer period if the notification popped up where the mouse pointer was already positioned.
|
// We wait for a longer period if the notification popped up where the mouse pointer was already positioned.
|
||||||
// That gives the user more time to mouse away from the notification and mouse back in in order to expand it.
|
// That gives the user more time to mouse away from the notification and mouse back in in order to expand it.
|
||||||
let timeout = this._useLongerTrayLeftTimeout ? LONGER_HIDE_TIMEOUT * 1000 : HIDE_TIMEOUT * 1000;
|
let timeout = this._useLongerNotificationLeftTimeout ? LONGER_HIDE_TIMEOUT * 1000 : HIDE_TIMEOUT * 1000;
|
||||||
this._trayLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onTrayLeftTimeout));
|
this._notificationLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onNotificationLeftTimeout));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2167,22 +2227,22 @@ const MessageTray = new Lang.Class({
|
|||||||
this._updateState();
|
this._updateState();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onTrayLeftTimeout: function() {
|
_onNotificationLeftTimeout: function() {
|
||||||
let [x, y, mods] = global.get_pointer();
|
let [x, y, mods] = global.get_pointer();
|
||||||
// We extend the timeout once if the mouse moved no further than MOUSE_LEFT_ACTOR_THRESHOLD to either side or up.
|
// We extend the timeout once if the mouse moved no further than MOUSE_LEFT_ACTOR_THRESHOLD to either side or up.
|
||||||
// We don't check how far down the mouse moved because any point above the tray, but below the exit coordinate,
|
// We don't check how far down the mouse moved because any point above the tray, but below the exit coordinate,
|
||||||
// is close to the tray.
|
// is close to the tray.
|
||||||
if (this._trayLeftMouseX > -1 &&
|
if (this._notificationLeftMouseX > -1 &&
|
||||||
y > this._trayLeftMouseY - MOUSE_LEFT_ACTOR_THRESHOLD &&
|
y > this._notificationLeftMouseY - MOUSE_LEFT_ACTOR_THRESHOLD &&
|
||||||
x < this._trayLeftMouseX + MOUSE_LEFT_ACTOR_THRESHOLD &&
|
x < this._notificationLeftMouseX + MOUSE_LEFT_ACTOR_THRESHOLD &&
|
||||||
x > this._trayLeftMouseX - MOUSE_LEFT_ACTOR_THRESHOLD) {
|
x > this._notificationLeftMouseX - MOUSE_LEFT_ACTOR_THRESHOLD) {
|
||||||
this._trayLeftMouseX = -1;
|
this._notificationLeftMouseX = -1;
|
||||||
this._trayLeftTimeoutId = Mainloop.timeout_add(LONGER_HIDE_TIMEOUT * 1000,
|
this._notificationLeftTimeoutId = Mainloop.timeout_add(LONGER_HIDE_TIMEOUT * 1000,
|
||||||
Lang.bind(this, this._onTrayLeftTimeout));
|
Lang.bind(this, this._onNotificationLeftTimeout));
|
||||||
} else {
|
} else {
|
||||||
this._trayLeftTimeoutId = 0;
|
this._notificationLeftTimeoutId = 0;
|
||||||
this._useLongerTrayLeftTimeout = false;
|
this._useLongerNotificationLeftTimeout = false;
|
||||||
this._pointerInTray = false;
|
this._pointerInNotification = false;
|
||||||
this._updateNotificationTimeout(0);
|
this._updateNotificationTimeout(0);
|
||||||
this._updateState();
|
this._updateState();
|
||||||
}
|
}
|
||||||
@ -2190,7 +2250,7 @@ const MessageTray = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_escapeTray: function() {
|
_escapeTray: function() {
|
||||||
this._pointerInTray = false;
|
this._pointerInNotification = false;
|
||||||
this._traySummoned = false;
|
this._traySummoned = false;
|
||||||
this._setClickedSummaryItem(null);
|
this._setClickedSummaryItem(null);
|
||||||
this._updateNotificationTimeout(0);
|
this._updateNotificationTimeout(0);
|
||||||
@ -2199,26 +2259,33 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
// All of the logic for what happens when occurs here; the various
|
// All of the logic for what happens when occurs here; the various
|
||||||
// event handlers merely update variables such as
|
// event handlers merely update variables such as
|
||||||
// 'this._pointerInTray', 'this._traySummoned', etc, and
|
// 'this._pointerInNotification', 'this._traySummoned', etc, and
|
||||||
// _updateState() figures out what (if anything) needs to be done
|
// _updateState() figures out what (if anything) needs to be done
|
||||||
// at the present time.
|
// at the present time.
|
||||||
_updateState: function() {
|
_updateState: function() {
|
||||||
// Notifications
|
// Notifications
|
||||||
let notificationQueue = this._notificationQueue;
|
let notificationQueue = this._notificationQueue.filter(function(n) {
|
||||||
|
return !n.acknowledged;
|
||||||
|
});
|
||||||
|
let hasNotifications = Main.sessionMode.hasNotifications;
|
||||||
|
|
||||||
|
this._notificationQueue = notificationQueue;
|
||||||
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
|
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
|
||||||
let notificationForFeedback = notificationQueue.length > 0 && notificationQueue[0].forFeedback;
|
let notificationForFeedback = notificationQueue.length > 0 && notificationQueue[0].forFeedback;
|
||||||
let notificationsLimited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
|
let notificationsLimited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
|
||||||
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && Main.sessionMode.hasNotifications;
|
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && hasNotifications;
|
||||||
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
|
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
|
||||||
let notificationPinned = this._pointerInTray && !this._notificationRemoved;
|
let notificationPinned = this._pointerInNotification && !this._notificationRemoved;
|
||||||
let notificationExpanded = this._notification && this._notification.expanded;
|
let notificationExpanded = this._notification && this._notification.expanded;
|
||||||
let notificationExpired = this._notificationTimeoutId == 0 &&
|
let notificationExpired = this._notificationTimeoutId == 0 &&
|
||||||
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
|
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
|
||||||
!(this._notification && this._notification.focused) &&
|
!(this._notification && this._notification.focused) &&
|
||||||
!this._pointerInTray;
|
!this._pointerInNotification;
|
||||||
let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification;
|
let notificationLockedOut = !hasNotifications && this._notification;
|
||||||
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed;
|
let notificationMustClose = (this._notificationRemoved || notificationLockedOut ||
|
||||||
let canShowNotification = notificationsPending && this._trayState == State.HIDDEN;
|
(notificationExpired && this._userActiveWhileNotificationShown) ||
|
||||||
|
this._notificationClosed || this._traySummoned);
|
||||||
|
let canShowNotification = notificationsPending && this._trayState == State.HIDDEN && !this._traySummoned;
|
||||||
|
|
||||||
if (this._notificationState == State.HIDDEN) {
|
if (this._notificationState == State.HIDDEN) {
|
||||||
if (canShowNotification)
|
if (canShowNotification)
|
||||||
@ -2232,12 +2299,6 @@ const MessageTray = new Lang.Class({
|
|||||||
this._ensureNotificationFocused();
|
this._ensureNotificationFocused();
|
||||||
}
|
}
|
||||||
|
|
||||||
let notificationsVisible = this._notificationState != State.HIDDEN;
|
|
||||||
let notificationsDone = !notificationsVisible && !notificationsPending;
|
|
||||||
|
|
||||||
let mustHideTray = ((notificationsPending && notificationUrgent)
|
|
||||||
|| notificationsVisible || !Main.sessionMode.hasNotifications);
|
|
||||||
|
|
||||||
// Summary notification
|
// Summary notification
|
||||||
let haveClickedSummaryItem = this._clickedSummaryItem != null;
|
let haveClickedSummaryItem = this._clickedSummaryItem != null;
|
||||||
let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification &&
|
let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification &&
|
||||||
@ -2245,10 +2306,15 @@ const MessageTray = new Lang.Class({
|
|||||||
let canShowSummaryBoxPointer = this._trayState == State.SHOWN;
|
let canShowSummaryBoxPointer = this._trayState == State.SHOWN;
|
||||||
// We only have sources with empty notification stacks for legacy tray icons. Currently, we never attempt
|
// We only have sources with empty notification stacks for legacy tray icons. Currently, we never attempt
|
||||||
// to show notifications for legacy tray icons, but this would be necessary if we did.
|
// to show notifications for legacy tray icons, but this would be necessary if we did.
|
||||||
let requestedNotificationStackIsEmpty = (this._clickedSummaryItemMouseButton == 1 && this._clickedSummaryItem.source.notifications.length == 0);
|
let requestedNotificationStackIsEmpty = (haveClickedSummaryItem &&
|
||||||
let wrongSummaryNotificationStack = (this._clickedSummaryItemMouseButton == 1 &&
|
this._clickedSummaryItemMouseButton == 1 &&
|
||||||
|
this._clickedSummaryItem.source.notifications.length == 0);
|
||||||
|
let wrongSummaryNotificationStack = (haveClickedSummaryItem &&
|
||||||
|
this._clickedSummaryItemMouseButton == 1 &&
|
||||||
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.notificationStackWidget);
|
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.notificationStackWidget);
|
||||||
let wrongSummaryRightClickMenu = (this._clickedSummaryItemMouseButton == 3 &&
|
let wrongSummaryRightClickMenu = (haveClickedSummaryItem &&
|
||||||
|
this._clickedSummaryItemMouseButton == 3 &&
|
||||||
|
this._clickedSummaryItem.rightClickMenu != null &&
|
||||||
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.rightClickMenu);
|
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.rightClickMenu);
|
||||||
let wrongSummaryBoxPointer = (haveClickedSummaryItem &&
|
let wrongSummaryBoxPointer = (haveClickedSummaryItem &&
|
||||||
(wrongSummaryNotificationStack || wrongSummaryRightClickMenu));
|
(wrongSummaryNotificationStack || wrongSummaryRightClickMenu));
|
||||||
@ -2257,7 +2323,7 @@ const MessageTray = new Lang.Class({
|
|||||||
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
|
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
|
||||||
this._showSummaryBoxPointer();
|
this._showSummaryBoxPointer();
|
||||||
} else if (this._summaryBoxPointerState == State.SHOWN) {
|
} else if (this._summaryBoxPointerState == State.SHOWN) {
|
||||||
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideTray) {
|
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || !hasNotifications) {
|
||||||
this._hideSummaryBoxPointer();
|
this._hideSummaryBoxPointer();
|
||||||
if (wrongSummaryBoxPointer)
|
if (wrongSummaryBoxPointer)
|
||||||
this._showSummaryBoxPointer();
|
this._showSummaryBoxPointer();
|
||||||
@ -2267,7 +2333,7 @@ const MessageTray = new Lang.Class({
|
|||||||
// Tray itself
|
// Tray itself
|
||||||
let trayIsVisible = (this._trayState == State.SHOWING ||
|
let trayIsVisible = (this._trayState == State.SHOWING ||
|
||||||
this._trayState == State.SHOWN);
|
this._trayState == State.SHOWN);
|
||||||
let trayShouldBeVisible = this._traySummoned && !this._keyboardVisible && !mustHideTray;
|
let trayShouldBeVisible = this._traySummoned && !this._keyboardVisible && hasNotifications;
|
||||||
if (!trayIsVisible && trayShouldBeVisible)
|
if (!trayIsVisible && trayShouldBeVisible)
|
||||||
trayShouldBeVisible = this._showTray();
|
trayShouldBeVisible = this._showTray();
|
||||||
else if (trayIsVisible && !trayShouldBeVisible)
|
else if (trayIsVisible && !trayShouldBeVisible)
|
||||||
@ -2311,7 +2377,6 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
_showTray: function() {
|
_showTray: function() {
|
||||||
if (!this._grabHelper.grab({ actor: this.actor,
|
if (!this._grabHelper.grab({ actor: this.actor,
|
||||||
modal: true,
|
|
||||||
onUngrab: Lang.bind(this, this._escapeTray) })) {
|
onUngrab: Lang.bind(this, this._escapeTray) })) {
|
||||||
this._traySummoned = false;
|
this._traySummoned = false;
|
||||||
return false;
|
return false;
|
||||||
@ -2324,7 +2389,8 @@ const MessageTray = new Lang.Class({
|
|||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
|
|
||||||
this._lightbox.show();
|
for (let i = 0; i < this._lightboxes.length; i++)
|
||||||
|
this._lightboxes[i].show();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -2347,7 +2413,7 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
if (this._desktopClone)
|
if (this._desktopClone)
|
||||||
this._desktopClone.destroy();
|
this._desktopClone.destroy();
|
||||||
let cloneSource = Main.overview.visible ? global.overlay_group : global.window_group;
|
let cloneSource = Main.overview.visible ? Main.layoutManager.overviewGroup : global.window_group;
|
||||||
this._desktopClone = new Clutter.Clone({ source: cloneSource,
|
this._desktopClone = new Clutter.Clone({ source: cloneSource,
|
||||||
clip: new Clutter.Geometry(this._bottomMonitorGeometry) });
|
clip: new Clutter.Geometry(this._bottomMonitorGeometry) });
|
||||||
Main.uiGroup.insert_child_above(this._desktopClone, cloneSource);
|
Main.uiGroup.insert_child_above(this._desktopClone, cloneSource);
|
||||||
@ -2379,7 +2445,8 @@ const MessageTray = new Lang.Class({
|
|||||||
// which would happen if GrabHelper ungrabbed for us.
|
// which would happen if GrabHelper ungrabbed for us.
|
||||||
// This is a no-op in that case.
|
// This is a no-op in that case.
|
||||||
this._grabHelper.ungrab({ actor: this.actor });
|
this._grabHelper.ungrab({ actor: this.actor });
|
||||||
this._lightbox.hide();
|
for (let i = 0; i < this._lightboxes.length; i++)
|
||||||
|
this._lightboxes[i].hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
_hideDesktopClone: function() {
|
_hideDesktopClone: function() {
|
||||||
@ -2428,7 +2495,7 @@ const MessageTray = new Lang.Class({
|
|||||||
let [x, y, mods] = global.get_pointer();
|
let [x, y, mods] = global.get_pointer();
|
||||||
// We save the position of the mouse at the time when we started showing the notification
|
// We save the position of the mouse at the time when we started showing the notification
|
||||||
// in order to determine if the notification popped up under it. We make that check if
|
// in order to determine if the notification popped up under it. We make that check if
|
||||||
// the user starts moving the mouse and _onTrayHoverChanged() gets called. We don't
|
// the user starts moving the mouse and _onNotificationHoverChanged() gets called. We don't
|
||||||
// expand the notification if it just happened to pop up under the mouse unless the user
|
// expand the notification if it just happened to pop up under the mouse unless the user
|
||||||
// explicitly mouses away from it and then mouses back in.
|
// explicitly mouses away from it and then mouses back in.
|
||||||
this._showNotificationMouseX = x;
|
this._showNotificationMouseX = x;
|
||||||
@ -2490,13 +2557,13 @@ const MessageTray = new Lang.Class({
|
|||||||
|
|
||||||
_notificationTimeout: function() {
|
_notificationTimeout: function() {
|
||||||
let [x, y, mods] = global.get_pointer();
|
let [x, y, mods] = global.get_pointer();
|
||||||
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
|
if (y > this._lastSeenMouseY + 10 && !this._notificationHovered) {
|
||||||
// The mouse is moving towards the notification, so don't
|
// The mouse is moving towards the notification, so don't
|
||||||
// hide it yet. (We just create a new timeout (and destroy
|
// hide it yet. (We just create a new timeout (and destroy
|
||||||
// the old one) each time because the bookkeeping is
|
// the old one) each time because the bookkeeping is
|
||||||
// simpler.)
|
// simpler.)
|
||||||
this._updateNotificationTimeout(1000);
|
this._updateNotificationTimeout(1000);
|
||||||
} else if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId &&
|
} else if (this._useLongerNotificationLeftTimeout && !this._notificationLeftTimeoutId &&
|
||||||
(x != this._lastSeenMouseX || y != this._lastSeenMouseY)) {
|
(x != this._lastSeenMouseX || y != this._lastSeenMouseY)) {
|
||||||
// Refresh the timeout if the notification originally
|
// Refresh the timeout if the notification originally
|
||||||
// popped up under the pointer, and the pointer is hovering
|
// popped up under the pointer, and the pointer is hovering
|
||||||
@ -2513,19 +2580,7 @@ const MessageTray = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_hideNotification: function() {
|
_hideNotification: function() {
|
||||||
// HACK!
|
this._notificationFocusGrabber.ungrabFocus();
|
||||||
// There seems to be a reentrancy issue in calling .ungrab() here,
|
|
||||||
// which causes _updateState to be called before _notificationState
|
|
||||||
// becomes HIDING. That hides the notification again, nullifying the
|
|
||||||
// object but not setting _notificationState (and that's the weird part)
|
|
||||||
// As then _notificationState is stuck into SHOWN but _notification
|
|
||||||
// is null, every new _updateState fails and the message tray is
|
|
||||||
// lost forever.
|
|
||||||
//
|
|
||||||
// See more at https://bugzilla.gnome.org/show_bug.cgi?id=683986
|
|
||||||
this._notificationState = State.HIDING;
|
|
||||||
|
|
||||||
this._grabHelper.ungrab({ actor: this._notification.actor });
|
|
||||||
|
|
||||||
if (this._notificationExpandedId) {
|
if (this._notificationExpandedId) {
|
||||||
this._notification.disconnect(this._notificationExpandedId);
|
this._notification.disconnect(this._notificationExpandedId);
|
||||||
@ -2540,12 +2595,12 @@ const MessageTray = new Lang.Class({
|
|||||||
this._notificationUnfocusedId = 0;
|
this._notificationUnfocusedId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._useLongerTrayLeftTimeout = false;
|
this._useLongerNotificationLeftTimeout = false;
|
||||||
if (this._trayLeftTimeoutId) {
|
if (this._notificationLeftTimeoutId) {
|
||||||
Mainloop.source_remove(this._trayLeftTimeoutId);
|
Mainloop.source_remove(this._notificationLeftTimeoutId);
|
||||||
this._trayLeftTimeoutId = 0;
|
this._notificationLeftTimeoutId = 0;
|
||||||
this._trayLeftMouseX = -1;
|
this._notificationLeftMouseX = -1;
|
||||||
this._trayLeftMouseY = -1;
|
this._notificationLeftMouseY = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._notificationRemoved) {
|
if (this._notificationRemoved) {
|
||||||
@ -2575,14 +2630,9 @@ const MessageTray = new Lang.Class({
|
|||||||
if (notification.isTransient)
|
if (notification.isTransient)
|
||||||
notification.destroy(NotificationDestroyedReason.EXPIRED);
|
notification.destroy(NotificationDestroyedReason.EXPIRED);
|
||||||
|
|
||||||
this._notificationRemoved = false;
|
|
||||||
this._closeButton.hide();
|
this._closeButton.hide();
|
||||||
this._pointerInTray = false;
|
this._pointerInNotification = false;
|
||||||
|
this._notificationRemoved = false;
|
||||||
// Clutter will send a leave-event the next time the mouse
|
|
||||||
// moves, but we need to set this here now to update the
|
|
||||||
// state machine.
|
|
||||||
this.actor.hover = false;
|
|
||||||
this._notificationBin.child = null;
|
this._notificationBin.child = null;
|
||||||
this._notificationWidget.hide();
|
this._notificationWidget.hide();
|
||||||
},
|
},
|
||||||
@ -2595,16 +2645,16 @@ const MessageTray = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_expandNotification: function(autoExpanding) {
|
_expandNotification: function(autoExpanding) {
|
||||||
// Don't focus notifications that are auto-expanding.
|
|
||||||
if (!autoExpanding)
|
|
||||||
this._ensureNotificationFocused();
|
|
||||||
|
|
||||||
if (!this._notificationExpandedId)
|
if (!this._notificationExpandedId)
|
||||||
this._notificationExpandedId =
|
this._notificationExpandedId =
|
||||||
this._notification.connect('expanded',
|
this._notification.connect('expanded',
|
||||||
Lang.bind(this, this._onNotificationExpanded));
|
Lang.bind(this, this._onNotificationExpanded));
|
||||||
// Don't animate changes in notifications that are auto-expanding.
|
// Don't animate changes in notifications that are auto-expanding.
|
||||||
this._notification.expand(!autoExpanding);
|
this._notification.expand(!autoExpanding);
|
||||||
|
|
||||||
|
// Don't focus notifications that are auto-expanding.
|
||||||
|
if (!autoExpanding)
|
||||||
|
this._ensureNotificationFocused();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onNotificationExpanded: function() {
|
_onNotificationExpanded: function() {
|
||||||
@ -2628,8 +2678,7 @@ const MessageTray = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_ensureNotificationFocused: function() {
|
_ensureNotificationFocused: function() {
|
||||||
this._grabHelper.grab({ actor: this._notification.actor,
|
this._notificationFocusGrabber.grabFocus();
|
||||||
grabFocus: true });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSourceDoneDisplayingContent: function(source, closeTray) {
|
_onSourceDoneDisplayingContent: function(source, closeTray) {
|
||||||
@ -2642,38 +2691,34 @@ const MessageTray = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_showSummaryBoxPointer: function() {
|
_showSummaryBoxPointer: function() {
|
||||||
this._summaryBoxPointerItem = this._clickedSummaryItem;
|
let child;
|
||||||
|
let summaryItem = this._clickedSummaryItem;
|
||||||
|
if (this._clickedSummaryItemMouseButton == 1) {
|
||||||
|
// Acknowledge all our notifications
|
||||||
|
summaryItem.source.notifications.forEach(function(n) { n.acknowledged = true; });
|
||||||
|
|
||||||
|
child = summaryItem.notificationStackWidget;
|
||||||
|
|
||||||
|
let closeButton = summaryItem.closeButton;
|
||||||
|
closeButton.show();
|
||||||
|
summaryItem.prepareNotificationStackForShowing();
|
||||||
|
} else if (this._clickedSummaryItemMouseButton == 3) {
|
||||||
|
child = summaryItem.rightClickMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user clicked the middle mouse button, or the item
|
||||||
|
// doesn't have a right-click menu, do nothing.
|
||||||
|
if (!child)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._summaryBoxPointerItem = summaryItem;
|
||||||
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
|
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
|
||||||
Lang.bind(this, this._onSummaryBoxPointerContentUpdated));
|
Lang.bind(this, this._onSummaryBoxPointerContentUpdated));
|
||||||
this._sourceDoneDisplayingId = this._summaryBoxPointerItem.source.connect('done-displaying-content',
|
this._sourceDoneDisplayingId = this._summaryBoxPointerItem.source.connect('done-displaying-content',
|
||||||
Lang.bind(this, this._onSourceDoneDisplayingContent));
|
Lang.bind(this, this._onSourceDoneDisplayingContent));
|
||||||
|
|
||||||
let hasRightClickMenu = this._summaryBoxPointerItem.rightClickMenu != null;
|
this._summaryBoxPointer.bin.child = child;
|
||||||
if (this._clickedSummaryItemMouseButton == 1 || !hasRightClickMenu) {
|
|
||||||
let newQueue = [];
|
|
||||||
for (let i = 0; i < this._notificationQueue.length; i++) {
|
|
||||||
let notification = this._notificationQueue[i];
|
|
||||||
let sameSource = this._summaryBoxPointerItem.source == notification.source;
|
|
||||||
if (sameSource)
|
|
||||||
notification.acknowledged = true;
|
|
||||||
else
|
|
||||||
newQueue.push(notification);
|
|
||||||
}
|
|
||||||
this._notificationQueue = newQueue;
|
|
||||||
|
|
||||||
this._summaryBoxPointer.bin.child = this._summaryBoxPointerItem.notificationStackWidget;
|
|
||||||
|
|
||||||
let closeButton = this._summaryBoxPointerItem.closeButton;
|
|
||||||
closeButton.show();
|
|
||||||
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
|
|
||||||
this._summaryBoxPointerItem.prepareNotificationStackForShowing();
|
|
||||||
} else if (this._clickedSummaryItemMouseButton == 3) {
|
|
||||||
this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
|
|
||||||
this._summaryBoxPointerCloseClickedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
||||||
grabFocus: true,
|
|
||||||
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
||||||
|
|
||||||
this._summaryBoxPointer.actor.opacity = 0;
|
this._summaryBoxPointer.actor.opacity = 0;
|
||||||
@ -2752,10 +2797,7 @@ const MessageTray = new Lang.Class({
|
|||||||
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
|
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
|
||||||
this._summaryBoxPointerContentUpdatedId = 0;
|
this._summaryBoxPointerContentUpdatedId = 0;
|
||||||
}
|
}
|
||||||
if (this._summaryBoxPointerCloseClickedId != 0) {
|
|
||||||
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
|
|
||||||
this._summaryBoxPointerCloseClickedId = 0;
|
|
||||||
}
|
|
||||||
if (this._sourceDoneDisplayingId) {
|
if (this._sourceDoneDisplayingId) {
|
||||||
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
|
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
|
||||||
this._sourceDoneDisplayingId = 0;
|
this._sourceDoneDisplayingId = 0;
|
||||||
@ -2782,17 +2824,14 @@ const MessageTray = new Lang.Class({
|
|||||||
this._summaryBoxPointerState = State.HIDDEN;
|
this._summaryBoxPointerState = State.HIDDEN;
|
||||||
this._summaryBoxPointer.bin.child = null;
|
this._summaryBoxPointer.bin.child = null;
|
||||||
|
|
||||||
let sourceNotificationStackDoneShowing = null;
|
|
||||||
if (doneShowingNotificationStack) {
|
if (doneShowingNotificationStack) {
|
||||||
|
let source = this._summaryBoxPointerItem.source;
|
||||||
|
|
||||||
this._summaryBoxPointerItem.doneShowingNotificationStack();
|
this._summaryBoxPointerItem.doneShowingNotificationStack();
|
||||||
sourceNotificationStackDoneShowing = this._summaryBoxPointerItem.source;
|
this._summaryBoxPointerItem = null;
|
||||||
}
|
|
||||||
|
|
||||||
this._summaryBoxPointerItem = null;
|
if (source.isTransient && !this._reNotifyAfterHideNotification)
|
||||||
|
source.destroy(NotificationDestroyedReason.EXPIRED);
|
||||||
if (sourceNotificationStackDoneShowing) {
|
|
||||||
if (sourceNotificationStackDoneShowing.isTransient && !this._reNotifyAfterHideNotification)
|
|
||||||
sourceNotificationStackDoneShowing.destroy(NotificationDestroyedReason.EXPIRED);
|
|
||||||
if (this._reNotifyAfterHideNotification) {
|
if (this._reNotifyAfterHideNotification) {
|
||||||
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
|
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
|
||||||
this._reNotifyAfterHideNotification = null;
|
this._reNotifyAfterHideNotification = null;
|
||||||
|
@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
|
|||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
const Layout = imports.ui.layout;
|
const Layout = imports.ui.layout;
|
||||||
const Lightbox = imports.ui.lightbox;
|
const Lightbox = imports.ui.lightbox;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -22,6 +23,10 @@ const Tweener = imports.ui.tweener;
|
|||||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||||
|
|
||||||
|
const WORK_SPINNER_ICON_SIZE = 24;
|
||||||
|
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
|
||||||
|
const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
||||||
|
|
||||||
const State = {
|
const State = {
|
||||||
OPENED: 0,
|
OPENED: 0,
|
||||||
CLOSED: 1,
|
CLOSED: 1,
|
||||||
@ -65,31 +70,27 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||||
|
|
||||||
this._backgroundBin = new St.Bin();
|
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||||
|
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
this._monitorConstraint = new Layout.MonitorConstraint();
|
this._monitorConstraint = new Layout.MonitorConstraint();
|
||||||
this._backgroundBin.add_constraint(this._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) {
|
||||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||||
{ inhibitEvents: true });
|
{ inhibitEvents: true });
|
||||||
this._lightbox.highlight(this._backgroundBin);
|
this._lightbox.highlight(this._backgroundBin);
|
||||||
|
|
||||||
let stack = new Shell.Stack();
|
|
||||||
this._backgroundBin.child = stack;
|
|
||||||
|
|
||||||
this._eventBlocker = new Clutter.Actor({ reactive: true });
|
this._eventBlocker = new Clutter.Actor({ reactive: true });
|
||||||
stack.add_actor(this._eventBlocker);
|
this.backgroundStack.add_actor(this._eventBlocker);
|
||||||
stack.add_actor(this.dialogLayout);
|
|
||||||
} else {
|
|
||||||
this._backgroundBin.child = this.dialogLayout;
|
|
||||||
}
|
}
|
||||||
|
this.backgroundStack.add_actor(this.dialogLayout);
|
||||||
|
|
||||||
|
|
||||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||||
@ -110,6 +111,8 @@ const ModalDialog = new Lang.Class({
|
|||||||
this._initialKeyFocus = this.dialogLayout;
|
this._initialKeyFocus = this.dialogLayout;
|
||||||
this._initialKeyFocusDestroyId = 0;
|
this._initialKeyFocusDestroyId = 0;
|
||||||
this._savedKeyFocus = null;
|
this._savedKeyFocus = null;
|
||||||
|
|
||||||
|
this._workSpinner = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@ -183,6 +186,42 @@ const ModalDialog = new Lang.Class({
|
|||||||
return button;
|
return button;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
placeSpinner: function(layoutInfo) {
|
||||||
|
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||||
|
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||||
|
this._workSpinner.actor.opacity = 0;
|
||||||
|
this._workSpinner.actor.show();
|
||||||
|
|
||||||
|
this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
|
||||||
|
},
|
||||||
|
|
||||||
|
setWorking: function(working) {
|
||||||
|
if (!this._workSpinner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tweener.removeTweens(this._workSpinner.actor);
|
||||||
|
if (working) {
|
||||||
|
this._workSpinner.play();
|
||||||
|
Tweener.addTween(this._workSpinner.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
delay: WORK_SPINNER_ANIMATION_DELAY,
|
||||||
|
time: WORK_SPINNER_ANIMATION_TIME,
|
||||||
|
transition: 'linear'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Tweener.addTween(this._workSpinner.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: WORK_SPINNER_ANIMATION_TIME,
|
||||||
|
transition: 'linear',
|
||||||
|
onCompleteScope: this,
|
||||||
|
onComplete: function() {
|
||||||
|
if (this._workSpinner)
|
||||||
|
this._workSpinner.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_onKeyPressEvent: function(object, event) {
|
_onKeyPressEvent: function(object, event) {
|
||||||
this._pressedKey = event.get_key_symbol();
|
this._pressedKey = event.get_key_symbol();
|
||||||
},
|
},
|
||||||
@ -317,8 +356,9 @@ const ModalDialog = new Lang.Class({
|
|||||||
if (this._savedKeyFocus) {
|
if (this._savedKeyFocus) {
|
||||||
this._savedKeyFocus.grab_key_focus();
|
this._savedKeyFocus.grab_key_focus();
|
||||||
this._savedKeyFocus = null;
|
this._savedKeyFocus = null;
|
||||||
} else
|
} else {
|
||||||
this._initialKeyFocus.grab_key_focus();
|
this._initialKeyFocus.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._shellReactive)
|
if (!this._shellReactive)
|
||||||
this._eventBlocker.lower_bottom();
|
this._eventBlocker.lower_bottom();
|
||||||
|
@ -717,8 +717,8 @@ const Source = new Lang.Class({
|
|||||||
this.notifications.length > 0)
|
this.notifications.length > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
|
let id = global.stage.connect('deactivate', Lang.bind(this, function () {
|
||||||
global.disconnect(id);
|
global.stage.disconnect(id);
|
||||||
this.trayIcon.click(event);
|
this.trayIcon.click(event);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ const Layout = imports.ui.layout;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
|
||||||
const HIDE_TIMEOUT = 1500;
|
const HIDE_TIMEOUT = 1500;
|
||||||
const FADE_TIME = 0.1;
|
const FADE_TIME = 0.1;
|
||||||
@ -71,6 +72,7 @@ const OsdWindow = new Lang.Class({
|
|||||||
Name: 'OsdWindow',
|
Name: 'OsdWindow',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this._popupSize = 0;
|
||||||
this.actor = new St.Widget({ x_expand: true,
|
this.actor = new St.Widget({ x_expand: true,
|
||||||
y_expand: true,
|
y_expand: true,
|
||||||
x_align: Clutter.ActorAlign.CENTER,
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
@ -80,6 +82,15 @@ const OsdWindow = new Lang.Class({
|
|||||||
vertical: true });
|
vertical: true });
|
||||||
this.actor.add_actor(this._box);
|
this.actor.add_actor(this._box);
|
||||||
|
|
||||||
|
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
|
this._box.connect('notify::height', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._box.width = this._box.height;
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
this._icon = new St.Icon();
|
this._icon = new St.Icon();
|
||||||
this._box.add(this._icon, { expand: true });
|
this._box.add(this._icon, { expand: true });
|
||||||
|
|
||||||
@ -96,7 +107,7 @@ const OsdWindow = new Lang.Class({
|
|||||||
Lang.bind(this, this._monitorsChanged));
|
Lang.bind(this, this._monitorsChanged));
|
||||||
this._monitorsChanged();
|
this._monitorsChanged();
|
||||||
|
|
||||||
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
|
Main.uiGroup.add_child(this.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
setIcon: function(icon) {
|
setIcon: function(icon) {
|
||||||
@ -125,8 +136,10 @@ const OsdWindow = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this.actor.visible) {
|
if (!this.actor.visible) {
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
this.actor.opacity = 0;
|
this.actor.opacity = 0;
|
||||||
|
this.actor.get_parent().set_child_above_sibling(this.actor, null);
|
||||||
|
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
@ -145,16 +158,20 @@ const OsdWindow = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Mainloop.source_remove(this._hideTimeoutId);
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
this._hideTimeoutId = 0;
|
|
||||||
this._hide();
|
this._hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
_hide: function() {
|
_hide: function() {
|
||||||
|
this._hideTimeoutId = 0;
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: FADE_TIME,
|
time: FADE_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, this._reset) });
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this._reset();
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
})
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_reset: function() {
|
_reset: function() {
|
||||||
@ -169,11 +186,25 @@ const OsdWindow = new Lang.Class({
|
|||||||
let scalew = monitor.width / 640.0;
|
let scalew = monitor.width / 640.0;
|
||||||
let scaleh = monitor.height / 480.0;
|
let scaleh = monitor.height / 480.0;
|
||||||
let scale = Math.min(scalew, scaleh);
|
let scale = Math.min(scalew, scaleh);
|
||||||
let size = 110 * Math.max(1, scale);
|
this._popupSize = 110 * Math.max(1, scale);
|
||||||
|
|
||||||
this._box.set_size(size, size);
|
|
||||||
this._box.translation_y = monitor.height / 4;
|
this._box.translation_y = monitor.height / 4;
|
||||||
|
this._icon.icon_size = this._popupSize / 2;
|
||||||
|
this._box.style_changed();
|
||||||
|
},
|
||||||
|
|
||||||
this._icon.icon_size = size / 2;
|
_onStyleChanged: function() {
|
||||||
|
let themeNode = this._box.get_theme_node();
|
||||||
|
let horizontalPadding = themeNode.get_horizontal_padding();
|
||||||
|
let verticalPadding = themeNode.get_vertical_padding();
|
||||||
|
let topBorder = themeNode.get_border_width(St.Side.TOP);
|
||||||
|
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
|
||||||
|
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
|
||||||
|
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
|
||||||
|
|
||||||
|
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
|
||||||
|
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
|
||||||
|
|
||||||
|
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,6 @@ const Shell = imports.gi.Shell;
|
|||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
|
|
||||||
const Background = imports.ui.background;
|
const Background = imports.ui.background;
|
||||||
const Dash = imports.ui.dash;
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const LayoutManager = imports.ui.layout;
|
const LayoutManager = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -20,7 +19,6 @@ const OverviewControls = imports.ui.overviewControls;
|
|||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const ViewSelector = imports.ui.viewSelector;
|
|
||||||
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
|
||||||
@ -31,7 +29,7 @@ const ANIMATION_TIME = 0.25;
|
|||||||
// and don't want the shading animation to get cut off
|
// and don't want the shading animation to get cut off
|
||||||
const SHADE_ANIMATION_TIME = .20;
|
const SHADE_ANIMATION_TIME = .20;
|
||||||
|
|
||||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
const DND_WINDOW_SWITCH_TIMEOUT = 750;
|
||||||
|
|
||||||
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
|
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
|
||||||
|
|
||||||
@ -117,7 +115,7 @@ const Overview = new Lang.Class({
|
|||||||
let monitor = Main.layoutManager.primaryMonitor;
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
this._desktopFade = new St.Bin();
|
this._desktopFade = new St.Bin();
|
||||||
global.overlay_group.add_actor(this._desktopFade);
|
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
|
||||||
|
|
||||||
let layout = new Clutter.BinLayout();
|
let layout = new Clutter.BinLayout();
|
||||||
this._stack = new Clutter.Actor({ layout_manager: layout });
|
this._stack = new Clutter.Actor({ layout_manager: layout });
|
||||||
@ -133,17 +131,8 @@ const Overview = new Lang.Class({
|
|||||||
y_expand: true });
|
y_expand: true });
|
||||||
this._overview._delegate = this;
|
this._overview._delegate = this;
|
||||||
|
|
||||||
this._groupStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
|
||||||
x_expand: true, y_expand: true,
|
|
||||||
clip_to_allocation: true });
|
|
||||||
this._group = new St.BoxLayout({ name: 'overview-group',
|
|
||||||
reactive: true,
|
|
||||||
x_expand: true, y_expand: true });
|
|
||||||
this._groupStack.add_actor(this._group);
|
|
||||||
|
|
||||||
this._backgroundGroup = new Meta.BackgroundGroup();
|
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||||
global.overlay_group.add_child(this._backgroundGroup);
|
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
|
||||||
this._backgroundGroup.hide();
|
|
||||||
this._bgManagers = [];
|
this._bgManagers = [];
|
||||||
|
|
||||||
this._activationTime = 0;
|
this._activationTime = 0;
|
||||||
@ -157,14 +146,13 @@ const Overview = new Lang.Class({
|
|||||||
// During transitions, we raise this to the top to avoid having the overview
|
// During transitions, we raise this to the top to avoid having the overview
|
||||||
// area be reactive; it causes too many issues such as double clicks on
|
// area be reactive; it causes too many issues such as double clicks on
|
||||||
// Dash elements, or mouseover handlers in the workspaces.
|
// Dash elements, or mouseover handlers in the workspaces.
|
||||||
this._coverPane = new Clutter.Rectangle({ opacity: 0,
|
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
this._overview.add_actor(this._coverPane);
|
this._stack.add_actor(this._coverPane);
|
||||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||||
|
|
||||||
this._stack.hide();
|
|
||||||
this._stack.add_actor(this._overview);
|
this._stack.add_actor(this._overview);
|
||||||
global.overlay_group.add_actor(this._stack);
|
Main.layoutManager.overviewGroup.add_child(this._stack);
|
||||||
|
|
||||||
this._coverPane.hide();
|
this._coverPane.hide();
|
||||||
|
|
||||||
@ -177,7 +165,6 @@ const Overview = new Lang.Class({
|
|||||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||||
|
|
||||||
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
||||||
this._group.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
|
||||||
|
|
||||||
this._windowSwitchTimeoutId = 0;
|
this._windowSwitchTimeoutId = 0;
|
||||||
this._windowSwitchTimestamp = 0;
|
this._windowSwitchTimestamp = 0;
|
||||||
@ -276,28 +263,13 @@ const Overview = new Lang.Class({
|
|||||||
this._overview.add_actor(this._searchEntryBin);
|
this._overview.add_actor(this._searchEntryBin);
|
||||||
|
|
||||||
// Create controls
|
// Create controls
|
||||||
this._dash = new Dash.Dash();
|
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
|
||||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
this._dash = this._controls.dash;
|
||||||
this._dash.showAppsButton);
|
this._viewSelector = this._controls.viewSelector;
|
||||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
|
||||||
this._controls = new OverviewControls.ControlsManager(this._dash,
|
|
||||||
this._thumbnailsBox,
|
|
||||||
this._viewSelector);
|
|
||||||
|
|
||||||
this._controls.dashActor.x_align = Clutter.ActorAlign.START;
|
|
||||||
this._controls.dashActor.y_expand = true;
|
|
||||||
|
|
||||||
// Put the dash in a separate layer to allow content to be centered
|
|
||||||
this._groupStack.add_actor(this._controls.dashActor);
|
|
||||||
|
|
||||||
// Pack all the actors into the group
|
|
||||||
this._group.add_actor(this._controls.dashSpacer);
|
|
||||||
this._group.add(this._viewSelector.actor, { x_fill: true,
|
|
||||||
expand: true });
|
|
||||||
this._group.add_actor(this._controls.thumbnailsActor);
|
|
||||||
|
|
||||||
// Add our same-line elements after the search entry
|
// Add our same-line elements after the search entry
|
||||||
this._overview.add(this._groupStack, { y_fill: true, expand: true });
|
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
|
||||||
|
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
|
|
||||||
this._stack.add_actor(this._controls.indicatorActor);
|
this._stack.add_actor(this._controls.indicatorActor);
|
||||||
|
|
||||||
@ -461,6 +433,7 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
beginItemDrag: function(source) {
|
beginItemDrag: function(source) {
|
||||||
this.emit('item-drag-begin');
|
this.emit('item-drag-begin');
|
||||||
|
this._inDrag = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelledItemDrag: function(source) {
|
cancelledItemDrag: function(source) {
|
||||||
@ -469,10 +442,12 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
endItemDrag: function(source) {
|
endItemDrag: function(source) {
|
||||||
this.emit('item-drag-end');
|
this.emit('item-drag-end');
|
||||||
|
this._inDrag = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
beginWindowDrag: function(source) {
|
beginWindowDrag: function(source) {
|
||||||
this.emit('window-drag-begin');
|
this.emit('window-drag-begin');
|
||||||
|
this._inDrag = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelledWindowDrag: function(source) {
|
cancelledWindowDrag: function(source) {
|
||||||
@ -481,24 +456,31 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
endWindowDrag: function(source) {
|
endWindowDrag: function(source) {
|
||||||
this.emit('window-drag-end');
|
this.emit('window-drag-end');
|
||||||
|
this._inDrag = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// show:
|
// show:
|
||||||
//
|
//
|
||||||
// Animates the overview visible and grabs mouse and keyboard input
|
// Animates the overview visible and grabs mouse and keyboard input
|
||||||
show : function() {
|
show: function() {
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
if (this._shown)
|
if (this._shown)
|
||||||
return;
|
return;
|
||||||
this._shown = true;
|
this._shown = true;
|
||||||
|
|
||||||
if (!this._syncInputMode())
|
if (!this._syncGrab())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Main.layoutManager.showOverview();
|
||||||
this._animateVisible();
|
this._animateVisible();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
focusSearch: function() {
|
||||||
|
this.show();
|
||||||
|
this._searchEntry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
fadeInDesktop: function() {
|
fadeInDesktop: function() {
|
||||||
this._desktopFade.opacity = 0;
|
this._desktopFade.opacity = 0;
|
||||||
this._desktopFade.show();
|
this._desktopFade.show();
|
||||||
@ -540,8 +522,6 @@ const Overview = new Lang.Class({
|
|||||||
//
|
//
|
||||||
// Disable unredirection while in the overview
|
// Disable unredirection while in the overview
|
||||||
Meta.disable_unredirect_for_screen(global.screen);
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
this._stack.show();
|
|
||||||
this._backgroundGroup.show();
|
|
||||||
this._viewSelector.show();
|
this._viewSelector.show();
|
||||||
|
|
||||||
this._stack.opacity = 0;
|
this._stack.opacity = 0;
|
||||||
@ -582,7 +562,7 @@ const Overview = new Lang.Class({
|
|||||||
this._animateNotVisible();
|
this._animateNotVisible();
|
||||||
|
|
||||||
this._shown = false;
|
this._shown = false;
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
@ -604,6 +584,8 @@ const Overview = new Lang.Class({
|
|||||||
shouldToggleByCornerOrButton: function() {
|
shouldToggleByCornerOrButton: function() {
|
||||||
if (this.animationInProgress)
|
if (this.animationInProgress)
|
||||||
return false;
|
return false;
|
||||||
|
if (this._inDrag)
|
||||||
|
return false;
|
||||||
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
|
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
@ -611,8 +593,8 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
_syncInputMode: function() {
|
_syncGrab: function() {
|
||||||
// We delay input mode changes during animation so that when removing the
|
// We delay grab changes during animation so that when removing the
|
||||||
// overview we don't have a problem with the release of a press/release
|
// overview we don't have a problem with the release of a press/release
|
||||||
// going to an application.
|
// going to an application.
|
||||||
if (this.animationInProgress)
|
if (this.animationInProgress)
|
||||||
@ -630,16 +612,12 @@ const Overview = new Lang.Class({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this._modal) {
|
if (this._modal) {
|
||||||
Main.popModal(this._overview);
|
Main.popModal(this._overview);
|
||||||
this._modal = false;
|
this._modal = false;
|
||||||
}
|
}
|
||||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
|
||||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -678,7 +656,7 @@ const Overview = new Lang.Class({
|
|||||||
if (!this._shown)
|
if (!this._shown)
|
||||||
this._animateNotVisible();
|
this._animateNotVisible();
|
||||||
|
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
global.sync_pointer();
|
global.sync_pointer();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -688,20 +666,19 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this._viewSelector.hide();
|
this._viewSelector.hide();
|
||||||
this._desktopFade.hide();
|
this._desktopFade.hide();
|
||||||
this._backgroundGroup.hide();
|
this._coverPane.hide();
|
||||||
this._stack.hide();
|
|
||||||
|
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.animationInProgress = false;
|
this.animationInProgress = false;
|
||||||
|
|
||||||
this._coverPane.hide();
|
|
||||||
|
|
||||||
this.emit('hidden');
|
this.emit('hidden');
|
||||||
// Handle any calls to show* while we were hiding
|
// Handle any calls to show* while we were hiding
|
||||||
if (this._shown)
|
if (this._shown)
|
||||||
this._animateVisible();
|
this._animateVisible();
|
||||||
|
else
|
||||||
|
Main.layoutManager.hideOverview();
|
||||||
|
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
|
|
||||||
// Fake a pointer event if requested
|
// Fake a pointer event if requested
|
||||||
if (this._needsFakePointerEvent) {
|
if (this._needsFakePointerEvent) {
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Dash = imports.ui.dash;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const ViewSelector = imports.ui.viewSelector;
|
const ViewSelector = imports.ui.viewSelector;
|
||||||
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||||
|
|
||||||
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
|
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
|
||||||
|
|
||||||
@ -244,6 +247,7 @@ const ThumbnailsSlider = new Lang.Class({
|
|||||||
|
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
|
||||||
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
|
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
|
||||||
|
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getAlwaysZoomOut: function() {
|
_getAlwaysZoomOut: function() {
|
||||||
@ -269,6 +273,11 @@ const ThumbnailsSlider = new Lang.Class({
|
|||||||
return alwaysZoomOut;
|
return alwaysZoomOut;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getNonExpandedWidth: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
return child.get_theme_node().get_length('visible-width');
|
||||||
|
},
|
||||||
|
|
||||||
getSlide: function() {
|
getSlide: function() {
|
||||||
if (!this.visible)
|
if (!this.visible)
|
||||||
return 0;
|
return 0;
|
||||||
@ -280,18 +289,16 @@ const ThumbnailsSlider = new Lang.Class({
|
|||||||
let child = this.actor.get_first_child();
|
let child = this.actor.get_first_child();
|
||||||
let preferredHeight = child.get_preferred_height(-1)[1];
|
let preferredHeight = child.get_preferred_height(-1)[1];
|
||||||
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
|
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
|
||||||
let visibleWidth = child.get_theme_node().get_length('visible-width');
|
|
||||||
|
|
||||||
return visibleWidth / expandedWidth;
|
return this.getNonExpandedWidth() / expandedWidth;
|
||||||
},
|
},
|
||||||
|
|
||||||
getVisibleWidth: function() {
|
getVisibleWidth: function() {
|
||||||
let alwaysZoomOut = this._getAlwaysZoomOut();
|
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||||
if (alwaysZoomOut)
|
if (alwaysZoomOut)
|
||||||
return this.parent();
|
return this.parent();
|
||||||
|
else
|
||||||
let child = this.actor.get_first_child();
|
return this.getNonExpandedWidth();
|
||||||
return child.get_theme_node().get_length('visible-width');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -309,6 +316,10 @@ const DashSlider = new Lang.Class({
|
|||||||
// available allocation
|
// available allocation
|
||||||
this._dash.actor.x_expand = true;
|
this._dash.actor.x_expand = true;
|
||||||
this._dash.actor.y_expand = true;
|
this._dash.actor.y_expand = true;
|
||||||
|
|
||||||
|
this.actor.x_align = Clutter.ActorAlign.START;
|
||||||
|
this.actor.y_expand = true;
|
||||||
|
|
||||||
this.actor.add_actor(this._dash.actor);
|
this.actor.add_actor(this._dash.actor);
|
||||||
|
|
||||||
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
|
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
|
||||||
@ -479,39 +490,77 @@ const MessagesIndicator = new Lang.Class({
|
|||||||
const ControlsManager = new Lang.Class({
|
const ControlsManager = new Lang.Class({
|
||||||
Name: 'ControlsManager',
|
Name: 'ControlsManager',
|
||||||
|
|
||||||
_init: function(dash, thumbnails, viewSelector) {
|
_init: function(searchEntry) {
|
||||||
this._dashSlider = new DashSlider(dash);
|
this.dash = new Dash.Dash();
|
||||||
this.dashActor = this._dashSlider.actor;
|
this._dashSlider = new DashSlider(this.dash);
|
||||||
this.dashSpacer = new DashSpacer();
|
this._dashSpacer = new DashSpacer();
|
||||||
this.dashSpacer.setDashActor(this.dashActor);
|
this._dashSpacer.setDashActor(this._dashSlider.actor);
|
||||||
|
|
||||||
this._thumbnailsSlider = new ThumbnailsSlider(thumbnails);
|
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||||
this.thumbnailsActor = this._thumbnailsSlider.actor;
|
this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
|
||||||
|
|
||||||
this._indicator = new MessagesIndicator(viewSelector);
|
this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
|
||||||
|
this.dash.showAppsButton);
|
||||||
|
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
|
||||||
|
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
|
||||||
|
|
||||||
|
this._indicator = new MessagesIndicator(this.viewSelector);
|
||||||
this.indicatorActor = this._indicator.actor;
|
this.indicatorActor = this._indicator.actor;
|
||||||
|
|
||||||
this._viewSelector = viewSelector;
|
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||||
this._viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
|
reactive: true,
|
||||||
this._viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
|
x_expand: true, y_expand: true,
|
||||||
|
clip_to_allocation: true });
|
||||||
|
this._group = new St.BoxLayout({ name: 'overview-group',
|
||||||
|
x_expand: true, y_expand: true });
|
||||||
|
this.actor.add_actor(this._group);
|
||||||
|
|
||||||
|
this.actor.add_actor(this._dashSlider.actor);
|
||||||
|
|
||||||
|
this._group.add_actor(this._dashSpacer);
|
||||||
|
this._group.add(this.viewSelector.actor, { x_fill: true,
|
||||||
|
expand: true });
|
||||||
|
this._group.add_actor(this._thumbnailsSlider.actor);
|
||||||
|
|
||||||
|
this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
|
||||||
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
|
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
|
||||||
Main.overview.connect('item-drag-begin', Lang.bind(this,
|
Main.overview.connect('item-drag-begin', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
let activePage = this._viewSelector.getActivePage();
|
let activePage = this.viewSelector.getActivePage();
|
||||||
if (activePage != ViewSelector.ViewPage.WINDOWS)
|
if (activePage != ViewSelector.ViewPage.WINDOWS)
|
||||||
this._viewSelector.fadeHalf();
|
this.viewSelector.fadeHalf();
|
||||||
}));
|
}));
|
||||||
Main.overview.connect('item-drag-end', Lang.bind(this,
|
Main.overview.connect('item-drag-end', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
this._viewSelector.fadeIn();
|
this.viewSelector.fadeIn();
|
||||||
}));
|
}));
|
||||||
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
|
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
this._viewSelector.fadeIn();
|
this.viewSelector.fadeIn();
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateWorkspacesGeometry: function() {
|
||||||
|
let [x, y] = this.actor.get_transformed_position();
|
||||||
|
let [width, height] = this.actor.get_transformed_size();
|
||||||
|
let geometry = { x: x, y: y, width: width, height: height };
|
||||||
|
|
||||||
|
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||||
|
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
|
||||||
|
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
|
||||||
|
|
||||||
|
geometry.width -= dashWidth;
|
||||||
|
geometry.width -= thumbnailsWidth;
|
||||||
|
|
||||||
|
if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
|
||||||
|
geometry.x += dashWidth;
|
||||||
|
else
|
||||||
|
geometry.x += thumbnailsWidth;
|
||||||
|
|
||||||
|
this.viewSelector.setWorkspacesFullGeometry(geometry);
|
||||||
|
},
|
||||||
|
|
||||||
_setVisibility: function() {
|
_setVisibility: function() {
|
||||||
// Ignore the case when we're leaving the overview, since
|
// Ignore the case when we're leaving the overview, since
|
||||||
// actors will be made visible again when entering the overview
|
// actors will be made visible again when entering the overview
|
||||||
@ -521,7 +570,7 @@ const ControlsManager = new Lang.Class({
|
|||||||
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
|
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let activePage = this._viewSelector.getActivePage();
|
let activePage = this.viewSelector.getActivePage();
|
||||||
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
|
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
|
||||||
activePage == ViewSelector.ViewPage.APPS);
|
activePage == ViewSelector.ViewPage.APPS);
|
||||||
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||||
@ -541,8 +590,8 @@ const ControlsManager = new Lang.Class({
|
|||||||
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
|
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let activePage = this._viewSelector.getActivePage();
|
let activePage = this.viewSelector.getActivePage();
|
||||||
this.dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onPageEmpty: function() {
|
_onPageEmpty: function() {
|
||||||
|
164
js/ui/panel.js
164
js/ui/panel.js
@ -15,12 +15,14 @@ const Signals = imports.signals;
|
|||||||
const Atk = imports.gi.Atk;
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const 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 RemoteMenu = imports.ui.remoteMenu;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
@ -28,7 +30,6 @@ const PANEL_ICON_SIZE = 24;
|
|||||||
|
|
||||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||||
|
|
||||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
|
||||||
const SPINNER_ANIMATION_TIME = 0.2;
|
const SPINNER_ANIMATION_TIME = 0.2;
|
||||||
|
|
||||||
// To make sure the panel corners blend nicely with the panel,
|
// To make sure the panel corners blend nicely with the panel,
|
||||||
@ -74,81 +75,6 @@ function _unpremultiply(color) {
|
|||||||
blue: blue, alpha: color.alpha });
|
blue: blue, alpha: color.alpha });
|
||||||
};
|
};
|
||||||
|
|
||||||
const Animation = new Lang.Class({
|
|
||||||
Name: 'Animation',
|
|
||||||
|
|
||||||
_init: function(filename, width, height, speed) {
|
|
||||||
this.actor = new St.Bin();
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
||||||
this._speed = speed;
|
|
||||||
|
|
||||||
this._isLoaded = false;
|
|
||||||
this._isPlaying = false;
|
|
||||||
this._timeoutId = 0;
|
|
||||||
this._frame = 0;
|
|
||||||
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
|
||||||
Lang.bind(this, this._animationsLoaded));
|
|
||||||
this.actor.set_child(this._animations);
|
|
||||||
},
|
|
||||||
|
|
||||||
play: function() {
|
|
||||||
if (this._isLoaded && this._timeoutId == 0) {
|
|
||||||
if (this._frame == 0)
|
|
||||||
this._showFrame(0);
|
|
||||||
|
|
||||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isPlaying = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
stop: function() {
|
|
||||||
if (this._timeoutId > 0) {
|
|
||||||
Mainloop.source_remove(this._timeoutId);
|
|
||||||
this._timeoutId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isPlaying = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_showFrame: function(frame) {
|
|
||||||
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
|
||||||
if (oldFrameActor)
|
|
||||||
oldFrameActor.hide();
|
|
||||||
|
|
||||||
this._frame = (frame % this._animations.get_n_children());
|
|
||||||
|
|
||||||
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
|
||||||
if (newFrameActor)
|
|
||||||
newFrameActor.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
_update: function() {
|
|
||||||
this._showFrame(this._frame + 1);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_animationsLoaded: function() {
|
|
||||||
this._isLoaded = true;
|
|
||||||
|
|
||||||
if (this._isPlaying)
|
|
||||||
this.play();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const AnimatedIcon = new Lang.Class({
|
|
||||||
Name: 'AnimatedIcon',
|
|
||||||
Extends: Animation,
|
|
||||||
|
|
||||||
_init: function(filename, size) {
|
|
||||||
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const TextShadower = new Lang.Class({
|
const TextShadower = new Lang.Class({
|
||||||
Name: 'TextShadower',
|
Name: 'TextShadower',
|
||||||
|
|
||||||
@ -289,10 +215,10 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._visible = !Main.overview.visible;
|
this._visible = !Main.overview.visible;
|
||||||
if (!this._visible)
|
if (!this._visible)
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||||
this.show();
|
this.show();
|
||||||
}));
|
}));
|
||||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
|
||||||
this.hide();
|
this.hide();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -302,10 +228,12 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
let appSys = Shell.AppSystem.get_default();
|
let appSys = Shell.AppSystem.get_default();
|
||||||
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
|
this._focusAppNotifyId =
|
||||||
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
|
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
|
||||||
|
this._appStateChangedSignalId =
|
||||||
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
|
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
|
||||||
|
this._switchWorkspaceNotifyId =
|
||||||
|
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
|
||||||
|
|
||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
@ -357,7 +285,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
if (!success || this._spinnerIcon == icon)
|
if (!success || this._spinnerIcon == icon)
|
||||||
return;
|
return;
|
||||||
this._spinnerIcon = icon;
|
this._spinnerIcon = icon;
|
||||||
this._spinner = new AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||||
this._container.add_actor(this._spinner.actor);
|
this._container.add_actor(this._spinner.actor);
|
||||||
this._spinner.actor.hide();
|
this._spinner.actor.hide();
|
||||||
this._spinner.actor.lower_bottom();
|
this._spinner.actor.lower_bottom();
|
||||||
@ -369,13 +297,16 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._updateIconBoxClip();
|
this._updateIconBoxClip();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_syncIcon: function() {
|
||||||
|
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
|
||||||
|
this._iconBox.set_child(icon);
|
||||||
|
},
|
||||||
|
|
||||||
_onIconThemeChanged: function() {
|
_onIconThemeChanged: function() {
|
||||||
if (this._iconBox.child == null)
|
if (this._iconBox.child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._iconBox.child.destroy();
|
this._syncIcon();
|
||||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
|
||||||
this._iconBox.set_child(icon);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateIconBoxClip: function() {
|
_updateIconBoxClip: function() {
|
||||||
@ -521,7 +452,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
// If the app has just lost focus to the panel, pretend
|
// If the app has just lost focus to the panel, pretend
|
||||||
// nothing happened; otherwise you can't keynav to the
|
// nothing happened; otherwise you can't keynav to the
|
||||||
// app menu.
|
// app menu.
|
||||||
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
|
if (global.stage.key_focus != null)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._sync();
|
this._sync();
|
||||||
@ -566,9 +497,14 @@ const AppMenuButton = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetApp == this._targetApp) {
|
if (targetApp == this._targetApp) {
|
||||||
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
|
if (targetApp &&
|
||||||
|
targetApp.get_state() != Shell.AppState.STARTING &&
|
||||||
|
targetApp.get_state() != Shell.AppState.BUSY) {
|
||||||
this.stopAnimation();
|
this.stopAnimation();
|
||||||
this._maybeSetMenu();
|
this._maybeSetMenu();
|
||||||
|
} else if (targetApp &&
|
||||||
|
targetApp.get_state() == Shell.AppState.BUSY) {
|
||||||
|
this.startAnimation();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -593,15 +529,14 @@ const AppMenuButton = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._targetApp = targetApp;
|
this._targetApp = targetApp;
|
||||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
|
||||||
|
|
||||||
this._label.setText(targetApp.get_name());
|
this._label.setText(targetApp.get_name());
|
||||||
this.setName(targetApp.get_name());
|
this.setName(targetApp.get_name());
|
||||||
|
|
||||||
this._iconBox.set_child(icon);
|
this._syncIcon();
|
||||||
this._iconBox.show();
|
this._iconBox.show();
|
||||||
|
|
||||||
if (targetApp.get_state() == Shell.AppState.STARTING)
|
if (targetApp.get_state() == Shell.AppState.STARTING ||
|
||||||
|
targetApp.get_state() == Shell.AppState.BUSY)
|
||||||
this.startAnimation();
|
this.startAnimation();
|
||||||
else
|
else
|
||||||
this._maybeSetMenu();
|
this._maybeSetMenu();
|
||||||
@ -613,11 +548,11 @@ const AppMenuButton = new Lang.Class({
|
|||||||
let menu;
|
let menu;
|
||||||
|
|
||||||
if (this._targetApp.action_group && this._targetApp.menu) {
|
if (this._targetApp.action_group && this._targetApp.menu) {
|
||||||
if (this.menu instanceof PopupMenu.RemoteMenu &&
|
if (this.menu instanceof RemoteMenu.RemoteMenu &&
|
||||||
this.menu.actionGroup == this._targetApp.action_group)
|
this.menu.actionGroup == this._targetApp.action_group)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
menu = new RemoteMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||||
menu.connect('activate', Lang.bind(this, function() {
|
menu.connect('activate', Lang.bind(this, function() {
|
||||||
let win = this._targetApp.get_windows()[0];
|
let win = this._targetApp.get_windows()[0];
|
||||||
win.check_alive(global.get_current_time());
|
win.check_alive(global.get_current_time());
|
||||||
@ -637,6 +572,33 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
this.setMenu(menu);
|
this.setMenu(menu);
|
||||||
this._menuManager.addMenu(menu);
|
this._menuManager.addMenu(menu);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._appStateChangedSignalId > 0) {
|
||||||
|
let appSys = Shell.AppSystem.get_default();
|
||||||
|
appSys.disconnect(this._appStateChangedSignalId);
|
||||||
|
this._appStateChangedSignalId = 0;
|
||||||
|
}
|
||||||
|
if (this._focusAppNotifyId > 0) {
|
||||||
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
tracker.disconnect(this._focusAppNotifyId);
|
||||||
|
this._focusAppNotifyId = 0;
|
||||||
|
}
|
||||||
|
if (this._overviewHidingId > 0) {
|
||||||
|
Main.overview.disconnect(this._overviewHidingId);
|
||||||
|
this._overviewHidingId = 0;
|
||||||
|
}
|
||||||
|
if (this._overviewShowingId > 0) {
|
||||||
|
Main.overview.disconnect(this._overviewShowingId);
|
||||||
|
this._overviewShowingId = 0;
|
||||||
|
}
|
||||||
|
if (this._switchWorkspaceNotifyId > 0) {
|
||||||
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||||
|
this._switchWorkspaceNotifyId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -894,10 +856,9 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
|
|||||||
'volume': imports.ui.status.volume.Indicator,
|
'volume': imports.ui.status.volume.Indicator,
|
||||||
'battery': imports.ui.status.power.Indicator,
|
'battery': imports.ui.status.power.Indicator,
|
||||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||||
'logo': imports.gdm.loginDialog.LogoMenuButton,
|
|
||||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
'system': imports.ui.status.system.Indicator,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Config.HAVE_BLUETOOTH)
|
if (Config.HAVE_BLUETOOTH)
|
||||||
@ -923,7 +884,7 @@ const Panel = new Lang.Class({
|
|||||||
|
|
||||||
this.statusArea = {};
|
this.statusArea = {};
|
||||||
|
|
||||||
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
this.menuManager = new PopupMenu.PopupMenuManager(this, { keybindingMode: Shell.KeyBindingMode.TOPBAR_POPUP });
|
||||||
|
|
||||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||||
this.actor.add_actor(this._leftBox);
|
this.actor.add_actor(this._leftBox);
|
||||||
@ -1077,17 +1038,18 @@ const Panel = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
openAppMenu: function() {
|
toggleAppMenu: function() {
|
||||||
let indicator = this.statusArea.appMenu;
|
let indicator = this.statusArea.appMenu;
|
||||||
if (!indicator) // appMenu not supported by current session mode
|
if (!indicator) // appMenu not supported by current session mode
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let menu = indicator.menu;
|
let menu = indicator.menu;
|
||||||
if (!indicator.actor.reactive || menu.isOpen)
|
if (!indicator.actor.reactive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
menu.open();
|
menu.toggle();
|
||||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
if (menu.isOpen)
|
||||||
|
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
set boxOpacity(value) {
|
set boxOpacity(value) {
|
||||||
|
@ -110,6 +110,7 @@ const Button = new Lang.Class({
|
|||||||
|
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||||
|
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
|
||||||
|
|
||||||
if (dontCreateMenu)
|
if (dontCreateMenu)
|
||||||
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
||||||
@ -183,7 +184,18 @@ const Button = new Lang.Class({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onVisibilityChanged: function() {
|
||||||
|
if (!this.menu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.actor.visible)
|
||||||
|
this.menu.close();
|
||||||
|
},
|
||||||
|
|
||||||
_onMenuKeyPress: function(actor, event) {
|
_onMenuKeyPress: function(actor, event) {
|
||||||
|
if (global.focus_manager.navigate_from_event(event))
|
||||||
|
return true;
|
||||||
|
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||||
let group = global.focus_manager.get_group(this.actor);
|
let group = global.focus_manager.get_group(this.actor);
|
||||||
|
1267
js/ui/popupMenu.js
1267
js/ui/popupMenu.js
File diff suppressed because it is too large
Load Diff
199
js/ui/remoteMenu.js
Normal file
199
js/ui/remoteMenu.js
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const ShellMenu = imports.gi.ShellMenu;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
function stripMnemonics(label) {
|
||||||
|
if (!label)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
// remove all underscores that are not followed by another underscore
|
||||||
|
return label.replace(/_([^_])/, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
function _insertItem(menu, trackerItem, position) {
|
||||||
|
let mapper;
|
||||||
|
|
||||||
|
if (trackerItem.get_is_separator())
|
||||||
|
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
|
||||||
|
else if (trackerItem.get_has_submenu())
|
||||||
|
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
|
||||||
|
else
|
||||||
|
mapper = new RemoteMenuItemMapper(trackerItem);
|
||||||
|
|
||||||
|
let item = mapper.menuItem;
|
||||||
|
menu.addMenuItem(item, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _removeItem(menu, position) {
|
||||||
|
let items = menu._getMenuItems();
|
||||||
|
items[position].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoteMenuSeparatorItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuSeparatorItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._updateLabel();
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RequestSubMenu = new Lang.Class({
|
||||||
|
Name: 'RequestSubMenu',
|
||||||
|
Extends: PopupMenu.PopupSubMenuMenuItem,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent('');
|
||||||
|
this._requestOpen = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setOpenState: function(open) {
|
||||||
|
this.emit('request-open', open);
|
||||||
|
this._requestOpen = open;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getOpenState: function() {
|
||||||
|
return this._requestOpen;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenuSubmenuItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuSubmenuItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
this.menuItem = new RequestSubMenu();
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._updateLabel();
|
||||||
|
|
||||||
|
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
|
||||||
|
_insertItem.bind(null, this.menuItem.menu),
|
||||||
|
_removeItem.bind(null, this.menuItem.menu));
|
||||||
|
|
||||||
|
this.menuItem.connect('request-open', Lang.bind(this, function(menu, open) {
|
||||||
|
this._trackerItem.request_submenu_shown(open);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._trackerItem.connect('notify::submenu-shown', Lang.bind(this, function() {
|
||||||
|
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._tracker.destroy();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenuItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
|
||||||
|
this.menuItem = new PopupMenu.PopupBaseMenuItem();
|
||||||
|
this._label = new St.Label();
|
||||||
|
this.menuItem.addActor(this._label);
|
||||||
|
this.menuItem.actor.label_actor = this._label;
|
||||||
|
|
||||||
|
this.menuItem.connect('activate', Lang.bind(this, function() {
|
||||||
|
this._trackerItem.activated();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._trackerItem.connect('notify::sensitive', Lang.bind(this, this._updateSensitivity));
|
||||||
|
this._trackerItem.connect('notify::role', Lang.bind(this, this._updateRole));
|
||||||
|
this._trackerItem.connect('notify::toggled', Lang.bind(this, this._updateDecoration));
|
||||||
|
|
||||||
|
this._updateLabel();
|
||||||
|
this._updateSensitivity();
|
||||||
|
this._updateRole();
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this._label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function() {
|
||||||
|
this.menuItem.setSensitive(this._trackerItem.sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDecoration: function() {
|
||||||
|
let ornamentForRole = {};
|
||||||
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
|
||||||
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
|
||||||
|
|
||||||
|
let ornament = PopupMenu.Ornament.NONE;
|
||||||
|
if (this._trackerItem.toggled)
|
||||||
|
ornament = ornamentForRole[this._trackerItem.role];
|
||||||
|
|
||||||
|
this.menuItem.setOrnament(ornament);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateRole: function() {
|
||||||
|
let a11yRoles = {};
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
|
||||||
|
|
||||||
|
let a11yRole = a11yRoles[this._trackerItem.role];
|
||||||
|
this.menuItem.actor.accessible_role = a11yRole;
|
||||||
|
|
||||||
|
this._updateDecoration();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenu = new Lang.Class({
|
||||||
|
Name: 'RemoteMenu',
|
||||||
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
|
_init: function(sourceActor, model, actionGroup) {
|
||||||
|
this.parent(sourceActor, 0.0, St.Side.TOP);
|
||||||
|
|
||||||
|
this._model = model;
|
||||||
|
this._actionGroup = actionGroup;
|
||||||
|
this._tracker = Shell.MenuTracker.new(this._actionGroup,
|
||||||
|
this._model,
|
||||||
|
null, /* action namespace */
|
||||||
|
_insertItem.bind(null, this),
|
||||||
|
_removeItem.bind(null, this));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._tracker.destroy();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
});
|
@ -7,7 +7,6 @@ const Lang = imports.lang;
|
|||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const FileUtils = imports.misc.fileUtils;
|
|
||||||
const Search = imports.ui.search;
|
const Search = imports.ui.search;
|
||||||
|
|
||||||
const KEY_FILE_GROUP = 'Shell Search Provider';
|
const KEY_FILE_GROUP = 'Shell Search Provider';
|
||||||
@ -60,108 +59,114 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
|||||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
|
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
|
||||||
|
|
||||||
function loadRemoteSearchProviders(addProviderCallback) {
|
function loadRemoteSearchProviders(addProviderCallback) {
|
||||||
let data = { loadedProviders: [],
|
let objectPaths = {};
|
||||||
objectPaths: {},
|
let loadedProviders = [];
|
||||||
addProviderCallback: addProviderCallback };
|
|
||||||
FileUtils.collectFromDatadirsAsync('search-providers',
|
|
||||||
{ loadedCallback: remoteProvidersLoaded,
|
|
||||||
processFile: loadRemoteSearchProvider,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadRemoteSearchProvider(file, info, data) {
|
function loadRemoteSearchProvider(file) {
|
||||||
let keyfile = new GLib.KeyFile();
|
let keyfile = new GLib.KeyFile();
|
||||||
let path = file.get_path();
|
let path = file.get_path();
|
||||||
|
|
||||||
try {
|
|
||||||
keyfile.load_from_file(path, 0);
|
|
||||||
} catch(e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!keyfile.has_group(KEY_FILE_GROUP))
|
|
||||||
return;
|
|
||||||
|
|
||||||
let remoteProvider;
|
|
||||||
try {
|
|
||||||
let group = KEY_FILE_GROUP;
|
|
||||||
let busName = keyfile.get_string(group, 'BusName');
|
|
||||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
|
||||||
|
|
||||||
if (data.objectPaths[objectPath])
|
|
||||||
return;
|
|
||||||
|
|
||||||
let appInfo = null;
|
|
||||||
try {
|
try {
|
||||||
let desktopId = keyfile.get_string(group, 'DesktopId');
|
keyfile.load_from_file(path, 0);
|
||||||
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
} catch(e) {
|
||||||
} catch (e) {
|
|
||||||
log('Ignoring search provider ' + path + ': missing DesktopId');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = '1';
|
if (!keyfile.has_group(KEY_FILE_GROUP))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let remoteProvider;
|
||||||
try {
|
try {
|
||||||
version = keyfile.get_string(group, 'Version');
|
let group = KEY_FILE_GROUP;
|
||||||
} catch (e) {
|
let busName = keyfile.get_string(group, 'BusName');
|
||||||
// ignore error
|
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||||
|
|
||||||
|
if (objectPaths[objectPath])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let appInfo = null;
|
||||||
|
try {
|
||||||
|
let desktopId = keyfile.get_string(group, 'DesktopId');
|
||||||
|
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
||||||
|
} catch (e) {
|
||||||
|
log('Ignoring search provider ' + path + ': missing DesktopId');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = '1';
|
||||||
|
try {
|
||||||
|
version = keyfile.get_string(group, 'Version');
|
||||||
|
} catch (e) {
|
||||||
|
// ignore error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version >= 2)
|
||||||
|
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
|
||||||
|
else
|
||||||
|
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
||||||
|
|
||||||
|
objectPaths[objectPath] = remoteProvider;
|
||||||
|
loadedProviders.push(remoteProvider);
|
||||||
|
} catch(e) {
|
||||||
|
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version >= 2)
|
|
||||||
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
|
|
||||||
else
|
|
||||||
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
|
||||||
|
|
||||||
data.objectPaths[objectPath] = remoteProvider;
|
|
||||||
data.loadedProviders.push(remoteProvider);
|
|
||||||
} catch(e) {
|
|
||||||
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function remoteProvidersLoaded(loadState) {
|
let dataDirs = GLib.get_system_data_dirs();
|
||||||
|
dataDirs.forEach(function(dataDir) {
|
||||||
|
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
|
||||||
|
let dir = Gio.File.new_for_path(path);
|
||||||
|
let fileEnum;
|
||||||
|
try {
|
||||||
|
fileEnum = dir.enumerate_children('standard::name,standard::type',
|
||||||
|
Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
} catch (e) {
|
||||||
|
fileEnum = null;
|
||||||
|
}
|
||||||
|
if (fileEnum != null) {
|
||||||
|
let info;
|
||||||
|
while ((info = fileEnum.next_file(null)))
|
||||||
|
loadRemoteSearchProvider(fileEnum.get_child(info));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||||
let sortOrder = searchSettings.get_strv('sort-order');
|
let sortOrder = searchSettings.get_strv('sort-order');
|
||||||
|
|
||||||
// Special case gnome-control-center to be always active and always first
|
// Special case gnome-control-center to be always active and always first
|
||||||
sortOrder.unshift('gnome-control-center.desktop');
|
sortOrder.unshift('gnome-control-center.desktop');
|
||||||
|
|
||||||
loadState.loadedProviders.sort(
|
loadedProviders.sort(function(providerA, providerB) {
|
||||||
function(providerA, providerB) {
|
let idxA, idxB;
|
||||||
let idxA, idxB;
|
let appIdA, appIdB;
|
||||||
let appIdA, appIdB;
|
|
||||||
|
|
||||||
appIdA = providerA.appInfo.get_id();
|
appIdA = providerA.appInfo.get_id();
|
||||||
appIdB = providerB.appInfo.get_id();
|
appIdB = providerB.appInfo.get_id();
|
||||||
|
|
||||||
idxA = sortOrder.indexOf(appIdA);
|
idxA = sortOrder.indexOf(appIdA);
|
||||||
idxB = sortOrder.indexOf(appIdB);
|
idxB = sortOrder.indexOf(appIdB);
|
||||||
|
|
||||||
// if no provider is found in the order, use alphabetical order
|
// if no provider is found in the order, use alphabetical order
|
||||||
if ((idxA == -1) && (idxB == -1)) {
|
if ((idxA == -1) && (idxB == -1)) {
|
||||||
let nameA = providerA.appInfo.get_name();
|
let nameA = providerA.appInfo.get_name();
|
||||||
let nameB = providerB.appInfo.get_name();
|
let nameB = providerB.appInfo.get_name();
|
||||||
|
|
||||||
return GLib.utf8_collate(nameA, nameB);
|
return GLib.utf8_collate(nameA, nameB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if providerA isn't found, it's sorted after providerB
|
// if providerA isn't found, it's sorted after providerB
|
||||||
if (idxA == -1)
|
if (idxA == -1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// if providerB isn't found, it's sorted after providerA
|
// if providerB isn't found, it's sorted after providerA
|
||||||
if (idxB == -1)
|
if (idxB == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// finally, if both providers are found, return their order in the list
|
// finally, if both providers are found, return their order in the list
|
||||||
return (idxA - idxB);
|
return (idxA - idxB);
|
||||||
});
|
});
|
||||||
|
|
||||||
loadState.loadedProviders.forEach(
|
loadedProviders.forEach(addProviderCallback);
|
||||||
function(provider) {
|
|
||||||
loadState.addProviderCallback(provider);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RemoteSearchProvider = new Lang.Class({
|
const RemoteSearchProvider = new Lang.Class({
|
||||||
@ -187,7 +192,9 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
|
|
||||||
createIcon: function(size, meta) {
|
createIcon: function(size, meta) {
|
||||||
let gicon;
|
let gicon;
|
||||||
if (meta['gicon']) {
|
if (meta['icon']) {
|
||||||
|
gicon = Gio.icon_deserialize(meta['icon']);
|
||||||
|
} else if (meta['gicon']) {
|
||||||
gicon = Gio.icon_new_for_string(meta['gicon']);
|
gicon = Gio.icon_new_for_string(meta['gicon']);
|
||||||
} else if (meta['icon-data']) {
|
} else if (meta['icon-data']) {
|
||||||
let [width, height, rowStride, hasAlpha,
|
let [width, height, rowStride, hasAlpha,
|
||||||
@ -203,7 +210,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
_getResultsFinished: function(results, error) {
|
_getResultsFinished: function(results, error) {
|
||||||
if (error)
|
if (error)
|
||||||
return;
|
return;
|
||||||
this.searchSystem.pushResults(this, results[0]);
|
this.searchSystem.setResults(this, results[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
@ -215,7 +222,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
this._cancellable);
|
this._cancellable);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
|
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||||
this.searchSystem.pushResults(this, []);
|
this.searchSystem.setResults(this, []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -228,7 +235,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
this._cancellable);
|
this._cancellable);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
|
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||||
this.searchSystem.pushResults(this, []);
|
this.searchSystem.setResults(this, []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -240,8 +247,12 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
let metas = results[0];
|
let metas = results[0];
|
||||||
let resultMetas = [];
|
let resultMetas = [];
|
||||||
for (let i = 0; i < metas.length; i++) {
|
for (let i = 0; i < metas.length; i++) {
|
||||||
for (let prop in metas[i])
|
for (let prop in metas[i]) {
|
||||||
metas[i][prop] = metas[i][prop].deep_unpack();
|
// we can use the serialized icon variant directly
|
||||||
|
if (prop != 'icon')
|
||||||
|
metas[i][prop] = metas[i][prop].deep_unpack();
|
||||||
|
}
|
||||||
|
|
||||||
resultMetas.push({ id: metas[i]['id'],
|
resultMetas.push({ id: metas[i]['id'],
|
||||||
name: metas[i]['name'],
|
name: metas[i]['name'],
|
||||||
description: metas[i]['description'],
|
description: metas[i]['description'],
|
||||||
|
@ -23,6 +23,7 @@ const Main = imports.ui.main;
|
|||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const ShellDBus = imports.ui.shellDBus;
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
|
const SmartcardManager = imports.misc.smartcardManager;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
|||||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||||
const LOCK_DELAY_KEY = 'lock-delay';
|
const LOCK_DELAY_KEY = 'lock-delay';
|
||||||
|
|
||||||
|
const LOCKED_STATE_STR = 'screenShield.locked';
|
||||||
// fraction of screen height the arrow must reach before completing
|
// fraction of screen height the arrow must reach before completing
|
||||||
// the slide up automatically
|
// the slide up automatically
|
||||||
const ARROW_DRAG_THRESHOLD = 0.1;
|
const ARROW_DRAG_THRESHOLD = 0.1;
|
||||||
@ -214,6 +216,7 @@ const NotificationsBox = new Lang.Class({
|
|||||||
|
|
||||||
if (musicNotification != null &&
|
if (musicNotification != null &&
|
||||||
this._musicBin.child == null) {
|
this._musicBin.child == null) {
|
||||||
|
musicNotification.acknowledged = true;
|
||||||
if (musicNotification.actor.get_parent() != null)
|
if (musicNotification.actor.get_parent() != null)
|
||||||
musicNotification.actor.get_parent().remove_actor(musicNotification.actor);
|
musicNotification.actor.get_parent().remove_actor(musicNotification.actor);
|
||||||
this._musicBin.child = musicNotification.actor;
|
this._musicBin.child = musicNotification.actor;
|
||||||
@ -246,6 +249,7 @@ const NotificationsBox = new Lang.Class({
|
|||||||
sourceCountChangedId: 0,
|
sourceCountChangedId: 0,
|
||||||
sourceTitleChangedId: 0,
|
sourceTitleChangedId: 0,
|
||||||
sourceUpdatedId: 0,
|
sourceUpdatedId: 0,
|
||||||
|
sourceNotifyId: 0,
|
||||||
musicNotification: null,
|
musicNotification: null,
|
||||||
sourceBox: null,
|
sourceBox: null,
|
||||||
titleLabel: null,
|
titleLabel: null,
|
||||||
@ -256,6 +260,12 @@ const NotificationsBox = new Lang.Class({
|
|||||||
this._showSource(source, obj, obj.sourceBox);
|
this._showSource(source, obj, obj.sourceBox);
|
||||||
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
||||||
|
|
||||||
|
if (obj.musicNotification) {
|
||||||
|
obj.sourceNotifyId = source.connect('notify', Lang.bind(this, function(source, notification) {
|
||||||
|
notification.acknowledged = true;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
obj.sourceCountChangedId = source.connect('count-updated', Lang.bind(this, function(source) {
|
obj.sourceCountChangedId = source.connect('count-updated', Lang.bind(this, function(source) {
|
||||||
this._countChanged(source, obj);
|
this._countChanged(source, obj);
|
||||||
}));
|
}));
|
||||||
@ -336,6 +346,8 @@ const NotificationsBox = new Lang.Class({
|
|||||||
if (obj.musicNotification) {
|
if (obj.musicNotification) {
|
||||||
this._musicBin.child = null;
|
this._musicBin.child = null;
|
||||||
obj.musicNotification = null;
|
obj.musicNotification = null;
|
||||||
|
|
||||||
|
source.disconnect(obj.sourceNotifyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
source.disconnect(obj.sourceDestroyId);
|
source.disconnect(obj.sourceDestroyId);
|
||||||
@ -505,6 +517,13 @@ const ScreenShield = new Lang.Class({
|
|||||||
|
|
||||||
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||||
|
|
||||||
|
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||||
|
this._smartcardManager.connect('smartcard-inserted',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
if (this._isLocked)
|
||||||
|
this._liftShield(true, 0);
|
||||||
|
}));
|
||||||
|
|
||||||
this._inhibitor = null;
|
this._inhibitor = null;
|
||||||
this._aboutToSuspend = false;
|
this._aboutToSuspend = false;
|
||||||
this._loginManager = LoginManager.getLoginManager();
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
@ -709,6 +728,8 @@ const ScreenShield = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
|
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
|
||||||
|
if (this._lockScreenState != MessageTray.State.HIDING)
|
||||||
|
return;
|
||||||
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
|
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
|
||||||
// Complete motion automatically
|
// Complete motion automatically
|
||||||
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
|
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
|
||||||
@ -751,19 +772,6 @@ const ScreenShield = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._becomeModal()) {
|
|
||||||
// We could not become modal, so we can't activate the
|
|
||||||
// screenshield. The user is probably very upset at this
|
|
||||||
// point, but any application using global grabs is broken
|
|
||||||
// Just tell him to stop using this app
|
|
||||||
//
|
|
||||||
// XXX: another option is to kick the user into the gdm login
|
|
||||||
// screen, where we're not affected by grabs
|
|
||||||
Main.notifyError(_("Unable to lock"),
|
|
||||||
_("Lock was blocked by an application"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._lightbox.actor.visible ||
|
if (this._lightbox.actor.visible ||
|
||||||
this._isActive) {
|
this._isActive) {
|
||||||
// We're either shown and active, or in the process of
|
// We're either shown and active, or in the process of
|
||||||
@ -777,6 +785,19 @@ const ScreenShield = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this._becomeModal()) {
|
||||||
|
// We could not become modal, so we can't activate the
|
||||||
|
// screenshield. The user is probably very upset at this
|
||||||
|
// point, but any application using global grabs is broken
|
||||||
|
// Just tell him to stop using this app
|
||||||
|
//
|
||||||
|
// XXX: another option is to kick the user into the gdm login
|
||||||
|
// screen, where we're not affected by grabs
|
||||||
|
Main.notifyError(_("Unable to lock"),
|
||||||
|
_("Lock was blocked by an application"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._lightbox.show();
|
this._lightbox.show();
|
||||||
|
|
||||||
if (this._activationTime == 0)
|
if (this._activationTime == 0)
|
||||||
@ -914,7 +935,6 @@ const ScreenShield = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||||
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dialog.allowCancel = allowCancel;
|
this._dialog.allowCancel = allowCancel;
|
||||||
@ -924,10 +944,6 @@ const ScreenShield = new Lang.Class({
|
|||||||
this._resetLockScreen(true, false);
|
this._resetLockScreen(true, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onUnlockSucceded: function() {
|
|
||||||
this.deactivate(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||||
// Don't reset the lock screen unless it is completely hidden
|
// Don't reset the lock screen unless it is completely hidden
|
||||||
// This prevents the shield going down if the lock-delay timeout
|
// This prevents the shield going down if the lock-delay timeout
|
||||||
@ -1113,13 +1129,30 @@ const ScreenShield = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
deactivate: function(animate) {
|
deactivate: function(animate) {
|
||||||
|
this._dialog.finish(Lang.bind(this, function() {
|
||||||
|
this._finishDeactivate(animate);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_finishDeactivate: function(animate) {
|
||||||
this._hideLockScreen(animate, 0);
|
this._hideLockScreen(animate, 0);
|
||||||
|
|
||||||
|
if (this._hasLockScreen)
|
||||||
|
this._clearLockScreen();
|
||||||
|
|
||||||
if (Main.sessionMode.currentMode == 'lock-screen')
|
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||||
Main.sessionMode.popMode('lock-screen');
|
Main.sessionMode.popMode('lock-screen');
|
||||||
if (Main.sessionMode.currentMode == 'unlock-dialog')
|
if (Main.sessionMode.currentMode == 'unlock-dialog')
|
||||||
Main.sessionMode.popMode('unlock-dialog');
|
Main.sessionMode.popMode('unlock-dialog');
|
||||||
|
|
||||||
|
if (this._dialog && !this._isGreeter)
|
||||||
|
this._dialog.popModal();
|
||||||
|
|
||||||
|
if (this._isModal) {
|
||||||
|
Main.popModal(this.actor);
|
||||||
|
this._isModal = false;
|
||||||
|
}
|
||||||
|
|
||||||
Tweener.addTween(this._lockDialogGroup, {
|
Tweener.addTween(this._lockDialogGroup, {
|
||||||
scale_x: 0,
|
scale_x: 0,
|
||||||
scale_y: 0,
|
scale_y: 0,
|
||||||
@ -1131,21 +1164,12 @@ const ScreenShield = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_completeDeactivate: function() {
|
_completeDeactivate: function() {
|
||||||
if (this._hasLockScreen)
|
|
||||||
this._clearLockScreen();
|
|
||||||
|
|
||||||
if (this._dialog && !this._isGreeter) {
|
if (this._dialog && !this._isGreeter) {
|
||||||
this._dialog.destroy();
|
this._dialog.destroy();
|
||||||
this._dialog = null;
|
this._dialog = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._lightbox.hide();
|
this._lightbox.hide();
|
||||||
|
|
||||||
if (this._isModal) {
|
|
||||||
Main.popModal(this.actor);
|
|
||||||
this._isModal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
|
||||||
if (this._becameActiveId != 0) {
|
if (this._becameActiveId != 0) {
|
||||||
@ -1163,6 +1187,7 @@ const ScreenShield = new Lang.Class({
|
|||||||
this._isLocked = false;
|
this._isLocked = false;
|
||||||
this.emit('active-changed');
|
this.emit('active-changed');
|
||||||
this.emit('locked-changed');
|
this.emit('locked-changed');
|
||||||
|
global.set_runtime_state(LOCKED_STATE_STR, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
activate: function(animate) {
|
activate: function(animate) {
|
||||||
@ -1179,6 +1204,7 @@ const ScreenShield = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._resetLockScreen(animate, animate);
|
this._resetLockScreen(animate, animate);
|
||||||
|
global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true));
|
||||||
|
|
||||||
// We used to set isActive and emit active-changed here,
|
// We used to set isActive and emit active-changed here,
|
||||||
// but now we do that from lockScreenShown, which means
|
// but now we do that from lockScreenShown, which means
|
||||||
@ -1200,10 +1226,26 @@ const ScreenShield = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the clipboard - otherwise, its contents may be leaked
|
||||||
|
// to unauthorized parties by pasting into the unlock dialog's
|
||||||
|
// password entry and unmasking the entry
|
||||||
|
St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, '');
|
||||||
|
St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, '');
|
||||||
|
|
||||||
this._isLocked = true;
|
this._isLocked = true;
|
||||||
this.activate(animate);
|
this.activate(animate);
|
||||||
|
|
||||||
this.emit('locked-changed');
|
this.emit('locked-changed');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// If the previous shell crashed, and gnome-session restarted us, then re-lock
|
||||||
|
lockIfWasLocked: function() {
|
||||||
|
let wasLocked = global.get_runtime_state('b', LOCKED_STATE_STR);
|
||||||
|
if (wasLocked === null)
|
||||||
|
return;
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||||
|
this.lock(false);
|
||||||
|
}));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ScreenShield.prototype);
|
Signals.addSignalMethods(ScreenShield.prototype);
|
||||||
|
140
js/ui/screencast.js
Normal file
140
js/ui/screencast.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Hash = imports.misc.hash;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
|
||||||
|
<method name="Screencast">
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
<method name="ScreencastArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
<method name="StopScreencast">
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
</method>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const ScreencastService = new Lang.Class({
|
||||||
|
Name: 'ScreencastService',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
|
||||||
|
|
||||||
|
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||||
|
|
||||||
|
this._recorders = new Hash.Map();
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated',
|
||||||
|
Lang.bind(this, this._sessionModeChanged));
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureRecorderForSender: function(sender) {
|
||||||
|
let recorder = this._recorders.get(sender);
|
||||||
|
if (!recorder) {
|
||||||
|
recorder = new Shell.Recorder({ stage: global.stage });
|
||||||
|
recorder._watchNameId =
|
||||||
|
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
|
||||||
|
Lang.bind(this, this._onNameVanished));
|
||||||
|
this._recorders.set(sender, recorder);
|
||||||
|
}
|
||||||
|
return recorder;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionModeChanged: function() {
|
||||||
|
if (Main.sessionMode.allowScreencast)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (let sender in this._recorders.keys())
|
||||||
|
this._recorders.delete(sender);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNameVanished: function(connection, name) {
|
||||||
|
this._stopRecordingForSender(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
_stopRecordingForSender: function(sender) {
|
||||||
|
let recorder = this._recorders.get(sender);
|
||||||
|
if (!recorder)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Gio.bus_unwatch_name(recorder._watchNameId);
|
||||||
|
recorder.close();
|
||||||
|
this._recorders.delete(sender);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_applyOptionalParameters: function(recorder, options) {
|
||||||
|
for (let option in options)
|
||||||
|
options[option] = options[option].deep_unpack();
|
||||||
|
|
||||||
|
if (options['pipeline'])
|
||||||
|
recorder.set_pipeline(options['pipeline']);
|
||||||
|
if (options['framerate'])
|
||||||
|
recorder.set_framerate(options['framerate']);
|
||||||
|
if (options['draw-cursor'])
|
||||||
|
recorder.set_draw_cursor(options['draw-cursor']);
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreencastAsync: function(params, invocation) {
|
||||||
|
let returnValue = [false, ''];
|
||||||
|
if (!Main.sessionMode.allowScreencast)
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
|
||||||
|
let sender = invocation.get_sender();
|
||||||
|
let recorder = this._ensureRecorderForSender(sender);
|
||||||
|
if (!recorder.is_recording()) {
|
||||||
|
let [fileTemplate, options] = params;
|
||||||
|
|
||||||
|
recorder.set_file_template(fileTemplate);
|
||||||
|
this._applyOptionalParameters(recorder, options);
|
||||||
|
let [success, fileName] = recorder.record();
|
||||||
|
returnValue = [success, fileName ? fileName : ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreencastAreaAsync: function(params, invocation) {
|
||||||
|
let returnValue = [false, ''];
|
||||||
|
if (!Main.sessionMode.allowScreencast)
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
|
||||||
|
let sender = invocation.get_sender();
|
||||||
|
let recorder = this._ensureRecorderForSender(sender);
|
||||||
|
|
||||||
|
if (!recorder.is_recording()) {
|
||||||
|
let [x, y, width, height, fileTemplate, options] = params;
|
||||||
|
|
||||||
|
recorder.set_file_template(fileTemplate);
|
||||||
|
recorder.set_area(x, y, width, height);
|
||||||
|
this._applyOptionalParameters(recorder, options);
|
||||||
|
let [success, fileName] = recorder.record();
|
||||||
|
returnValue = [success, fileName ? fileName : ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
},
|
||||||
|
|
||||||
|
StopScreencastAsync: function(params, invocation) {
|
||||||
|
let success = this._stopRecordingForSender(invocation.get_sender());
|
||||||
|
invocation.return_value(GLib.Variant.new('(b)', [success]));
|
||||||
|
}
|
||||||
|
});
|
@ -31,7 +31,7 @@ const SearchSystem = new Lang.Class({
|
|||||||
|
|
||||||
let remoteIndex = this._remoteProviders.indexOf(provider);
|
let remoteIndex = this._remoteProviders.indexOf(provider);
|
||||||
if (remoteIndex != -1)
|
if (remoteIndex != -1)
|
||||||
this._remoteProviders.splice(index, 1);
|
this._remoteProviders.splice(remoteIndex, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
getProviders: function() {
|
getProviders: function() {
|
||||||
@ -51,7 +51,7 @@ const SearchSystem = new Lang.Class({
|
|||||||
this._previousResults = [];
|
this._previousResults = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
pushResults: function(provider, results) {
|
setResults: function(provider, results) {
|
||||||
let i = this._providers.indexOf(provider);
|
let i = this._providers.indexOf(provider);
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
return;
|
return;
|
||||||
|
@ -180,13 +180,82 @@ const GridSearchResult = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListSearchResults = new Lang.Class({
|
const SearchResultsBase = new Lang.Class({
|
||||||
Name: 'ListSearchResults',
|
Name: 'SearchResultsBase',
|
||||||
|
|
||||||
_init: function(provider) {
|
_init: function(provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
|
this._terms = [];
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ style_class: 'search-section',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._resultDisplayBin = new St.Bin({ x_fill: true,
|
||||||
|
y_fill: true });
|
||||||
|
this.actor.add(this._resultDisplayBin, { expand: true });
|
||||||
|
|
||||||
|
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||||
|
this.actor.add(separator.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.actor.destroy();
|
||||||
|
this._terms = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearResultDisplay: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function() {
|
||||||
|
this._clearResultDisplay();
|
||||||
|
this.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_keyFocusIn: function(icon) {
|
||||||
|
this.emit('key-focus-in', icon);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setMoreIconVisible: function(visible) {
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSearch: function(providerResults, terms, callback) {
|
||||||
|
this._terms = terms;
|
||||||
|
|
||||||
|
if (providerResults.length == 0) {
|
||||||
|
this._clearResultDisplay();
|
||||||
|
this.actor.hide();
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
let maxResults = this._getMaxDisplayedResults();
|
||||||
|
let results = providerResults.slice(0, maxResults);
|
||||||
|
let hasMoreResults = results.length < providerResults.length;
|
||||||
|
|
||||||
|
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
// To avoid CSS transitions causing flickering when
|
||||||
|
// the first search result stays the same, we hide the
|
||||||
|
// content while filling in the results.
|
||||||
|
this.actor.hide();
|
||||||
|
this._clearResultDisplay();
|
||||||
|
this._renderResults(metas);
|
||||||
|
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
|
||||||
|
this.actor.show();
|
||||||
|
callback();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ListSearchResults = new Lang.Class({
|
||||||
|
Name: 'ListSearchResults',
|
||||||
|
Extends: SearchResultsBase,
|
||||||
|
|
||||||
|
_init: function(provider) {
|
||||||
|
this.parent(provider);
|
||||||
|
|
||||||
|
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||||
this.providerIcon = new ProviderIcon(provider);
|
this.providerIcon = new ProviderIcon(provider);
|
||||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
@ -194,48 +263,27 @@ const ListSearchResults = new Lang.Class({
|
|||||||
Main.overview.toggle();
|
Main.overview.toggle();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.actor.add(this.providerIcon, { x_fill: false,
|
this._container.add(this.providerIcon, { x_fill: false,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this.actor.add(this._content, { expand: true });
|
this._container.add(this._content, { expand: true });
|
||||||
|
|
||||||
this._notDisplayedResult = [];
|
this._resultDisplayBin.set_child(this._container);
|
||||||
this._terms = [];
|
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultsForDisplay: function() {
|
_setMoreIconVisible: function(visible) {
|
||||||
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
|
this.providerIcon.moreIcon.visible = true;
|
||||||
let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
|
|
||||||
|
|
||||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
|
||||||
return newResults;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getVisibleResultCount: function() {
|
_getMaxDisplayedResults: function() {
|
||||||
return this._content.get_n_children();
|
return MAX_LIST_SEARCH_RESULTS_ROWS;
|
||||||
},
|
},
|
||||||
|
|
||||||
hasMoreResults: function() {
|
_renderResults: function(metas) {
|
||||||
return this._notDisplayedResult.length > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
setResults: function(results, terms) {
|
|
||||||
// copy the lists
|
|
||||||
this._notDisplayedResult = results.slice(0);
|
|
||||||
this._terms = terms.slice(0);
|
|
||||||
this._pendingClear = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_keyFocusIn: function(icon) {
|
|
||||||
this.emit('key-focus-in', icon);
|
|
||||||
},
|
|
||||||
|
|
||||||
renderResults: function(metas) {
|
|
||||||
for (let i = 0; i < metas.length; i++) {
|
for (let i = 0; i < metas.length; i++) {
|
||||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
@ -243,13 +291,12 @@ const ListSearchResults = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clear: function () {
|
_clearResultDisplay: function () {
|
||||||
this._content.destroy_all_children();
|
this._content.destroy_all_children();
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getFirstResult: function() {
|
getFirstResult: function() {
|
||||||
if (this.getVisibleResultCount() > 0)
|
if (this._content.get_n_children() > 0)
|
||||||
return this._content.get_child_at_index(0)._delegate;
|
return this._content.get_child_at_index(0)._delegate;
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
@ -259,50 +306,24 @@ Signals.addSignalMethods(ListSearchResults.prototype);
|
|||||||
|
|
||||||
const GridSearchResults = new Lang.Class({
|
const GridSearchResults = new Lang.Class({
|
||||||
Name: 'GridSearchResults',
|
Name: 'GridSearchResults',
|
||||||
|
Extends: SearchResultsBase,
|
||||||
|
|
||||||
_init: function(provider) {
|
_init: function(provider) {
|
||||||
this.provider = provider;
|
this.parent(provider);
|
||||||
|
|
||||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||||
xAlign: St.Align.START });
|
xAlign: St.Align.START });
|
||||||
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
|
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||||
|
this._bin.set_child(this._grid.actor);
|
||||||
|
|
||||||
this.actor.set_child(this._grid.actor);
|
this._resultDisplayBin.set_child(this._bin);
|
||||||
|
|
||||||
this._notDisplayedResult = [];
|
|
||||||
this._terms = [];
|
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultsForDisplay: function() {
|
_getMaxDisplayedResults: function() {
|
||||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
return this._grid.childrenInRow(this._bin.width) * this._grid.getRowLimit();
|
||||||
let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
|
|
||||||
- alreadyVisible;
|
|
||||||
|
|
||||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
|
||||||
return newResults;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getVisibleResultCount: function() {
|
_renderResults: function(metas) {
|
||||||
return this._grid.visibleItemsCount();
|
|
||||||
},
|
|
||||||
|
|
||||||
hasMoreResults: function() {
|
|
||||||
return this._notDisplayedResult.length > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
setResults: function(results, terms) {
|
|
||||||
// copy the lists
|
|
||||||
this._notDisplayedResult = results.slice(0);
|
|
||||||
this._terms = terms.slice(0);
|
|
||||||
this._pendingClear = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_keyFocusIn: function(icon) {
|
|
||||||
this.emit('key-focus-in', icon);
|
|
||||||
},
|
|
||||||
|
|
||||||
renderResults: function(metas) {
|
|
||||||
for (let i = 0; i < metas.length; i++) {
|
for (let i = 0; i < metas.length; i++) {
|
||||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
@ -310,13 +331,12 @@ const GridSearchResults = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clear: function () {
|
_clearResultDisplay: function () {
|
||||||
this._grid.removeAll();
|
this._grid.removeAll();
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getFirstResult: function() {
|
getFirstResult: function() {
|
||||||
if (this.getVisibleResultCount() > 0)
|
if (this._grid.visibleItemsCount() > 0)
|
||||||
return this._grid.getItemAtIndex(0)._delegate;
|
return this._grid.getItemAtIndex(0)._delegate;
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
@ -366,9 +386,9 @@ const SearchResults = new Lang.Class({
|
|||||||
this._content.add(this._statusBin, { expand: true });
|
this._content.add(this._statusBin, { expand: true });
|
||||||
this._statusBin.add_actor(this._statusText);
|
this._statusBin.add_actor(this._statusText);
|
||||||
this._providers = this._searchSystem.getProviders();
|
this._providers = this._searchSystem.getProviders();
|
||||||
this._providerMeta = [];
|
this._providerDisplays = {};
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
this.createProviderMeta(this._providers[i]);
|
this.createProviderDisplay(this._providers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._highlightDefault = false;
|
this._highlightDefault = false;
|
||||||
@ -386,61 +406,33 @@ const SearchResults = new Lang.Class({
|
|||||||
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
|
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
|
||||||
},
|
},
|
||||||
|
|
||||||
createProviderMeta: function(provider) {
|
createProviderDisplay: function(provider) {
|
||||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
let providerDisplay = null;
|
||||||
vertical: true });
|
|
||||||
let providerIcon = null;
|
|
||||||
let resultDisplay = null;
|
|
||||||
|
|
||||||
if (provider.appInfo) {
|
if (provider.appInfo) {
|
||||||
resultDisplay = new ListSearchResults(provider);
|
providerDisplay = new ListSearchResults(provider);
|
||||||
providerIcon = resultDisplay.providerIcon;
|
|
||||||
} else {
|
} else {
|
||||||
resultDisplay = new GridSearchResults(provider);
|
providerDisplay = new GridSearchResults(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
resultDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
|
this._providerDisplays[provider.id] = providerDisplay;
|
||||||
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
|
this._content.add(providerDisplay.actor);
|
||||||
x_fill: true,
|
|
||||||
y_fill: true });
|
|
||||||
providerBox.add(resultDisplayBin, { expand: true });
|
|
||||||
|
|
||||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
|
||||||
providerBox.add(separator.actor);
|
|
||||||
|
|
||||||
this._providerMeta.push({ provider: provider,
|
|
||||||
actor: providerBox,
|
|
||||||
icon: providerIcon,
|
|
||||||
resultDisplay: resultDisplay });
|
|
||||||
this._content.add(providerBox);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyProviderMeta: function(provider) {
|
destroyProviderDisplay: function(provider) {
|
||||||
for (let i=0; i < this._providerMeta.length; i++) {
|
this._providerDisplays[provider.id].destroy();
|
||||||
let meta = this._providerMeta[i];
|
delete this._providerDisplays[provider.id];
|
||||||
if (meta.provider == provider) {
|
|
||||||
meta.actor.destroy();
|
|
||||||
this._providerMeta.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearDisplay: function() {
|
_clearDisplay: function() {
|
||||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
let meta = this._providerMeta[i];
|
let provider = this._providers[i];
|
||||||
meta.resultDisplay.clear();
|
let providerDisplay = this._providerDisplays[provider.id];
|
||||||
meta.actor.hide();
|
providerDisplay.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearDisplayForProvider: function(provider) {
|
|
||||||
let meta = this._metaForProvider(provider);
|
|
||||||
meta.resultDisplay.clear();
|
|
||||||
meta.actor.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this._searchSystem.reset();
|
this._searchSystem.reset();
|
||||||
this._statusBin.hide();
|
this._statusBin.hide();
|
||||||
@ -454,20 +446,17 @@ const SearchResults = new Lang.Class({
|
|||||||
this._statusBin.show();
|
this._statusBin.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
_metaForProvider: function(provider) {
|
|
||||||
return this._providerMeta[this._providers.indexOf(provider)];
|
|
||||||
},
|
|
||||||
|
|
||||||
_maybeSetInitialSelection: function() {
|
_maybeSetInitialSelection: function() {
|
||||||
let newDefaultResult = null;
|
let newDefaultResult = null;
|
||||||
|
|
||||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
let meta = this._providerMeta[i];
|
let provider = this._providers[i];
|
||||||
|
let display = this._providerDisplays[provider.id];
|
||||||
|
|
||||||
if (!meta.actor.visible)
|
if (!display.actor.visible)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let firstResult = meta.resultDisplay.getFirstResult();
|
let firstResult = display.getFirstResult();
|
||||||
if (firstResult) {
|
if (firstResult) {
|
||||||
newDefaultResult = firstResult;
|
newDefaultResult = firstResult;
|
||||||
break; // select this one!
|
break; // select this one!
|
||||||
@ -487,11 +476,14 @@ const SearchResults = new Lang.Class({
|
|||||||
_updateStatusText: function () {
|
_updateStatusText: function () {
|
||||||
let haveResults = false;
|
let haveResults = false;
|
||||||
|
|
||||||
for (let i = 0; i < this._providerMeta.length; ++i)
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
|
let provider = this._providers[i];
|
||||||
|
let display = this._providerDisplays[provider.id];
|
||||||
|
if (display.getFirstResult()) {
|
||||||
haveResults = true;
|
haveResults = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!haveResults) {
|
if (!haveResults) {
|
||||||
this._statusText.set_text(_("No results."));
|
this._statusText.set_text(_("No results."));
|
||||||
@ -504,42 +496,12 @@ const SearchResults = new Lang.Class({
|
|||||||
_updateResults: function(searchSystem, results) {
|
_updateResults: function(searchSystem, results) {
|
||||||
let terms = searchSystem.getTerms();
|
let terms = searchSystem.getTerms();
|
||||||
let [provider, providerResults] = results;
|
let [provider, providerResults] = results;
|
||||||
let meta = this._metaForProvider(provider);
|
let display = this._providerDisplays[provider.id];
|
||||||
|
|
||||||
if (providerResults.length == 0) {
|
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
|
||||||
this._clearDisplayForProvider(provider);
|
|
||||||
meta.resultDisplay.setResults([], []);
|
|
||||||
this._maybeSetInitialSelection();
|
this._maybeSetInitialSelection();
|
||||||
this._updateStatusText();
|
this._updateStatusText();
|
||||||
} else {
|
}));
|
||||||
meta.resultDisplay.setResults(providerResults, terms);
|
|
||||||
let results = meta.resultDisplay.getResultsForDisplay();
|
|
||||||
|
|
||||||
if (meta.icon)
|
|
||||||
meta.icon.moreIcon.visible =
|
|
||||||
meta.resultDisplay.hasMoreResults() &&
|
|
||||||
provider.canLaunchSearch;
|
|
||||||
|
|
||||||
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
|
||||||
this._clearDisplayForProvider(provider);
|
|
||||||
meta.actor.show();
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
meta.resultDisplay.renderResults(metas);
|
|
||||||
this._maybeSetInitialSelection();
|
|
||||||
this._updateStatusText();
|
|
||||||
|
|
||||||
this._content.show();
|
|
||||||
if (this._content.contains(focus))
|
|
||||||
global.stage.set_key_focus(focus);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
activateDefault: function() {
|
activateDefault: function() {
|
||||||
|
@ -16,6 +16,7 @@ const _modes = {
|
|||||||
'restrictive': {
|
'restrictive': {
|
||||||
parentMode: null,
|
parentMode: null,
|
||||||
stylesheetName: 'gnome-shell.css',
|
stylesheetName: 'gnome-shell.css',
|
||||||
|
overridesSchema: 'org.gnome.shell.overrides',
|
||||||
hasOverview: false,
|
hasOverview: false,
|
||||||
showCalendarEvents: false,
|
showCalendarEvents: false,
|
||||||
allowSettings: false,
|
allowSettings: false,
|
||||||
@ -45,7 +46,7 @@ const _modes = {
|
|||||||
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
||||||
components: ['polkitAgent'],
|
components: ['polkitAgent'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['logo'],
|
left: [],
|
||||||
center: ['dateMenu'],
|
center: ['dateMenu'],
|
||||||
right: ['a11yGreeter', 'display', 'keyboard',
|
right: ['a11yGreeter', 'display', 'keyboard',
|
||||||
'volume', 'battery', 'powerMenu']
|
'volume', 'battery', 'powerMenu']
|
||||||
@ -59,7 +60,7 @@ const _modes = {
|
|||||||
unlockDialog: undefined,
|
unlockDialog: undefined,
|
||||||
components: ['polkitAgent', 'telepathyClient'],
|
components: ['polkitAgent', 'telepathyClient'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['userMenu'],
|
left: [],
|
||||||
center: [],
|
center: [],
|
||||||
right: ['lockScreen']
|
right: ['lockScreen']
|
||||||
},
|
},
|
||||||
@ -71,24 +72,13 @@ const _modes = {
|
|||||||
unlockDialog: undefined,
|
unlockDialog: undefined,
|
||||||
components: ['polkitAgent', 'telepathyClient'],
|
components: ['polkitAgent', 'telepathyClient'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['userMenu'],
|
left: [],
|
||||||
center: [],
|
center: [],
|
||||||
right: ['a11y', 'keyboard', 'lockScreen']
|
right: ['a11y', 'keyboard', 'lockScreen']
|
||||||
},
|
},
|
||||||
panelStyle: 'unlock-screen'
|
panelStyle: 'unlock-screen'
|
||||||
},
|
},
|
||||||
|
|
||||||
'initial-setup': {
|
|
||||||
hasWindows: true,
|
|
||||||
isPrimary: true,
|
|
||||||
components: ['networkAgent', 'keyring'],
|
|
||||||
panel: {
|
|
||||||
left: [],
|
|
||||||
center: ['dateMenu'],
|
|
||||||
right: ['a11yGreeter', 'keyboard', 'volume', 'battery']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'user': {
|
'user': {
|
||||||
hasOverview: true,
|
hasOverview: true,
|
||||||
showCalendarEvents: true,
|
showCalendarEvents: true,
|
||||||
@ -107,7 +97,7 @@ const _modes = {
|
|||||||
left: ['activities', 'appMenu'],
|
left: ['activities', 'appMenu'],
|
||||||
center: ['dateMenu'],
|
center: ['dateMenu'],
|
||||||
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
||||||
'network', 'battery', 'userMenu']
|
'network', 'battery', 'system']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -195,6 +185,10 @@ const SessionMode = new Lang.Class({
|
|||||||
return this._modeStack[this._modeStack.length - 1];
|
return this._modeStack[this._modeStack.length - 1];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get allowScreencast() {
|
||||||
|
return this.components.indexOf('recorder') != -1;
|
||||||
|
},
|
||||||
|
|
||||||
_sync: function() {
|
_sync: function() {
|
||||||
let params = this._modes[this.currentMode];
|
let params = this._modes[this.currentMode];
|
||||||
let defaults;
|
let defaults;
|
||||||
|
@ -12,6 +12,7 @@ const ExtensionDownloader = imports.ui.extensionDownloader;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Hash = imports.misc.hash;
|
const Hash = imports.misc.hash;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Screencast = imports.ui.screencast;
|
||||||
const Screenshot = imports.ui.screenshot;
|
const Screenshot = imports.ui.screenshot;
|
||||||
|
|
||||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||||
@ -20,6 +21,7 @@ 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="FocusSearch"/>
|
||||||
<method name="ShowOSD">
|
<method name="ShowOSD">
|
||||||
<arg type="a{sv}" direction="in" name="params"/>
|
<arg type="a{sv}" direction="in" name="params"/>
|
||||||
</method>
|
</method>
|
||||||
@ -39,6 +41,7 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
|||||||
<signal name="AcceleratorActivated">
|
<signal name="AcceleratorActivated">
|
||||||
<arg name="action" type="u" />
|
<arg name="action" type="u" />
|
||||||
<arg name="deviceid" type="u" />
|
<arg name="deviceid" type="u" />
|
||||||
|
<arg name="timestamp" type="u" />
|
||||||
</signal>
|
</signal>
|
||||||
<property name="Mode" type="s" access="read" />
|
<property name="Mode" type="s" access="read" />
|
||||||
<property name="OverviewActive" type="b" access="readwrite" />
|
<property name="OverviewActive" type="b" access="readwrite" />
|
||||||
@ -70,14 +73,15 @@ const GnomeShell = new Lang.Class({
|
|||||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||||
|
|
||||||
this._extensionsService = new GnomeShellExtensions();
|
this._extensionsService = new GnomeShellExtensions();
|
||||||
|
this._screencastService = new Screencast.ScreencastService();
|
||||||
this._screenshotService = new Screenshot.ScreenshotService();
|
this._screenshotService = new Screenshot.ScreenshotService();
|
||||||
|
|
||||||
this._grabbedAccelerators = new Hash.Map();
|
this._grabbedAccelerators = new Hash.Map();
|
||||||
this._grabbers = new Hash.Map();
|
this._grabbers = new Hash.Map();
|
||||||
|
|
||||||
global.display.connect('accelerator-activated', Lang.bind(this,
|
global.display.connect('accelerator-activated', Lang.bind(this,
|
||||||
function(display, action, deviceid) {
|
function(display, action, deviceid, timestamp) {
|
||||||
this._emitAcceleratorActivated(action, deviceid);
|
this._emitAcceleratorActivated(action, deviceid, timestamp);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -97,7 +101,7 @@ const GnomeShell = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
Eval: function(code) {
|
Eval: function(code) {
|
||||||
if (!global.settings.get_boolean('development-tools'))
|
if (!global.settings.get_boolean('development-tools'))
|
||||||
return [false, null];
|
return [false, ''];
|
||||||
|
|
||||||
let returnValue;
|
let returnValue;
|
||||||
let success;
|
let success;
|
||||||
@ -114,6 +118,10 @@ const GnomeShell = new Lang.Class({
|
|||||||
return [success, returnValue];
|
return [success, returnValue];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
FocusSearch: function() {
|
||||||
|
Main.overview.focusSearch();
|
||||||
|
},
|
||||||
|
|
||||||
ShowOSD: function(params) {
|
ShowOSD: function(params) {
|
||||||
for (let param in params)
|
for (let param in params)
|
||||||
params[param] = params[param].deep_unpack();
|
params[param] = params[param].deep_unpack();
|
||||||
@ -159,7 +167,7 @@ const GnomeShell = new Lang.Class({
|
|||||||
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
|
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
|
||||||
},
|
},
|
||||||
|
|
||||||
_emitAcceleratorActivated: function(action, deviceid) {
|
_emitAcceleratorActivated: function(action, deviceid, timestamp) {
|
||||||
let destination = this._grabbedAccelerators.get(action);
|
let destination = this._grabbedAccelerators.get(action);
|
||||||
if (!destination)
|
if (!destination)
|
||||||
return;
|
return;
|
||||||
@ -170,7 +178,7 @@ const GnomeShell = new Lang.Class({
|
|||||||
this._dbusImpl.get_object_path(),
|
this._dbusImpl.get_object_path(),
|
||||||
info ? info.name : null,
|
info ? info.name : null,
|
||||||
'AcceleratorActivated',
|
'AcceleratorActivated',
|
||||||
GLib.Variant.new('(uu)', [action, deviceid]));
|
GLib.Variant.new('(uuu)', [action, deviceid, timestamp]));
|
||||||
},
|
},
|
||||||
|
|
||||||
_grabAcceleratorForSender: function(accelerator, flags, sender) {
|
_grabAcceleratorForSender: function(accelerator, flags, sender) {
|
||||||
|
@ -14,9 +14,7 @@ const EntryMenu = new Lang.Class({
|
|||||||
Name: 'ShellEntryMenu',
|
Name: 'ShellEntryMenu',
|
||||||
Extends: PopupMenu.PopupMenu,
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
_init: function(entry, params) {
|
_init: function(entry) {
|
||||||
params = Params.parse (params, { isPassword: false });
|
|
||||||
|
|
||||||
this.parent(entry, 0, St.Side.TOP);
|
this.parent(entry, 0, St.Side.TOP);
|
||||||
|
|
||||||
this.actor.add_style_class_name('entry-context-menu');
|
this.actor.add_style_class_name('entry-context-menu');
|
||||||
@ -37,8 +35,6 @@ const EntryMenu = new Lang.Class({
|
|||||||
this._pasteItem = item;
|
this._pasteItem = item;
|
||||||
|
|
||||||
this._passwordItem = null;
|
this._passwordItem = null;
|
||||||
if (params.isPassword)
|
|
||||||
this._makePasswordItem();
|
|
||||||
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
Main.uiGroup.add_actor(this.actor);
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
@ -53,19 +49,21 @@ const EntryMenu = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
get isPassword() {
|
get isPassword() {
|
||||||
return this._passwordItem != null;
|
return this._passwordItem != null;
|
||||||
},
|
},
|
||||||
|
|
||||||
set isPassword(v) {
|
set isPassword(v) {
|
||||||
if (v == this.isPassword)
|
if (v == this.isPassword)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (v)
|
if (v) {
|
||||||
this._makePasswordItem();
|
this._makePasswordItem();
|
||||||
else {
|
this._entry.input_purpose = Gtk.InputPurpose.PASSWORD;
|
||||||
this._passwordItem.destroy();
|
} else {
|
||||||
this._passwordItem = null;
|
this._passwordItem.destroy();
|
||||||
}
|
this._passwordItem = null;
|
||||||
|
this._entry.input_purpose = Gtk.InputPurpose.FREE_FORM;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(animate) {
|
open: function(animate) {
|
||||||
@ -155,7 +153,10 @@ function addContextMenu(entry, params) {
|
|||||||
if (entry.menu)
|
if (entry.menu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entry.menu = new EntryMenu(entry, params);
|
params = Params.parse (params, { isPassword: false });
|
||||||
|
|
||||||
|
entry.menu = new EntryMenu(entry);
|
||||||
|
entry.menu.isPassword = params.isPassword;
|
||||||
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
||||||
entry._menuManager.addMenu(entry.menu);
|
entry._menuManager.addMenu(entry.menu);
|
||||||
|
|
||||||
|
210
js/ui/slider.js
Normal file
210
js/ui/slider.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Cairo = imports.cairo;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||||
|
|
||||||
|
const Slider = new Lang.Class({
|
||||||
|
Name: "Slider",
|
||||||
|
|
||||||
|
_init: function(value) {
|
||||||
|
if (isNaN(value))
|
||||||
|
// Avoid spreading NaNs around
|
||||||
|
throw TypeError('The slider value must be a number');
|
||||||
|
this._value = Math.max(Math.min(value, 1), 0);
|
||||||
|
|
||||||
|
this.actor = new St.DrawingArea({ style_class: 'slider',
|
||||||
|
can_focus: true,
|
||||||
|
reactive: true });
|
||||||
|
this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
||||||
|
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
|
||||||
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
|
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
|
|
||||||
|
this._releaseId = this._motionId = 0;
|
||||||
|
this._dragging = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
if (isNaN(value))
|
||||||
|
throw TypeError('The slider value must be a number');
|
||||||
|
|
||||||
|
this._value = Math.max(Math.min(value, 1), 0);
|
||||||
|
this.actor.queue_repaint();
|
||||||
|
},
|
||||||
|
|
||||||
|
_sliderRepaint: function(area) {
|
||||||
|
let cr = area.get_context();
|
||||||
|
let themeNode = area.get_theme_node();
|
||||||
|
let [width, height] = area.get_surface_size();
|
||||||
|
|
||||||
|
let handleRadius = themeNode.get_length('-slider-handle-radius');
|
||||||
|
|
||||||
|
let handleBorderWidth = themeNode.get_length('-slider-handle-border-width');
|
||||||
|
let [hasHandleColor, handleBorderColor] =
|
||||||
|
themeNode.lookup_color('-slider-handle-border-color', false);
|
||||||
|
|
||||||
|
let sliderHeight = themeNode.get_length('-slider-height');
|
||||||
|
|
||||||
|
let sliderBorderWidth = themeNode.get_length('-slider-border-width');
|
||||||
|
let sliderBorderRadius = Math.min(width, sliderHeight) / 2;
|
||||||
|
|
||||||
|
let sliderBorderColor = themeNode.get_color('-slider-border-color');
|
||||||
|
let sliderColor = themeNode.get_color('-slider-background-color');
|
||||||
|
|
||||||
|
let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color');
|
||||||
|
let sliderActiveColor = themeNode.get_color('-slider-active-background-color');
|
||||||
|
|
||||||
|
const TAU = Math.PI * 2;
|
||||||
|
|
||||||
|
let handleX = handleRadius + (width - 2 * handleRadius) * this._value;
|
||||||
|
|
||||||
|
cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4);
|
||||||
|
cr.lineTo(handleX, (height - sliderHeight) / 2);
|
||||||
|
cr.lineTo(handleX, (height + sliderHeight) / 2);
|
||||||
|
cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2);
|
||||||
|
Clutter.cairo_set_source_color(cr, sliderActiveColor);
|
||||||
|
cr.fillPreserve();
|
||||||
|
Clutter.cairo_set_source_color(cr, sliderActiveBorderColor);
|
||||||
|
cr.setLineWidth(sliderBorderWidth);
|
||||||
|
cr.stroke();
|
||||||
|
|
||||||
|
cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4);
|
||||||
|
cr.lineTo(handleX, (height + sliderHeight) / 2);
|
||||||
|
cr.lineTo(handleX, (height - sliderHeight) / 2);
|
||||||
|
cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2);
|
||||||
|
Clutter.cairo_set_source_color(cr, sliderColor);
|
||||||
|
cr.fillPreserve();
|
||||||
|
Clutter.cairo_set_source_color(cr, sliderBorderColor);
|
||||||
|
cr.setLineWidth(sliderBorderWidth);
|
||||||
|
cr.stroke();
|
||||||
|
|
||||||
|
let handleY = height / 2;
|
||||||
|
|
||||||
|
let color = themeNode.get_foreground_color();
|
||||||
|
Clutter.cairo_set_source_color(cr, color);
|
||||||
|
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
|
||||||
|
cr.fillPreserve();
|
||||||
|
if (hasHandleColor && handleBorderWidth) {
|
||||||
|
Clutter.cairo_set_source_color(cr, handleBorderColor);
|
||||||
|
cr.setLineWidth(handleBorderWidth);
|
||||||
|
cr.stroke();
|
||||||
|
}
|
||||||
|
cr.$dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
_startDragging: function(actor, event) {
|
||||||
|
this.startDragging(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
startDragging: function(event) {
|
||||||
|
if (this._dragging)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._dragging = true;
|
||||||
|
|
||||||
|
let device = event.get_device();
|
||||||
|
device.grab(this.actor);
|
||||||
|
this._grabbedDevice = device;
|
||||||
|
|
||||||
|
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
|
||||||
|
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
|
||||||
|
let absX, absY;
|
||||||
|
[absX, absY] = event.get_coords();
|
||||||
|
this._moveHandle(absX, absY);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_endDragging: function() {
|
||||||
|
if (this._dragging) {
|
||||||
|
this.actor.disconnect(this._releaseId);
|
||||||
|
this.actor.disconnect(this._motionId);
|
||||||
|
|
||||||
|
this._grabbedDevice.ungrab();
|
||||||
|
this._grabbedDevice = null;
|
||||||
|
this._dragging = false;
|
||||||
|
|
||||||
|
this.emit('drag-end');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
scroll: function(event) {
|
||||||
|
let direction = event.get_scroll_direction();
|
||||||
|
let delta;
|
||||||
|
|
||||||
|
if (event.is_pointer_emulated())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||||
|
delta = -SLIDER_SCROLL_STEP;
|
||||||
|
} else if (direction == Clutter.ScrollDirection.UP) {
|
||||||
|
delta = +SLIDER_SCROLL_STEP;
|
||||||
|
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
|
||||||
|
let [dx, dy] = event.get_scroll_delta();
|
||||||
|
// Even though the slider is horizontal, use dy to match
|
||||||
|
// the UP/DOWN above.
|
||||||
|
delta = -dy / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._value = Math.min(Math.max(0, this._value + delta), 1);
|
||||||
|
|
||||||
|
this.actor.queue_repaint();
|
||||||
|
this.emit('value-changed', this._value);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onScrollEvent: function(actor, event) {
|
||||||
|
this.scroll(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
_motionEvent: function(actor, event) {
|
||||||
|
let absX, absY;
|
||||||
|
[absX, absY] = event.get_coords();
|
||||||
|
this._moveHandle(absX, absY);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyPressEvent: function (actor, event) {
|
||||||
|
let key = event.get_key_symbol();
|
||||||
|
if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
|
||||||
|
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
|
||||||
|
this._value = Math.max(0, Math.min(this._value + delta, 1));
|
||||||
|
this._slider.queue_repaint();
|
||||||
|
this.emit('value-changed', this._value);
|
||||||
|
this.emit('drag-end');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_moveHandle: function(absX, absY) {
|
||||||
|
let relX, relY, sliderX, sliderY;
|
||||||
|
[sliderX, sliderY] = this.actor.get_transformed_position();
|
||||||
|
relX = absX - sliderX;
|
||||||
|
relY = absY - sliderY;
|
||||||
|
|
||||||
|
let width = this.actor.width;
|
||||||
|
let handleRadius = this.actor.get_theme_node().get_length('-slider-handle-radius');
|
||||||
|
|
||||||
|
let newvalue;
|
||||||
|
if (relX < handleRadius)
|
||||||
|
newvalue = 0;
|
||||||
|
else if (relX > width - handleRadius)
|
||||||
|
newvalue = 1;
|
||||||
|
else
|
||||||
|
newvalue = (relX - handleRadius) / (width - 2 * handleRadius);
|
||||||
|
this._value = newvalue;
|
||||||
|
this.actor.queue_repaint();
|
||||||
|
this.emit('value-changed', this._value);
|
||||||
|
},
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this._value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Signals.addSignalMethods(Slider.prototype);
|
@ -68,9 +68,6 @@ const ATIndicator = new Lang.Class({
|
|||||||
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
|
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
|
||||||
this.menu.addMenuItem(mouseKeys);
|
this.menu.addMenuItem(mouseKeys);
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
|
||||||
|
|
||||||
this._syncMenuVisibility();
|
this._syncMenuVisibility();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -13,13 +13,6 @@ const NotificationDaemon = imports.ui.notificationDaemon;
|
|||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
const ConnectionState = {
|
|
||||||
DISCONNECTED: 0,
|
|
||||||
CONNECTED: 1,
|
|
||||||
DISCONNECTING: 2,
|
|
||||||
CONNECTING: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
const Indicator = new Lang.Class({
|
const Indicator = new Lang.Class({
|
||||||
Name: 'BTIndicator',
|
Name: 'BTIndicator',
|
||||||
Extends: PanelMenu.SystemStatusButton,
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
@ -27,261 +20,38 @@ const Indicator = new Lang.Class({
|
|||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
|
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
|
||||||
|
|
||||||
|
// The Bluetooth menu only appears when Bluetooth is in use,
|
||||||
|
// so just statically build it with a "Turn Off" menu item.
|
||||||
|
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
|
||||||
|
this._item.icon.icon_name = 'bluetooth-active-symbolic';
|
||||||
|
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
|
||||||
|
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||||
|
}));
|
||||||
|
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||||
|
|
||||||
this._applet = new GnomeBluetoothApplet.Applet();
|
this._applet = new GnomeBluetoothApplet.Applet();
|
||||||
|
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
|
||||||
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
|
this._sync();
|
||||||
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
|
||||||
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
|
||||||
let current_state = this._applet.killswitch_state;
|
|
||||||
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
|
|
||||||
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
|
|
||||||
this._applet.killswitch_state = this._killswitch.state ?
|
|
||||||
GnomeBluetooth.KillswitchState.UNBLOCKED:
|
|
||||||
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
|
||||||
} else
|
|
||||||
this._killswitch.setToggleState(false);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
|
|
||||||
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
|
|
||||||
this._discoverable.setToggleState(this._applet.discoverable);
|
|
||||||
}));
|
|
||||||
this._discoverable.connect('toggled', Lang.bind(this, function() {
|
|
||||||
this._applet.discoverable = this._discoverable.state;
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._updateKillswitch();
|
|
||||||
this.menu.addMenuItem(this._killswitch);
|
|
||||||
this.menu.addMenuItem(this._discoverable);
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
|
|
||||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
|
||||||
new PopupMenu.PopupMenuItem(_("Send Files to Device…")),
|
|
||||||
new PopupMenu.PopupMenuItem(_("Set Up a New Device…")),
|
|
||||||
new PopupMenu.PopupSeparatorMenuItem()];
|
|
||||||
this._hasDevices = false;
|
|
||||||
|
|
||||||
this._fullMenuItems[1].connect('activate', function() {
|
|
||||||
GLib.spawn_command_line_async('bluetooth-sendto');
|
|
||||||
});
|
|
||||||
this._fullMenuItems[2].connect('activate', function() {
|
|
||||||
GLib.spawn_command_line_async('bluetooth-wizard');
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < this._fullMenuItems.length; i++) {
|
|
||||||
let item = this._fullMenuItems[i];
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._deviceItemPosition = 3;
|
|
||||||
this._deviceItems = [];
|
|
||||||
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._updateDevices();
|
|
||||||
|
|
||||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
|
||||||
this._updateFullMenu();
|
|
||||||
|
|
||||||
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
|
||||||
|
|
||||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||||
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
|
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
|
||||||
|
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
|
||||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateKillswitch: function() {
|
_sync: function() {
|
||||||
let current_state = this._applet.killswitch_state;
|
let connectedDevices = this._applet.get_devices().filter(function(device) {
|
||||||
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
|
return device.connected;
|
||||||
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
|
});
|
||||||
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
|
let nDevices = connectedDevices.length;
|
||||||
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
|
|
||||||
|
|
||||||
this._killswitch.setToggleState(on);
|
let on = nDevices > 0;
|
||||||
if (can_toggle)
|
this.mainIcon.visible = on;
|
||||||
this._killswitch.setStatus(null);
|
this.actor.visible = on;
|
||||||
else
|
|
||||||
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
|
||||||
this._killswitch.setStatus(_("hardware disabled"));
|
|
||||||
|
|
||||||
this.actor.visible = has_adapter;
|
if (on)
|
||||||
|
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
|
||||||
if (on) {
|
|
||||||
this._discoverable.actor.show();
|
|
||||||
this.setIcon('bluetooth-active-symbolic');
|
|
||||||
} else {
|
|
||||||
this._discoverable.actor.hide();
|
|
||||||
this.setIcon('bluetooth-disabled-symbolic');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDevices: function() {
|
|
||||||
let devices = this._applet.get_devices();
|
|
||||||
|
|
||||||
let newlist = [ ];
|
|
||||||
for (let i = 0; i < this._deviceItems.length; i++) {
|
|
||||||
let item = this._deviceItems[i];
|
|
||||||
let destroy = true;
|
|
||||||
for (let j = 0; j < devices.length; j++) {
|
|
||||||
if (item._device.device_path == devices[j].device_path) {
|
|
||||||
this._updateDeviceItem(item, devices[j]);
|
|
||||||
destroy = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (destroy)
|
|
||||||
item.destroy();
|
|
||||||
else
|
|
||||||
newlist.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._deviceItems = newlist;
|
|
||||||
this._hasDevices = newlist.length > 0;
|
|
||||||
for (let i = 0; i < devices.length; i++) {
|
|
||||||
let d = devices[i];
|
|
||||||
if (d._item)
|
|
||||||
continue;
|
|
||||||
let item = this._createDeviceItem(d);
|
|
||||||
if (item) {
|
|
||||||
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
|
|
||||||
this._deviceItems.push(item);
|
|
||||||
this._hasDevices = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDeviceItem: function(item, device) {
|
|
||||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
|
|
||||||
item.destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let prevDevice = item._device;
|
|
||||||
let prevCapabilities = prevDevice.capabilities;
|
|
||||||
let prevCanConnect = prevDevice.can_connect;
|
|
||||||
|
|
||||||
// adopt the new device object
|
|
||||||
item._device = device;
|
|
||||||
device._item = item;
|
|
||||||
|
|
||||||
// update properties
|
|
||||||
item.label.text = device.alias;
|
|
||||||
|
|
||||||
if (prevCapabilities != device.capabilities ||
|
|
||||||
prevCanConnect != device.can_connect) {
|
|
||||||
// need to rebuild the submenu
|
|
||||||
item.menu.removeAll();
|
|
||||||
this._buildDeviceSubMenu(item, device);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update connected property
|
|
||||||
if (device.can_connect)
|
|
||||||
item._connectedMenuItem.setToggleState(device.connected);
|
|
||||||
},
|
|
||||||
|
|
||||||
_createDeviceItem: function(device) {
|
|
||||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
|
|
||||||
return null;
|
|
||||||
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
|
|
||||||
|
|
||||||
// adopt the device object, and add a back link
|
|
||||||
item._device = device;
|
|
||||||
device._item = item;
|
|
||||||
|
|
||||||
this._buildDeviceSubMenu(item, device);
|
|
||||||
|
|
||||||
return item;
|
|
||||||
},
|
|
||||||
|
|
||||||
_buildDeviceSubMenu: function(item, device) {
|
|
||||||
if (device.can_connect) {
|
|
||||||
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
|
|
||||||
item._connected = device.connected;
|
|
||||||
item._connectedMenuItem = menuitem;
|
|
||||||
menuitem.connect('toggled', Lang.bind(this, function() {
|
|
||||||
if (item._connected > ConnectionState.CONNECTED) {
|
|
||||||
// operation already in progress, revert
|
|
||||||
// (should not happen anyway)
|
|
||||||
menuitem.setToggleState(menuitem.state);
|
|
||||||
}
|
|
||||||
if (item._connected) {
|
|
||||||
item._connected = ConnectionState.DISCONNECTING;
|
|
||||||
menuitem.setStatus(_("disconnecting..."));
|
|
||||||
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
|
|
||||||
if (success) { // apply
|
|
||||||
item._connected = ConnectionState.DISCONNECTED;
|
|
||||||
menuitem.setToggleState(false);
|
|
||||||
} else { // revert
|
|
||||||
item._connected = ConnectionState.CONNECTED;
|
|
||||||
menuitem.setToggleState(true);
|
|
||||||
}
|
|
||||||
menuitem.setStatus(null);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
item._connected = ConnectionState.CONNECTING;
|
|
||||||
menuitem.setStatus(_("connecting..."));
|
|
||||||
this._applet.connect_device(item._device.device_path, function(applet, success) {
|
|
||||||
if (success) { // apply
|
|
||||||
item._connected = ConnectionState.CONNECTED;
|
|
||||||
menuitem.setToggleState(true);
|
|
||||||
} else { // revert
|
|
||||||
item._connected = ConnectionState.DISCONNECTED;
|
|
||||||
menuitem.setToggleState(false);
|
|
||||||
}
|
|
||||||
menuitem.setStatus(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
item.menu.addMenuItem(menuitem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
|
||||||
item.menu.addAction(_("Send Files…"), Lang.bind(this, function() {
|
|
||||||
this._applet.send_to_address(device.bdaddr, device.alias);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (device.type) {
|
|
||||||
case GnomeBluetoothApplet.Type.KEYBOARD:
|
|
||||||
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
|
|
||||||
break;
|
|
||||||
case GnomeBluetoothApplet.Type.MOUSE:
|
|
||||||
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
|
|
||||||
break;
|
|
||||||
case GnomeBluetoothApplet.Type.HEADSET:
|
|
||||||
case GnomeBluetoothApplet.Type.HEADPHONES:
|
|
||||||
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
|
||||||
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateFullMenu: function() {
|
|
||||||
if (this._applet.show_full_menu) {
|
|
||||||
this._showAll(this._fullMenuItems);
|
|
||||||
if (this._hasDevices)
|
|
||||||
this._showAll(this._deviceItems);
|
|
||||||
} else {
|
|
||||||
this._hideAll(this._fullMenuItems);
|
|
||||||
this._hideAll(this._deviceItems);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_showAll: function(items) {
|
|
||||||
for (let i = 0; i < items.length; i++)
|
|
||||||
items[i].actor.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
_hideAll: function(items) {
|
|
||||||
for (let i = 0; i < items.length; i++)
|
|
||||||
items[i].actor.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
_destroyAll: function(items) {
|
|
||||||
for (let i = 0; i < items.length; i++)
|
|
||||||
items[i].destroy();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ensureSource: function() {
|
_ensureSource: function() {
|
||||||
@ -292,9 +62,14 @@ const Indicator = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_authRequest: function(applet, device_path, name, long_name, uuid) {
|
_authRequest: function(applet, device_path, name, long_name) {
|
||||||
this._ensureSource();
|
this._ensureSource();
|
||||||
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
|
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
|
||||||
|
},
|
||||||
|
|
||||||
|
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
|
||||||
|
this._ensureSource();
|
||||||
|
this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
|
||||||
},
|
},
|
||||||
|
|
||||||
_confirmRequest: function(applet, device_path, name, long_name, pin) {
|
_confirmRequest: function(applet, device_path, name, long_name, pin) {
|
||||||
@ -316,6 +91,34 @@ const AuthNotification = new Lang.Class({
|
|||||||
Name: 'AuthNotification',
|
Name: 'AuthNotification',
|
||||||
Extends: MessageTray.Notification,
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
|
_init: function(source, applet, device_path, name, long_name) {
|
||||||
|
this.parent(source,
|
||||||
|
_("Bluetooth"),
|
||||||
|
_("Authorization request from %s").format(name),
|
||||||
|
{ customContent: true });
|
||||||
|
this.setResident(true);
|
||||||
|
|
||||||
|
this._applet = applet;
|
||||||
|
this._devicePath = device_path;
|
||||||
|
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||||
|
|
||||||
|
this.addButton('allow', _("Allow"));
|
||||||
|
this.addButton('deny', _("Deny"));
|
||||||
|
|
||||||
|
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||||
|
if (action == 'allow')
|
||||||
|
this._applet.agent_reply_confirm(this._devicePath, true);
|
||||||
|
else
|
||||||
|
this._applet.agent_reply_confirm(this._devicePath, false);
|
||||||
|
this.destroy();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const AuthServiceNotification = new Lang.Class({
|
||||||
|
Name: 'AuthServiceNotification',
|
||||||
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
_init: function(source, applet, device_path, name, long_name, uuid) {
|
_init: function(source, applet, device_path, name, long_name, uuid) {
|
||||||
this.parent(source,
|
this.parent(source,
|
||||||
_("Bluetooth"),
|
_("Bluetooth"),
|
||||||
@ -334,14 +137,14 @@ const AuthNotification = new Lang.Class({
|
|||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'always-grant':
|
case 'always-grant':
|
||||||
this._applet.agent_reply_auth(this._devicePath, true, true);
|
this._applet.agent_reply_auth_service(this._devicePath, true, true);
|
||||||
break;
|
break;
|
||||||
case 'grant':
|
case 'grant':
|
||||||
this._applet.agent_reply_auth(this._devicePath, true, false);
|
this._applet.agent_reply_auth_service(this._devicePath, true, false);
|
||||||
break;
|
break;
|
||||||
case 'reject':
|
case 'reject':
|
||||||
default:
|
default:
|
||||||
this._applet.agent_reply_auth(this._devicePath, false, false);
|
this._applet.agent_reply_auth_service(this._devicePath, false, false);
|
||||||
}
|
}
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}));
|
}));
|
||||||
@ -363,7 +166,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 '%06d' matches the one on the device.").format(pin));
|
this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
|
||||||
|
|
||||||
/* Translators: this is the verb, not the noun */
|
/* Translators: this is the verb, not the noun */
|
||||||
this.addButton('matches', _("Matches"));
|
this.addButton('matches', _("Matches"));
|
||||||
|
@ -33,6 +33,33 @@ const KEY_INPUT_SOURCES = 'sources';
|
|||||||
const INPUT_SOURCE_TYPE_XKB = 'xkb';
|
const INPUT_SOURCE_TYPE_XKB = 'xkb';
|
||||||
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
|
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
|
||||||
|
|
||||||
|
// This is the longest we'll keep the keyboard frozen until an input
|
||||||
|
// source is active.
|
||||||
|
const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
|
||||||
|
|
||||||
|
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
|
||||||
|
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
|
||||||
|
|
||||||
|
const KeyboardManagerInterface =
|
||||||
|
<interface name="org.gnome.SettingsDaemon.Keyboard">
|
||||||
|
<method name="SetInputSource">
|
||||||
|
<arg type="u" direction="in" />
|
||||||
|
</method>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
|
||||||
|
|
||||||
|
function releaseKeyboard() {
|
||||||
|
if (Main.modalCount > 0)
|
||||||
|
global.display.unfreeze_keyboard(global.get_current_time());
|
||||||
|
else
|
||||||
|
global.display.ungrab_keyboard(global.get_current_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
function holdKeyboard() {
|
||||||
|
global.freeze_keyboard(global.get_current_time());
|
||||||
|
}
|
||||||
|
|
||||||
const IBusManager = new Lang.Class({
|
const IBusManager = new Lang.Class({
|
||||||
Name: 'IBusManager',
|
Name: 'IBusManager',
|
||||||
|
|
||||||
@ -45,26 +72,24 @@ const IBusManager = new Lang.Class({
|
|||||||
this._readyCallback = readyCallback;
|
this._readyCallback = readyCallback;
|
||||||
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
|
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
|
||||||
|
|
||||||
this._ibus = null;
|
|
||||||
this._panelService = null;
|
this._panelService = null;
|
||||||
this._engines = {};
|
this._engines = {};
|
||||||
this._ready = false;
|
this._ready = false;
|
||||||
this._registerPropertiesId = 0;
|
this._registerPropertiesId = 0;
|
||||||
this._currentEngineName = null;
|
this._currentEngineName = null;
|
||||||
|
|
||||||
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
|
this._ibus = IBus.Bus.new_async();
|
||||||
Gio.BusNameWatcherFlags.NONE,
|
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
|
||||||
Lang.bind(this, this._onNameAppeared),
|
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
|
||||||
Lang.bind(this, this._clear));
|
// 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._engineChanged));
|
||||||
},
|
},
|
||||||
|
|
||||||
_clear: function() {
|
_clear: function() {
|
||||||
if (this._panelService)
|
if (this._panelService)
|
||||||
this._panelService.destroy();
|
this._panelService.destroy();
|
||||||
if (this._ibus)
|
|
||||||
this._ibus.destroy();
|
|
||||||
|
|
||||||
this._ibus = null;
|
|
||||||
this._panelService = null;
|
this._panelService = null;
|
||||||
this._candidatePopup.setPanelService(null);
|
this._candidatePopup.setPanelService(null);
|
||||||
this._engines = {};
|
this._engines = {};
|
||||||
@ -76,18 +101,12 @@ const IBusManager = new Lang.Class({
|
|||||||
this._readyCallback(false);
|
this._readyCallback(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onNameAppeared: function() {
|
|
||||||
this._ibus = IBus.Bus.new_async();
|
|
||||||
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onConnected: function() {
|
_onConnected: function() {
|
||||||
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
|
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
|
||||||
this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
||||||
IBus.BusNameFlag.REPLACE_EXISTING,
|
IBus.BusNameFlag.REPLACE_EXISTING,
|
||||||
-1, null,
|
-1, null,
|
||||||
Lang.bind(this, this._initPanelService));
|
Lang.bind(this, this._initPanelService));
|
||||||
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_initEngines: function(ibus, result) {
|
_initEngines: function(ibus, result) {
|
||||||
@ -109,9 +128,6 @@ const IBusManager = new Lang.Class({
|
|||||||
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
||||||
object_path: IBus.PATH_PANEL });
|
object_path: IBus.PATH_PANEL });
|
||||||
this._candidatePopup.setPanelService(this._panelService);
|
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._engineChanged));
|
|
||||||
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||||
// If an engine is already active we need to get its properties
|
// If an engine is already active we need to get its properties
|
||||||
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
|
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
|
||||||
@ -140,6 +156,9 @@ const IBusManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_engineChanged: function(bus, engineName) {
|
_engineChanged: function(bus, engineName) {
|
||||||
|
if (!this._ready)
|
||||||
|
return;
|
||||||
|
|
||||||
this._currentEngineName = engineName;
|
this._currentEngineName = engineName;
|
||||||
|
|
||||||
if (this._registerPropertiesId != 0)
|
if (this._registerPropertiesId != 0)
|
||||||
@ -337,14 +356,14 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
Main.wm.addKeybinding('switch-input-source',
|
Main.wm.addKeybinding('switch-input-source',
|
||||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||||
Meta.KeyBindingFlags.REVERSES,
|
Meta.KeyBindingFlags.REVERSES,
|
||||||
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY,
|
Shell.KeyBindingMode.ALL,
|
||||||
Lang.bind(this, this._switchInputSource));
|
Lang.bind(this, this._switchInputSource));
|
||||||
this._keybindingActionBackward =
|
this._keybindingActionBackward =
|
||||||
Main.wm.addKeybinding('switch-input-source-backward',
|
Main.wm.addKeybinding('switch-input-source-backward',
|
||||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||||
Meta.KeyBindingFlags.REVERSES |
|
Meta.KeyBindingFlags.REVERSES |
|
||||||
Meta.KeyBindingFlags.REVERSED,
|
Meta.KeyBindingFlags.REVERSED,
|
||||||
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY,
|
Shell.KeyBindingMode.ALL,
|
||||||
Lang.bind(this, this._switchInputSource));
|
Lang.bind(this, this._switchInputSource));
|
||||||
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||||
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||||
@ -364,14 +383,21 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
|
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
|
||||||
this._inputSourcesChanged();
|
this._inputSourcesChanged();
|
||||||
|
|
||||||
|
this._keyboardManager = new KeyboardManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||||
|
function(proxy, error) {
|
||||||
|
if (error)
|
||||||
|
log(error.message);
|
||||||
|
});
|
||||||
|
this._keyboardManager.g_default_timeout = MAX_INPUT_SOURCE_ACTIVATION_TIME;
|
||||||
|
|
||||||
|
global.display.connect('modifiers-accelerator-activated', Lang.bind(this, this._modifiersSwitcher));
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
||||||
|
|
||||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
this._sessionUpdated();
|
this._sessionUpdated();
|
||||||
|
|
||||||
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
|
|
||||||
|
|
||||||
this._sourcesPerWindow = false;
|
this._sourcesPerWindow = false;
|
||||||
this._focusWindowNotifyId = 0;
|
this._focusWindowNotifyId = 0;
|
||||||
this._overviewShowingId = 0;
|
this._overviewShowingId = 0;
|
||||||
@ -397,10 +423,43 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
this._inputSourcesChanged();
|
this._inputSourcesChanged();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_modifiersSwitcher: function() {
|
||||||
|
let sourceIndexes = Object.keys(this._inputSources);
|
||||||
|
if (sourceIndexes.length == 0) {
|
||||||
|
releaseKeyboard();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is = this._currentSource;
|
||||||
|
if (!is)
|
||||||
|
is = this._inputSources[sourceIndexes[0]];
|
||||||
|
|
||||||
|
let nextIndex = is.index + 1;
|
||||||
|
if (nextIndex > sourceIndexes[sourceIndexes.length - 1])
|
||||||
|
nextIndex = 0;
|
||||||
|
|
||||||
|
while (!(is = this._inputSources[nextIndex]))
|
||||||
|
nextIndex += 1;
|
||||||
|
|
||||||
|
is.activate();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
_switchInputSource: function(display, screen, window, binding) {
|
_switchInputSource: function(display, screen, window, binding) {
|
||||||
if (this._mruSources.length < 2)
|
if (this._mruSources.length < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// HACK: Fall back on simple input source switching since we
|
||||||
|
// can't show a popup switcher while a GrabHelper grab is in
|
||||||
|
// effect without considerable work to consolidate the usage
|
||||||
|
// of pushModal/popModal and grabHelper. See
|
||||||
|
// https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
|
||||||
|
if (Main.keybindingMode == Shell.KeyBindingMode.MESSAGE_TRAY ||
|
||||||
|
Main.keybindingMode == Shell.KeyBindingMode.TOPBAR_POPUP) {
|
||||||
|
this._modifiersSwitcher();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
|
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
|
||||||
let modifiers = binding.get_modifiers();
|
let modifiers = binding.get_modifiers();
|
||||||
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
|
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
|
||||||
@ -417,7 +476,7 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||||
|
|
||||||
if (oldSource) {
|
if (oldSource) {
|
||||||
oldSource.menuItem.setShowDot(false);
|
oldSource.menuItem.setOrnament(PopupMenu.Ornament.NONE);
|
||||||
oldSource.indicatorLabel.hide();
|
oldSource.indicatorLabel.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +494,7 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
|
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
|
|
||||||
newSource.menuItem.setShowDot(true);
|
newSource.menuItem.setOrnament(PopupMenu.Ornament.DOT);
|
||||||
newSource.indicatorLabel.show();
|
newSource.indicatorLabel.show();
|
||||||
|
|
||||||
this._buildPropSection(newSource.properties);
|
this._buildPropSection(newSource.properties);
|
||||||
@ -459,6 +518,7 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
|
|
||||||
this._inputSources = {};
|
this._inputSources = {};
|
||||||
this._ibusSources = {};
|
this._ibusSources = {};
|
||||||
|
this._currentSource = null;
|
||||||
|
|
||||||
let inputSourcesByShortName = {};
|
let inputSourcesByShortName = {};
|
||||||
|
|
||||||
@ -487,10 +547,8 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
let is = new InputSource(type, id, displayName, shortName, i);
|
let is = new InputSource(type, id, displayName, shortName, i);
|
||||||
|
|
||||||
is.connect('activate', Lang.bind(this, function() {
|
is.connect('activate', Lang.bind(this, function() {
|
||||||
if (this._currentSource && this._currentSource.index == is.index)
|
holdKeyboard();
|
||||||
return;
|
this._keyboardManager.SetInputSourceRemote(is.index, releaseKeyboard);
|
||||||
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
|
||||||
GLib.Variant.new_uint32(is.index));
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (!(is.shortName in inputSourcesByShortName))
|
if (!(is.shortName in inputSourcesByShortName))
|
||||||
@ -660,7 +718,8 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
item.prop = prop;
|
item.prop = prop;
|
||||||
radioGroup.push(item);
|
radioGroup.push(item);
|
||||||
item.radioGroup = radioGroup;
|
item.radioGroup = radioGroup;
|
||||||
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
|
item.setOrnament(prop.get_state() == IBus.PropState.CHECKED ?
|
||||||
|
PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
|
||||||
item.connect('activate', Lang.bind(this, function() {
|
item.connect('activate', Lang.bind(this, function() {
|
||||||
if (item.prop.get_state() == IBus.PropState.CHECKED)
|
if (item.prop.get_state() == IBus.PropState.CHECKED)
|
||||||
return;
|
return;
|
||||||
@ -668,12 +727,12 @@ const InputSourceIndicator = new Lang.Class({
|
|||||||
let group = item.radioGroup;
|
let group = item.radioGroup;
|
||||||
for (let i = 0; i < group.length; ++i) {
|
for (let i = 0; i < group.length; ++i) {
|
||||||
if (group[i] == item) {
|
if (group[i] == item) {
|
||||||
item.setShowDot(true);
|
item.setOrnament(PopupMenu.Ornament.DOT);
|
||||||
item.prop.set_state(IBus.PropState.CHECKED);
|
item.prop.set_state(IBus.PropState.CHECKED);
|
||||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||||
IBus.PropState.CHECKED);
|
IBus.PropState.CHECKED);
|
||||||
} else {
|
} else {
|
||||||
group[i].setShowDot(false);
|
group[i].setOrnament(PopupMenu.Ornament.NONE);
|
||||||
group[i].prop.set_state(IBus.PropState.UNCHECKED);
|
group[i].prop.set_state(IBus.PropState.UNCHECKED);
|
||||||
this._ibusManager.activateProperty(group[i].prop.get_key(),
|
this._ibusManager.activateProperty(group[i].prop.get_key(),
|
||||||
IBus.PropState.UNCHECKED);
|
IBus.PropState.UNCHECKED);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,39 +2,15 @@
|
|||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const St = imports.gi.St;
|
const UPower = imports.gi.UPowerGlib;
|
||||||
|
|
||||||
|
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 BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||||
|
|
||||||
const UPDeviceType = {
|
|
||||||
UNKNOWN: 0,
|
|
||||||
AC_POWER: 1,
|
|
||||||
BATTERY: 2,
|
|
||||||
UPS: 3,
|
|
||||||
MONITOR: 4,
|
|
||||||
MOUSE: 5,
|
|
||||||
KEYBOARD: 6,
|
|
||||||
PDA: 7,
|
|
||||||
PHONE: 8,
|
|
||||||
MEDIA_PLAYER: 9,
|
|
||||||
TABLET: 10,
|
|
||||||
COMPUTER: 11
|
|
||||||
};
|
|
||||||
|
|
||||||
const UPDeviceState = {
|
|
||||||
UNKNOWN: 0,
|
|
||||||
CHARGING: 1,
|
|
||||||
DISCHARGING: 2,
|
|
||||||
EMPTY: 3,
|
|
||||||
FULLY_CHARGED: 4,
|
|
||||||
PENDING_CHARGE: 5,
|
|
||||||
PENDING_DISCHARGE: 6
|
|
||||||
};
|
|
||||||
|
|
||||||
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
|
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
|
||||||
<method name="GetDevices">
|
<method name="GetDevices">
|
||||||
<arg type="a(susdut)" direction="out" />
|
<arg type="a(susdut)" direction="out" />
|
||||||
@ -55,96 +31,73 @@ const Indicator = new Lang.Class({
|
|||||||
this.parent('battery-missing-symbolic', _("Battery"));
|
this.parent('battery-missing-symbolic', _("Battery"));
|
||||||
|
|
||||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||||
Lang.bind(this, function(proxy, error) {
|
Lang.bind(this, function(proxy, error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
log(error.message);
|
log(error.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._proxy.connect('g-properties-changed',
|
this._proxy.connect('g-properties-changed',
|
||||||
Lang.bind(this, this._devicesChanged));
|
Lang.bind(this, this._sync));
|
||||||
this._devicesChanged();
|
this._sync();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._deviceItems = [ ];
|
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
|
||||||
this._hasPrimary = false;
|
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||||
this._primaryDeviceId = null;
|
this.menu.addMenuItem(this._item);
|
||||||
|
|
||||||
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
|
this._sessionUpdated();
|
||||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
|
||||||
this.menu.addMenuItem(this._batteryItem);
|
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
this._otherDevicePosition = 2;
|
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_readPrimaryDevice: function() {
|
_sessionUpdated: function() {
|
||||||
|
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||||
|
this.menu.setSensitive(sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
|
_statusForDevice: function(device) {
|
||||||
|
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||||
|
|
||||||
|
if (state == UPower.DeviceState.FULLY_CHARGED)
|
||||||
|
return _("Fully Charged");
|
||||||
|
|
||||||
|
let time = Math.round(seconds / 60);
|
||||||
|
if (time == 0) {
|
||||||
|
// 0 is reported when UPower does not have enough data
|
||||||
|
// to estimate battery life
|
||||||
|
return _("Estimating…");
|
||||||
|
}
|
||||||
|
|
||||||
|
let minutes = time % 60;
|
||||||
|
let hours = Math.floor(time / 60);
|
||||||
|
|
||||||
|
if (state == UPower.DeviceState.DISCHARGING) {
|
||||||
|
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
|
||||||
|
return _("%d\u2236%d Remaining (%d%%)".format(hours, minutes, percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == UPower.DeviceState.CHARGING) {
|
||||||
|
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
|
||||||
|
return _("%d\u2236%d Until Full (%d%%)".format(hours, minutes, percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
|
||||||
|
return _("Estimating…");
|
||||||
|
},
|
||||||
|
|
||||||
|
_syncStatusLabel: function() {
|
||||||
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
|
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
this._hasPrimary = false;
|
this._item.actor.hide();
|
||||||
this._primaryDeviceId = null;
|
|
||||||
this._batteryItem.actor.hide();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [[device_id, device_type, icon, percentage, state, seconds]] = result;
|
|
||||||
if (device_type == UPDeviceType.BATTERY) {
|
|
||||||
this._hasPrimary = true;
|
|
||||||
let time = Math.round(seconds / 60);
|
|
||||||
if (time == 0) {
|
|
||||||
// 0 is reported when UPower does not have enough data
|
|
||||||
// to estimate battery life
|
|
||||||
this._batteryItem.label.text = _("Estimating…");
|
|
||||||
} else {
|
|
||||||
let minutes = time % 60;
|
|
||||||
let hours = Math.floor(time / 60);
|
|
||||||
let timestring;
|
|
||||||
if (time >= 60) {
|
|
||||||
if (minutes == 0) {
|
|
||||||
timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
|
||||||
} else {
|
|
||||||
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
|
||||||
let template = _("%d %s %d %s remaining");
|
|
||||||
|
|
||||||
timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
|
let [device] = result;
|
||||||
}
|
let [device_id, device_type] = device;
|
||||||
} else
|
if (device_type == UPower.DeviceKind.BATTERY) {
|
||||||
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
this._item.status.text = this._statusForDevice(device);
|
||||||
this._batteryItem.label.text = timestring;
|
this._item.actor.show();
|
||||||
}
|
|
||||||
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage));
|
|
||||||
this._batteryItem.actor.show();
|
|
||||||
} else {
|
} else {
|
||||||
this._hasPrimary = false;
|
this._item.actor.hide();
|
||||||
this._batteryItem.actor.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._primaryDeviceId = device_id;
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_readOtherDevices: function() {
|
|
||||||
this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) {
|
|
||||||
this._deviceItems.forEach(function(i) { i.destroy(); });
|
|
||||||
this._deviceItems = [];
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let position = 0;
|
|
||||||
let [devices] = result;
|
|
||||||
for (let i = 0; i < devices.length; i++) {
|
|
||||||
let [device_id, device_type] = devices[i];
|
|
||||||
if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let item = new DeviceItem (devices[i]);
|
|
||||||
this._deviceItems.push(item);
|
|
||||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
|
||||||
position++;
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@ -156,71 +109,15 @@ const Indicator = new Lang.Class({
|
|||||||
if (icon) {
|
if (icon) {
|
||||||
let gicon = Gio.icon_new_for_string(icon);
|
let gicon = Gio.icon_new_for_string(icon);
|
||||||
this.setGIcon(gicon);
|
this.setGIcon(gicon);
|
||||||
|
this._item.icon.gicon = gicon;
|
||||||
hasIcon = true;
|
hasIcon = true;
|
||||||
}
|
}
|
||||||
this.mainIcon.visible = hasIcon;
|
this.mainIcon.visible = hasIcon;
|
||||||
this.actor.visible = hasIcon;
|
this.actor.visible = hasIcon;
|
||||||
},
|
},
|
||||||
|
|
||||||
_devicesChanged: function() {
|
_sync: function() {
|
||||||
this._syncIcon();
|
this._syncIcon();
|
||||||
this._readPrimaryDevice();
|
this._syncStatusLabel();
|
||||||
this._readOtherDevices();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const DeviceItem = new Lang.Class({
|
|
||||||
Name: 'DeviceItem',
|
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
|
||||||
|
|
||||||
_init: function(device) {
|
|
||||||
this.parent({ reactive: false });
|
|
||||||
|
|
||||||
let [device_id, device_type, icon, percentage, state, time] = device;
|
|
||||||
|
|
||||||
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
|
|
||||||
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
|
|
||||||
|
|
||||||
this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon),
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
|
|
||||||
this._box.add_actor(this._icon);
|
|
||||||
this._box.add_actor(this._label);
|
|
||||||
this.addActor(this._box);
|
|
||||||
|
|
||||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
|
|
||||||
style_class: 'popup-battery-percentage' });
|
|
||||||
this.addActor(percentLabel, { align: St.Align.END });
|
|
||||||
//FIXME: ideally we would like to expose this._label and percentLabel
|
|
||||||
this.actor.label_actor = percentLabel;
|
|
||||||
},
|
|
||||||
|
|
||||||
_deviceTypeToString: function(type) {
|
|
||||||
switch (type) {
|
|
||||||
case UPDeviceType.AC_POWER:
|
|
||||||
return _("AC Adapter");
|
|
||||||
case UPDeviceType.BATTERY:
|
|
||||||
return _("Laptop Battery");
|
|
||||||
case UPDeviceType.UPS:
|
|
||||||
return _("UPS");
|
|
||||||
case UPDeviceType.MONITOR:
|
|
||||||
return _("Monitor");
|
|
||||||
case UPDeviceType.MOUSE:
|
|
||||||
return _("Mouse");
|
|
||||||
case UPDeviceType.KEYBOARD:
|
|
||||||
return _("Keyboard");
|
|
||||||
case UPDeviceType.PDA:
|
|
||||||
return _("PDA");
|
|
||||||
case UPDeviceType.PHONE:
|
|
||||||
return _("Cell Phone");
|
|
||||||
case UPDeviceType.MEDIA_PLAYER:
|
|
||||||
return _("Media Player");
|
|
||||||
case UPDeviceType.TABLET:
|
|
||||||
return _("Tablet");
|
|
||||||
case UPDeviceType.COMPUTER:
|
|
||||||
return _("Computer");
|
|
||||||
default:
|
|
||||||
return C_("device", "Unknown");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
358
js/ui/status/system.js
Normal file
358
js/ui/status/system.js
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const AccountsService = imports.gi.AccountsService;
|
||||||
|
const Gdm = imports.gi.Gdm;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
|
||||||
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
|
const LoginManager = imports.misc.loginManager;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
|
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||||
|
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||||
|
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
||||||
|
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||||
|
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||||
|
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||||
|
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||||
|
|
||||||
|
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||||
|
|
||||||
|
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||||
|
<property name="Id" type="s" access="read"/>
|
||||||
|
<property name="Remote" type="b" access="read"/>
|
||||||
|
<property name="Class" type="s" access="read"/>
|
||||||
|
<property name="Type" type="s" access="read"/>
|
||||||
|
<property name="State" type="s" access="read"/>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||||
|
|
||||||
|
const Indicator = new Lang.Class({
|
||||||
|
Name: 'SystemIndicator',
|
||||||
|
Extends: PanelMenu.SystemStatusButton,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent('system-shutdown-symbolic', _("System"));
|
||||||
|
|
||||||
|
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||||
|
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||||
|
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
||||||
|
|
||||||
|
this._session = new GnomeSession.SessionManager();
|
||||||
|
this._haveShutdown = true;
|
||||||
|
|
||||||
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
|
this._userManager = AccountsService.UserManager.get_default();
|
||||||
|
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||||
|
|
||||||
|
this._createSubMenu();
|
||||||
|
|
||||||
|
this._userManager.connect('notify::is-loaded',
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._userManager.connect('notify::has-multiple-users',
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._userManager.connect('user-added',
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._userManager.connect('user-removed',
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||||
|
Lang.bind(this, this._updateLockScreen));
|
||||||
|
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
|
||||||
|
Lang.bind(this, this._updateMultiUser));
|
||||||
|
this._updateSwitchUser();
|
||||||
|
this._updateMultiUser();
|
||||||
|
|
||||||
|
// Whether shutdown is available or not depends on both lockdown
|
||||||
|
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||||
|
// notify, so we update the menu item each time the menu opens or
|
||||||
|
// the lockdown setting changes, which should be close enough.
|
||||||
|
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||||
|
function(menu, open) {
|
||||||
|
if (!open)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._updateHaveShutdown();
|
||||||
|
}));
|
||||||
|
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||||
|
Lang.bind(this, this._updateHaveShutdown));
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
this._sessionUpdated();
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
this._updateLockScreen();
|
||||||
|
this._updatePowerOff();
|
||||||
|
this._settingsAction.visible = Main.sessionMode.allowSettings;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateMultiUser: function() {
|
||||||
|
let shouldShowInMode = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||||
|
let hasSwitchUser = this._updateSwitchUser();
|
||||||
|
let hasLogout = this._updateLogout();
|
||||||
|
|
||||||
|
this._switchUserSubMenu.actor.visible = shouldShowInMode && (hasSwitchUser || hasLogout);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSwitchUser: function() {
|
||||||
|
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||||
|
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
|
||||||
|
|
||||||
|
let visible = allowSwitch && multiUser;
|
||||||
|
this._loginScreenItem.actor.visible = visible;
|
||||||
|
return visible;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLogout: function() {
|
||||||
|
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||||
|
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
|
||||||
|
let systemAccount = this._user.system_account;
|
||||||
|
let localAccount = this._user.local_account;
|
||||||
|
let multiUser = this._userManager.has_multiple_users;
|
||||||
|
let multiSession = Gdm.get_session_ids().length > 1;
|
||||||
|
|
||||||
|
let visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
|
||||||
|
this._logoutItem.actor.visible = visible;
|
||||||
|
return visible;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSwitchUserSubMenu: function() {
|
||||||
|
this._switchUserSubMenu.label.text = this._user.get_real_name();
|
||||||
|
|
||||||
|
let iconFile = this._user.get_icon_file();
|
||||||
|
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||||
|
iconFile = null;
|
||||||
|
|
||||||
|
if (iconFile) {
|
||||||
|
let file = Gio.File.new_for_path(iconFile);
|
||||||
|
let gicon = new Gio.FileIcon({ file: file });
|
||||||
|
this._switchUserSubMenu.icon.gicon = gicon;
|
||||||
|
} else {
|
||||||
|
this._switchUserSubMenu.icon_name = 'avatar-default-symbolic';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLockScreen: function() {
|
||||||
|
let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||||
|
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||||
|
this._lockScreenAction.visible = showLock && allowLockScreen && LoginManager.canLock();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateHaveShutdown: function() {
|
||||||
|
this._session.CanShutdownRemote(Lang.bind(this,
|
||||||
|
function(result, error) {
|
||||||
|
if (!error) {
|
||||||
|
this._haveShutdown = result[0];
|
||||||
|
this._updatePowerOff();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePowerOff: function() {
|
||||||
|
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createActionButton: function(iconName, accessibleName) {
|
||||||
|
let icon = new St.Button({ reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
track_hover: true,
|
||||||
|
accessible_name: accessibleName,
|
||||||
|
style_class: 'system-menu-action' });
|
||||||
|
icon.child = new St.Icon({ icon_name: iconName });
|
||||||
|
return icon;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createSubMenu: function() {
|
||||||
|
let item;
|
||||||
|
|
||||||
|
this._switchUserSubMenu = new PopupMenu.PopupSubMenuMenuItem('', true);
|
||||||
|
this._switchUserSubMenu.icon.style_class = 'system-switch-user-submenu-icon';
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||||
|
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||||
|
this._switchUserSubMenu.menu.addMenuItem(item);
|
||||||
|
this._loginScreenItem = item;
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
||||||
|
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||||
|
this._switchUserSubMenu.menu.addMenuItem(item);
|
||||||
|
this._logoutItem = item;
|
||||||
|
|
||||||
|
this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu));
|
||||||
|
this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu));
|
||||||
|
|
||||||
|
this.menu.addMenuItem(this._switchUserSubMenu);
|
||||||
|
|
||||||
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
|
let hbox = new St.BoxLayout({ style_class: 'system-menu-actions-box' });
|
||||||
|
|
||||||
|
this._settingsAction = this._createActionButton('preferences-system-symbolic', _("Settings"));
|
||||||
|
this._settingsAction.connect('clicked', Lang.bind(this, this._onSettingsClicked));
|
||||||
|
hbox.add(this._settingsAction, { expand: true, x_fill: false });
|
||||||
|
|
||||||
|
this._lockScreenAction = this._createActionButton('changes-prevent-symbolic', _("Lock"));
|
||||||
|
this._lockScreenAction.connect('clicked', Lang.bind(this, this._onLockScreenClicked));
|
||||||
|
hbox.add(this._lockScreenAction, { expand: true, x_fill: false });
|
||||||
|
|
||||||
|
this._powerOffAction = this._createActionButton('system-shutdown-symbolic', _("Power Off"));
|
||||||
|
this._powerOffAction.connect('clicked', Lang.bind(this, this._onPowerOffClicked));
|
||||||
|
hbox.add(this._powerOffAction, { expand: true, x_fill: false });
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
|
||||||
|
can_focus: false });
|
||||||
|
item.addActor(hbox, { expand: true });
|
||||||
|
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSettingsClicked: function() {
|
||||||
|
this.menu.itemActivated();
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
|
||||||
|
Main.overview.hide();
|
||||||
|
app.activate();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLockScreenClicked: function() {
|
||||||
|
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
|
||||||
|
Main.overview.hide();
|
||||||
|
Main.screenShield.lock(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLoginScreenActivate: function() {
|
||||||
|
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
|
||||||
|
Main.overview.hide();
|
||||||
|
if (Main.screenShield)
|
||||||
|
Main.screenShield.lock(false);
|
||||||
|
Gdm.goto_login_session_sync(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onQuitSessionActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
this._session.LogoutRemote(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
_openSessionWarnDialog: function(sessions) {
|
||||||
|
let dialog = new ModalDialog.ModalDialog();
|
||||||
|
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
|
||||||
|
text: _("Other users are logged in.") });
|
||||||
|
dialog.contentLayout.add(subjectLabel, { y_fill: true,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
|
||||||
|
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
|
||||||
|
descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
|
||||||
|
scrollView.add_style_class_name('vfade');
|
||||||
|
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||||
|
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
|
||||||
|
|
||||||
|
let userList = new St.BoxLayout({ vertical: true });
|
||||||
|
scrollView.add_actor(userList);
|
||||||
|
|
||||||
|
for (let i = 0; i < sessions.length; i++) {
|
||||||
|
let session = sessions[i];
|
||||||
|
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
|
||||||
|
vertical: false });
|
||||||
|
let avatar = new UserWidget.Avatar(session.user);
|
||||||
|
avatar.update();
|
||||||
|
userEntry.add(avatar.actor);
|
||||||
|
|
||||||
|
let userLabelText = "";;
|
||||||
|
let userName = session.user.get_real_name() ?
|
||||||
|
session.user.get_real_name() : session.username;
|
||||||
|
|
||||||
|
if (session.info.remote)
|
||||||
|
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||||
|
userLabelText = _("%s (remote)").format(userName);
|
||||||
|
else if (session.info.type == "tty")
|
||||||
|
/* Translators: Console here refers to a tty like a VT console */
|
||||||
|
userLabelText = _("%s (console)").format(userName);
|
||||||
|
else
|
||||||
|
userLabelText = userName;
|
||||||
|
|
||||||
|
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||||
|
vertical: true });
|
||||||
|
textLayout.add(new St.Label({ text: userLabelText }),
|
||||||
|
{ y_fill: false,
|
||||||
|
y_align: St.Align.MIDDLE,
|
||||||
|
expand: true });
|
||||||
|
userEntry.add(textLayout, { expand: true });
|
||||||
|
userList.add(userEntry, { x_fill: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelButton = { label: _("Cancel"),
|
||||||
|
action: function() { dialog.close(); },
|
||||||
|
key: Clutter.Escape };
|
||||||
|
|
||||||
|
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
|
||||||
|
dialog.close();
|
||||||
|
this._session.ShutdownRemote();
|
||||||
|
}), default: true };
|
||||||
|
|
||||||
|
dialog.setButtons([cancelButton, powerOffButton]);
|
||||||
|
|
||||||
|
dialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPowerOffClicked: function() {
|
||||||
|
this.menu.itemActivated();
|
||||||
|
Main.overview.hide();
|
||||||
|
this._loginManager.listSessions(Lang.bind(this, function(result) {
|
||||||
|
let sessions = [];
|
||||||
|
let n = 0;
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||||
|
let proxy = new SystemdLoginSession(Gio.DBus.system,
|
||||||
|
'org.freedesktop.login1',
|
||||||
|
sessionPath);
|
||||||
|
|
||||||
|
if (proxy.Class != 'user')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (proxy.State == 'closing')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sessions.push({ user: this._userManager.get_user(userName),
|
||||||
|
username: userName,
|
||||||
|
info: { type: proxy.Type,
|
||||||
|
remote: proxy.Remote }
|
||||||
|
});
|
||||||
|
|
||||||
|
// limit the number of entries
|
||||||
|
n++;
|
||||||
|
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != 0)
|
||||||
|
this._openSessionWarnDialog(sessions);
|
||||||
|
else
|
||||||
|
this._session.ShutdownRemote();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
@ -9,8 +9,7 @@ const Signals = imports.signals;
|
|||||||
|
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const Slider = imports.ui.slider;
|
||||||
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
|
|
||||||
|
|
||||||
const VOLUME_NOTIFY_ID = 1;
|
const VOLUME_NOTIFY_ID = 1;
|
||||||
|
|
||||||
@ -30,21 +29,25 @@ function getMixerControl() {
|
|||||||
const StreamSlider = new Lang.Class({
|
const StreamSlider = new Lang.Class({
|
||||||
Name: 'StreamSlider',
|
Name: 'StreamSlider',
|
||||||
|
|
||||||
_init: function(control, title) {
|
_init: function(control) {
|
||||||
this._control = control;
|
this._control = control;
|
||||||
|
|
||||||
this.item = new PopupMenu.PopupMenuSection();
|
this.item = new PopupMenu.PopupBaseMenuItem({ activate: false });
|
||||||
|
|
||||||
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
|
this._slider = new Slider.Slider(0);
|
||||||
this._slider = new PopupMenu.PopupSliderMenuItem(0);
|
|
||||||
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
||||||
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||||
|
|
||||||
this.item.addMenuItem(this._title);
|
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||||
this.item.addMenuItem(this._slider);
|
|
||||||
|
this.item.addActor(this._icon, { align: St.Align.MIDDLE });
|
||||||
|
this.item.addActor(this._slider.actor, { expand: true });
|
||||||
|
|
||||||
|
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||||
|
this._slider.startDragging(event);
|
||||||
|
}));
|
||||||
|
|
||||||
this._stream = null;
|
this._stream = null;
|
||||||
this._shouldShow = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get stream() {
|
get stream() {
|
||||||
@ -86,8 +89,7 @@ const StreamSlider = new Lang.Class({
|
|||||||
|
|
||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
let visible = this._shouldBeVisible();
|
let visible = this._shouldBeVisible();
|
||||||
this._title.actor.visible = visible;
|
this.item.actor.visible = visible;
|
||||||
this._slider.actor.visible = visible;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
scroll: function(event) {
|
scroll: function(event) {
|
||||||
@ -181,11 +183,17 @@ const OutputStreamSlider = new Lang.Class({
|
|||||||
this._portChangedId = 0;
|
this._portChangedId = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateSliderIcon: function() {
|
||||||
|
this._icon.icon_name = (this._hasHeadphones ?
|
||||||
|
'audio-headphones-symbolic' :
|
||||||
|
'audio-speakers-symbolic');
|
||||||
|
},
|
||||||
|
|
||||||
_portChanged: function() {
|
_portChanged: function() {
|
||||||
let hasHeadphones = this._findHeadphones(this._stream);
|
let hasHeadphones = this._findHeadphones(this._stream);
|
||||||
if (hasHeadphones != this._hasHeadphones) {
|
if (hasHeadphones != this._hasHeadphones) {
|
||||||
this._hasHeadphones = hasHeadphones;
|
this._hasHeadphones = hasHeadphones;
|
||||||
this.emit('headphones-changed', this._hasHeadphones);
|
this._updateSliderIcon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -194,10 +202,11 @@ const InputStreamSlider = new Lang.Class({
|
|||||||
Name: 'InputStreamSlider',
|
Name: 'InputStreamSlider',
|
||||||
Extends: StreamSlider,
|
Extends: StreamSlider,
|
||||||
|
|
||||||
_init: function(control, title) {
|
_init: function(control) {
|
||||||
this.parent(control, title);
|
this.parent(control);
|
||||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||||
|
this._icon.icon_name = 'audio-input-microphone-symbolic';
|
||||||
},
|
},
|
||||||
|
|
||||||
_connectStream: function(stream) {
|
_connectStream: function(stream) {
|
||||||
@ -245,17 +254,13 @@ const VolumeMenu = new Lang.Class({
|
|||||||
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
|
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
|
||||||
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
||||||
|
|
||||||
/* Translators: This is the label for audio volume */
|
this._output = new OutputStreamSlider(this._control);
|
||||||
this._output = new OutputStreamSlider(this._control, _("Volume"));
|
|
||||||
this._output.connect('stream-updated', Lang.bind(this, function() {
|
this._output.connect('stream-updated', Lang.bind(this, function() {
|
||||||
this.emit('icon-changed');
|
this.emit('icon-changed');
|
||||||
}));
|
}));
|
||||||
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
|
|
||||||
this.emit('headphones-changed', value);
|
|
||||||
}));
|
|
||||||
this.addMenuItem(this._output.item);
|
this.addMenuItem(this._output.item);
|
||||||
|
|
||||||
this._input = new InputStreamSlider(this._control, _("Microphone"));
|
this._input = new InputStreamSlider(this._control);
|
||||||
this.addMenuItem(this._input.item);
|
this.addMenuItem(this._input.item);
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
@ -303,18 +308,9 @@ const Indicator = new Lang.Class({
|
|||||||
this.actor.visible = (icon != null);
|
this.actor.visible = (icon != null);
|
||||||
this.setIcon(icon);
|
this.setIcon(icon);
|
||||||
}));
|
}));
|
||||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
|
|
||||||
this._headphoneIcon.visible = value;
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
|
|
||||||
this._headphoneIcon.visible = false;
|
|
||||||
|
|
||||||
this.menu.addMenuItem(this._volumeMenu);
|
this.menu.addMenuItem(this._volumeMenu);
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
||||||
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
|
||||||
|
|
||||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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 AccountsService = imports.gi.AccountsService;
|
const AccountsService = imports.gi.AccountsService;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gdm = imports.gi.Gdm;
|
const Gdm = imports.gi.Gdm;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
@ -12,14 +13,13 @@ 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 Layout = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const ShellEntry = imports.ui.shellEntry;
|
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const UserMenu = imports.ui.userMenu;
|
|
||||||
const UserWidget = imports.ui.userWidget;
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
|
const AuthPrompt = imports.gdm.authPrompt;
|
||||||
const Batch = imports.gdm.batch;
|
const Batch = imports.gdm.batch;
|
||||||
const GdmUtil = imports.gdm.util;
|
const GdmUtil = imports.gdm.util;
|
||||||
const LoginDialog = imports.gdm.loginDialog;
|
const LoginDialog = imports.gdm.loginDialog;
|
||||||
@ -29,96 +29,35 @@ const IDLE_TIMEOUT = 2 * 60;
|
|||||||
|
|
||||||
const UnlockDialog = new Lang.Class({
|
const UnlockDialog = new Lang.Class({
|
||||||
Name: 'UnlockDialog',
|
Name: 'UnlockDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
|
||||||
|
|
||||||
_init: function(parentActor) {
|
_init: function(parentActor) {
|
||||||
this.parent({ shellReactive: true,
|
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
|
||||||
styleClass: 'login-dialog',
|
style_class: 'login-dialog',
|
||||||
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
|
visible: false });
|
||||||
parentActor: parentActor
|
|
||||||
});
|
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||||
|
parentActor.add_child(this.actor);
|
||||||
|
|
||||||
this._userManager = AccountsService.UserManager.get_default();
|
this._userManager = AccountsService.UserManager.get_default();
|
||||||
this._userName = GLib.get_user_name();
|
this._userName = GLib.get_user_name();
|
||||||
this._user = this._userManager.get_user(this._userName);
|
this._user = this._userManager.get_user(this._userName);
|
||||||
|
|
||||||
this._failCounter = 0;
|
this._promptBox = new St.BoxLayout({ vertical: true });
|
||||||
this._firstQuestion = true;
|
this.actor.add_child(this._promptBox);
|
||||||
|
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||||
|
align_axis: Clutter.AlignAxis.BOTH,
|
||||||
|
factor: 0.5 }));
|
||||||
|
|
||||||
this._greeterClient = new Gdm.Client();
|
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
|
||||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
|
this._authPrompt.connect('failed', Lang.bind(this, this._fail));
|
||||||
this._userVerified = false;
|
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail));
|
||||||
|
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||||
|
this._authPrompt.setPasswordChar('\u25cf');
|
||||||
|
this._authPrompt.nextButton.label = _("Unlock");
|
||||||
|
|
||||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
this._promptBox.add_child(this._authPrompt.actor);
|
||||||
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
|
||||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
|
||||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
|
||||||
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._userWidget = new UserWidget.UserWidget(this._user);
|
|
||||||
this.contentLayout.add_actor(this._userWidget.actor);
|
|
||||||
|
|
||||||
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
|
||||||
vertical: true });
|
|
||||||
|
|
||||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
|
||||||
this._promptLayout.add(this._promptLabel,
|
|
||||||
{ x_align: St.Align.START });
|
|
||||||
|
|
||||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
|
||||||
can_focus: true });
|
|
||||||
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
|
|
||||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
|
||||||
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
|
|
||||||
this.setInitialKeyFocus(this._promptEntry);
|
|
||||||
this._promptEntry.clutter_text.connect('text-changed', Lang.bind(this, function() {
|
|
||||||
this._updateOkButtonSensitivity(this._promptEntry.text.length > 0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._promptLayout.add(this._promptEntry,
|
|
||||||
{ expand: true,
|
|
||||||
x_fill: true });
|
|
||||||
|
|
||||||
this.contentLayout.add_actor(this._promptLayout);
|
|
||||||
|
|
||||||
this._promptMessage = new St.Label({ visible: false });
|
|
||||||
this.contentLayout.add(this._promptMessage, { x_fill: true });
|
|
||||||
|
|
||||||
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
|
|
||||||
this._promptLoginHint.hide();
|
|
||||||
this.contentLayout.add_actor(this._promptLoginHint);
|
|
||||||
|
|
||||||
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
|
||||||
this._workSpinner = new Panel.AnimatedIcon(spinnerIcon, LoginDialog.WORK_SPINNER_ICON_SIZE);
|
|
||||||
this._workSpinner.actor.opacity = 0;
|
|
||||||
|
|
||||||
this.allowCancel = false;
|
this.allowCancel = false;
|
||||||
this.buttonLayout.visible = true;
|
|
||||||
this.addButton({ label: _("Cancel"),
|
|
||||||
action: Lang.bind(this, this._escape),
|
|
||||||
key: Clutter.KEY_Escape },
|
|
||||||
{ expand: true,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
this.buttonLayout.add(this._workSpinner.actor,
|
|
||||||
{ expand: false,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.END,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
this._okButton = this.addButton({ label: _("Unlock"),
|
|
||||||
action: Lang.bind(this, this._doUnlock),
|
|
||||||
default: true },
|
|
||||||
{ expand: false,
|
|
||||||
x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.END,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
|
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
|
||||||
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
|
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
|
||||||
@ -131,196 +70,100 @@ const UnlockDialog = new Lang.Class({
|
|||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
x_fill: true });
|
x_fill: true });
|
||||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||||
this.dialogLayout.add(this._otherUserButton,
|
this._promptBox.add_child(this._otherUserButton);
|
||||||
{ x_align: St.Align.START,
|
|
||||||
x_fill: false });
|
|
||||||
} else {
|
} else {
|
||||||
this._otherUserButton = null;
|
this._otherUserButton = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._authPrompt.reset();
|
||||||
this._updateSensitivity(true);
|
this._updateSensitivity(true);
|
||||||
|
|
||||||
let batch = new Batch.Hold();
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic');
|
||||||
this._userVerifier.begin(this._userName, batch);
|
|
||||||
|
|
||||||
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
|
|
||||||
|
|
||||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||||
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateSensitivity: function(sensitive) {
|
_updateSensitivity: function(sensitive) {
|
||||||
this._promptEntry.reactive = sensitive;
|
this._authPrompt.updateSensitivity(sensitive);
|
||||||
this._promptEntry.clutter_text.editable = sensitive;
|
|
||||||
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
|
|
||||||
if (this._otherUserButton) {
|
if (this._otherUserButton) {
|
||||||
this._otherUserButton.reactive = sensitive;
|
this._otherUserButton.reactive = sensitive;
|
||||||
this._otherUserButton.can_focus = sensitive;
|
this._otherUserButton.can_focus = sensitive;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateOkButtonSensitivity: function(sensitive) {
|
_fail: function() {
|
||||||
this._okButton.reactive = sensitive;
|
this.emit('failed');
|
||||||
this._okButton.can_focus = sensitive;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_setWorking: function(working) {
|
_onReset: function(authPrompt, beginRequest) {
|
||||||
if (working) {
|
let userName;
|
||||||
this._workSpinner.play();
|
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||||
Tweener.addTween(this._workSpinner.actor,
|
this._authPrompt.setUser(this._user);
|
||||||
{ opacity: 255,
|
userName = this._userName;
|
||||||
delay: LoginDialog.WORK_SPINNER_ANIMATION_DELAY,
|
|
||||||
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
|
|
||||||
transition: 'linear'
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Tweener.addTween(this._workSpinner.actor,
|
userName = null;
|
||||||
{ opacity: 0,
|
|
||||||
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
|
|
||||||
transition: 'linear',
|
|
||||||
onCompleteScope: this,
|
|
||||||
onComplete: function() {
|
|
||||||
this._workSpinner.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_showMessage: function(userVerifier, message, styleClass) {
|
|
||||||
if (message) {
|
|
||||||
this._promptMessage.text = message;
|
|
||||||
this._promptMessage.styleClass = styleClass;
|
|
||||||
GdmUtil.fadeInActor(this._promptMessage);
|
|
||||||
} else {
|
|
||||||
GdmUtil.fadeOutActor(this._promptMessage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
|
||||||
if (this._firstQuestion && this._firstQuestionAnswer) {
|
|
||||||
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
|
|
||||||
this._firstQuestionAnswer = null;
|
|
||||||
this._firstQuestion = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._promptLabel.text = question;
|
this._authPrompt.begin({ userName: userName });
|
||||||
|
|
||||||
if (!this._firstQuestion)
|
|
||||||
this._promptEntry.text = '';
|
|
||||||
else
|
|
||||||
this._firstQuestion = false;
|
|
||||||
|
|
||||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
|
||||||
this._promptEntry.menu.isPassword = passwordChar != '';
|
|
||||||
|
|
||||||
this._currentQuery = serviceName;
|
|
||||||
this._updateSensitivity(true);
|
|
||||||
this._setWorking(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_showLoginHint: function(verifier, message) {
|
|
||||||
this._promptLoginHint.set_text(message)
|
|
||||||
GdmUtil.fadeInActor(this._promptLoginHint);
|
|
||||||
},
|
|
||||||
|
|
||||||
_hideLoginHint: function() {
|
|
||||||
GdmUtil.fadeOutActor(this._promptLoginHint);
|
|
||||||
},
|
|
||||||
|
|
||||||
_doUnlock: function() {
|
|
||||||
if (this._firstQuestion) {
|
|
||||||
// we haven't received a query yet, so stash the answer
|
|
||||||
// and make ourself non-reactive
|
|
||||||
// the actual reply to GDM will be sent as soon as asked
|
|
||||||
this._firstQuestionAnswer = this._promptEntry.text;
|
|
||||||
this._updateSensitivity(false);
|
|
||||||
this._setWorking(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._currentQuery)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let query = this._currentQuery;
|
|
||||||
this._currentQuery = null;
|
|
||||||
|
|
||||||
this._updateSensitivity(false);
|
|
||||||
this._setWorking(true);
|
|
||||||
|
|
||||||
this._userVerifier.answerQuery(query, this._promptEntry.text);
|
|
||||||
},
|
|
||||||
|
|
||||||
_finishUnlock: function() {
|
|
||||||
this._userVerifier.clear();
|
|
||||||
this.emit('unlocked');
|
|
||||||
},
|
|
||||||
|
|
||||||
_onVerificationComplete: function() {
|
|
||||||
this._userVerified = true;
|
|
||||||
if (!this._userVerifier.hasPendingMessages) {
|
|
||||||
this._finishUnlock();
|
|
||||||
} else {
|
|
||||||
let signalId = this._userVerifier.connect('no-more-messages',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._userVerifier.disconnect(signalId);
|
|
||||||
this._finishUnlock();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onReset: function() {
|
|
||||||
if (!this._userVerified) {
|
|
||||||
this._userVerifier.clear();
|
|
||||||
this.emit('failed');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onVerificationFailed: function() {
|
|
||||||
this._currentQuery = null;
|
|
||||||
this._firstQuestion = true;
|
|
||||||
this._userVerified = false;
|
|
||||||
|
|
||||||
this._promptEntry.text = '';
|
|
||||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
|
||||||
this._promptEntry.menu.isPassword = true;
|
|
||||||
|
|
||||||
this._updateSensitivity(false);
|
|
||||||
this._setWorking(false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_escape: function() {
|
_escape: function() {
|
||||||
if (this.allowCancel) {
|
if (this.allowCancel)
|
||||||
this._userVerifier.cancel();
|
this._authPrompt.cancel();
|
||||||
this.emit('failed');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_otherUserClicked: function(button, event) {
|
_otherUserClicked: function(button, event) {
|
||||||
Gdm.goto_login_session_sync(null);
|
Gdm.goto_login_session_sync(null);
|
||||||
|
|
||||||
this._userVerifier.cancel();
|
this._authPrompt.cancel();
|
||||||
this.emit('failed');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this._userVerifier.clear();
|
this.popModal();
|
||||||
|
this.actor.destroy();
|
||||||
|
|
||||||
if (this._idleWatchId) {
|
if (this._idleWatchId) {
|
||||||
this._idleMonitor.remove_watch(this._idleWatchId);
|
this._idleMonitor.remove_watch(this._idleWatchId);
|
||||||
this._idleWatchId = 0;
|
this._idleWatchId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parent();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
this._userVerifier.cancel(null);
|
this._authPrompt.cancel();
|
||||||
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
addCharacter: function(unichar) {
|
addCharacter: function(unichar) {
|
||||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
this._authPrompt.addCharacter(unichar);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
finish: function(onComplete) {
|
||||||
|
this._authPrompt.finish(onComplete);
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function(timestamp) {
|
||||||
|
this.actor.show();
|
||||||
|
|
||||||
|
if (this._isModal)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!Main.pushModal(this.actor, { timestamp: timestamp,
|
||||||
|
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN }))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._isModal = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
popModal: function(timestamp) {
|
||||||
|
if (this._isModal) {
|
||||||
|
Main.popModal(this.actor, timestamp);
|
||||||
|
this._isModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(UnlockDialog.prototype);
|
||||||
|
@ -1,998 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const AccountsService = imports.gi.AccountsService;
|
|
||||||
const Gdm = imports.gi.Gdm;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const GLib = imports.gi.GLib;
|
|
||||||
const Gtk = imports.gi.Gtk;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Pango = imports.gi.Pango;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const St = imports.gi.St;
|
|
||||||
const Tp = imports.gi.TelepathyGLib;
|
|
||||||
const Atk = imports.gi.Atk;
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
|
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
|
||||||
const LoginManager = imports.misc.loginManager;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
|
||||||
const Params = imports.misc.params;
|
|
||||||
const Util = imports.misc.util;
|
|
||||||
|
|
||||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
|
||||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
|
||||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
|
||||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
|
||||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
|
||||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
|
||||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
|
||||||
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
|
|
||||||
|
|
||||||
const DIALOG_ICON_SIZE = 64;
|
|
||||||
|
|
||||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
|
||||||
|
|
||||||
const IMStatus = {
|
|
||||||
AVAILABLE: 0,
|
|
||||||
BUSY: 1,
|
|
||||||
HIDDEN: 2,
|
|
||||||
AWAY: 3,
|
|
||||||
IDLE: 4,
|
|
||||||
OFFLINE: 5,
|
|
||||||
LAST: 6
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
|
||||||
<property name="Id" type="s" access="read"/>
|
|
||||||
<property name="Remote" type="b" access="read"/>
|
|
||||||
<property name="Class" type="s" access="read"/>
|
|
||||||
<property name="Type" type="s" access="read"/>
|
|
||||||
<property name="State" type="s" access="read"/>
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
|
||||||
|
|
||||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
|
||||||
//
|
|
||||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
|
||||||
// Copyright (C) 2008,2009 Red Hat, Inc.
|
|
||||||
|
|
||||||
const UserAvatarWidget = new Lang.Class({
|
|
||||||
Name: 'UserAvatarWidget',
|
|
||||||
|
|
||||||
_init: function(user, params) {
|
|
||||||
this._user = user;
|
|
||||||
params = Params.parse(params, { reactive: false,
|
|
||||||
iconSize: DIALOG_ICON_SIZE,
|
|
||||||
styleClass: 'status-chooser-user-icon' });
|
|
||||||
this._iconSize = params.iconSize;
|
|
||||||
|
|
||||||
this.actor = new St.Bin({ style_class: params.styleClass,
|
|
||||||
track_hover: params.reactive,
|
|
||||||
reactive: params.reactive });
|
|
||||||
},
|
|
||||||
|
|
||||||
setSensitive: function(sensitive) {
|
|
||||||
this.actor.can_focus = sensitive;
|
|
||||||
this.actor.reactive = sensitive;
|
|
||||||
},
|
|
||||||
|
|
||||||
update: function() {
|
|
||||||
let iconFile = this._user.get_icon_file();
|
|
||||||
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
|
||||||
iconFile = null;
|
|
||||||
|
|
||||||
if (iconFile) {
|
|
||||||
let file = Gio.File.new_for_path(iconFile);
|
|
||||||
this.actor.child = null;
|
|
||||||
this.actor.style = 'background-image: url("%s");'.format(iconFile);
|
|
||||||
} else {
|
|
||||||
this.actor.style = null;
|
|
||||||
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
|
|
||||||
icon_size: this._iconSize });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const IMStatusItem = new Lang.Class({
|
|
||||||
Name: 'IMStatusItem',
|
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
|
||||||
|
|
||||||
_init: function(label, iconName) {
|
|
||||||
this.parent();
|
|
||||||
|
|
||||||
this.actor.add_style_class_name('status-chooser-status-item');
|
|
||||||
|
|
||||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
|
||||||
this.addActor(this._icon);
|
|
||||||
|
|
||||||
if (iconName)
|
|
||||||
this._icon.icon_name = iconName;
|
|
||||||
|
|
||||||
this.label = new St.Label({ text: label });
|
|
||||||
this.actor.label_actor = this.label;
|
|
||||||
this.addActor(this.label);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const IMUserNameItem = new Lang.Class({
|
|
||||||
Name: 'IMUserNameItem',
|
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.parent({ reactive: false,
|
|
||||||
can_focus: false,
|
|
||||||
style_class: 'status-chooser-user-name' });
|
|
||||||
|
|
||||||
this._wrapper = new Shell.GenericContainer();
|
|
||||||
this._wrapper.connect('get-preferred-width',
|
|
||||||
Lang.bind(this, this._wrapperGetPreferredWidth));
|
|
||||||
this._wrapper.connect('get-preferred-height',
|
|
||||||
Lang.bind(this, this._wrapperGetPreferredHeight));
|
|
||||||
this._wrapper.connect('allocate',
|
|
||||||
Lang.bind(this, this._wrapperAllocate));
|
|
||||||
this.addActor(this._wrapper, { expand: true, span: -1 });
|
|
||||||
|
|
||||||
this.label = new St.Label();
|
|
||||||
this.label.clutter_text.set_line_wrap(true);
|
|
||||||
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
|
||||||
this._wrapper.add_actor(this.label);
|
|
||||||
},
|
|
||||||
|
|
||||||
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
|
|
||||||
alloc.min_size = 1;
|
|
||||||
alloc.natural_size = 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
|
|
||||||
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
|
|
||||||
},
|
|
||||||
|
|
||||||
_wrapperAllocate: function(actor, box, flags) {
|
|
||||||
this.label.allocate(box, flags);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const IMStatusChooserItem = new Lang.Class({
|
|
||||||
Name: 'IMStatusChooserItem',
|
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.parent({ reactive: false,
|
|
||||||
can_focus: false,
|
|
||||||
style_class: 'status-chooser' });
|
|
||||||
|
|
||||||
this._userManager = AccountsService.UserManager.get_default();
|
|
||||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
|
||||||
|
|
||||||
this._avatar = new UserAvatarWidget(this._user, { reactive: true });
|
|
||||||
this._iconBin = new St.Button({ child: this._avatar.actor });
|
|
||||||
this.addActor(this._iconBin);
|
|
||||||
|
|
||||||
this._iconBin.connect('clicked', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this.activate();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._section = new PopupMenu.PopupMenuSection();
|
|
||||||
this.addActor(this._section.actor);
|
|
||||||
|
|
||||||
this._name = new IMUserNameItem();
|
|
||||||
this._section.addMenuItem(this._name);
|
|
||||||
|
|
||||||
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
|
|
||||||
this._section.addMenuItem(this._combo);
|
|
||||||
|
|
||||||
let item;
|
|
||||||
|
|
||||||
item = new IMStatusItem(_("Available"), 'user-available-symbolic');
|
|
||||||
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
|
|
||||||
|
|
||||||
item = new IMStatusItem(_("Busy"), 'user-busy-symbolic');
|
|
||||||
this._combo.addMenuItem(item, IMStatus.BUSY);
|
|
||||||
|
|
||||||
item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic');
|
|
||||||
this._combo.addMenuItem(item, IMStatus.HIDDEN);
|
|
||||||
|
|
||||||
item = new IMStatusItem(_("Away"), 'user-away-symbolic');
|
|
||||||
this._combo.addMenuItem(item, IMStatus.AWAY);
|
|
||||||
|
|
||||||
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
|
|
||||||
this._combo.addMenuItem(item, IMStatus.IDLE);
|
|
||||||
|
|
||||||
item = new IMStatusItem(_("Offline"), 'user-offline-symbolic');
|
|
||||||
this._combo.addMenuItem(item, IMStatus.OFFLINE);
|
|
||||||
|
|
||||||
this._combo.connect('active-item-changed',
|
|
||||||
Lang.bind(this, this._changeIMStatus));
|
|
||||||
|
|
||||||
this._presence = new GnomeSession.Presence();
|
|
||||||
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
|
|
||||||
this._sessionStatusChanged(status);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._sessionPresenceRestored = false;
|
|
||||||
this._imPresenceRestored = false;
|
|
||||||
this._currentPresence = undefined;
|
|
||||||
|
|
||||||
this._accountMgr = Tp.AccountManager.dup();
|
|
||||||
this._accountMgr.connect('most-available-presence-changed',
|
|
||||||
Lang.bind(this, this._IMStatusChanged));
|
|
||||||
this._accountMgr.connect('account-enabled',
|
|
||||||
Lang.bind(this, this._IMAccountsChanged));
|
|
||||||
this._accountMgr.connect('account-disabled',
|
|
||||||
Lang.bind(this, this._IMAccountsChanged));
|
|
||||||
this._accountMgr.connect('account-removed',
|
|
||||||
Lang.bind(this, this._IMAccountsChanged));
|
|
||||||
this._accountMgr.connect('account-validity-changed',
|
|
||||||
Lang.bind(this, this._IMAccountsChanged));
|
|
||||||
this._accountMgr.prepare_async(null, Lang.bind(this,
|
|
||||||
function(mgr) {
|
|
||||||
this._IMAccountsChanged(mgr);
|
|
||||||
|
|
||||||
if (this._networkMonitor.network_available)
|
|
||||||
this._restorePresence();
|
|
||||||
else
|
|
||||||
this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._networkMonitor = Gio.NetworkMonitor.get_default();
|
|
||||||
this._networkMonitor.connect('network-changed',
|
|
||||||
Lang.bind(this, function(monitor, available) {
|
|
||||||
this._IMAccountsChanged(this._accountMgr);
|
|
||||||
|
|
||||||
if (available && !this._imPresenceRestored)
|
|
||||||
this._restorePresence();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._updateUser));
|
|
||||||
this._userChangedId = this._user.connect('changed',
|
|
||||||
Lang.bind(this,
|
|
||||||
this._updateUser));
|
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this, function() {
|
|
||||||
if (this.actor.mapped)
|
|
||||||
this._updateUser();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.connect('sensitive-changed', function(sensitive) {
|
|
||||||
this._avatar.setSensitive(sensitive);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_restorePresence: function() {
|
|
||||||
let [presence, status, msg] = this._accountMgr.get_most_available_presence();
|
|
||||||
|
|
||||||
let savedPresence = global.settings.get_int('saved-im-presence');
|
|
||||||
|
|
||||||
if (savedPresence == presence) {
|
|
||||||
this._IMStatusChanged(this._accountMgr, presence, status, msg);
|
|
||||||
} else {
|
|
||||||
this._setComboboxPresence(savedPresence);
|
|
||||||
status = this._statusForPresence(savedPresence);
|
|
||||||
msg = msg ? msg : '';
|
|
||||||
this._accountMgr.set_all_requested_presences(savedPresence, status, msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy: function() {
|
|
||||||
// clean up signal handlers
|
|
||||||
if (this._userLoadedId != 0) {
|
|
||||||
this._user.disconnect(this._userLoadedId);
|
|
||||||
this._userLoadedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._userChangedId != 0) {
|
|
||||||
this._user.disconnect(this._userChangedId);
|
|
||||||
this._userChangedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.parent();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override getColumnWidths()/setColumnWidths() to make the item
|
|
||||||
// independent from the overall column layout of the menu
|
|
||||||
getColumnWidths: function() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
|
|
||||||
setColumnWidths: function(widths) {
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateUser: function() {
|
|
||||||
if (this._user.is_loaded)
|
|
||||||
this._name.label.set_text(this._user.get_real_name());
|
|
||||||
else
|
|
||||||
this._name.label.set_text("");
|
|
||||||
|
|
||||||
this._avatar.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
_statusForPresence: function(presence) {
|
|
||||||
switch(presence) {
|
|
||||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
|
||||||
return 'available';
|
|
||||||
case Tp.ConnectionPresenceType.BUSY:
|
|
||||||
return 'busy';
|
|
||||||
case Tp.ConnectionPresenceType.OFFLINE:
|
|
||||||
return 'offline';
|
|
||||||
case Tp.ConnectionPresenceType.HIDDEN:
|
|
||||||
return 'hidden';
|
|
||||||
case Tp.ConnectionPresenceType.AWAY:
|
|
||||||
return 'away';
|
|
||||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
|
||||||
return 'xa';
|
|
||||||
default:
|
|
||||||
return 'unknown';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_IMAccountsChanged: function(mgr) {
|
|
||||||
let accounts = mgr.get_valid_accounts().filter(function(account) {
|
|
||||||
return account.enabled;
|
|
||||||
});
|
|
||||||
let sensitive = accounts.length > 0 && this._networkMonitor.network_available;
|
|
||||||
this._combo.setSensitive(sensitive);
|
|
||||||
},
|
|
||||||
|
|
||||||
_IMStatusChanged: function(accountMgr, presence, status, message) {
|
|
||||||
if (!this._imPresenceRestored)
|
|
||||||
this._imPresenceRestored = true;
|
|
||||||
|
|
||||||
if (presence == this._currentPresence)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._currentPresence = presence;
|
|
||||||
this._setComboboxPresence(presence);
|
|
||||||
|
|
||||||
if (!this._sessionPresenceRestored) {
|
|
||||||
this._sessionStatusChanged(this._presence.status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
|
||||||
this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
|
|
||||||
|
|
||||||
// We ignore the actual value of _expectedPresence and never safe
|
|
||||||
// the first presence change after an "automatic" change, assuming
|
|
||||||
// that it is the response to our request; this is to account for
|
|
||||||
// mission control falling back to "similar" presences if an account
|
|
||||||
// type does not implement the requested presence.
|
|
||||||
if (!this._expectedPresence)
|
|
||||||
global.settings.set_int('saved-im-presence', presence);
|
|
||||||
else
|
|
||||||
this._expectedPresence = undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setComboboxPresence: function(presence) {
|
|
||||||
let activatedItem;
|
|
||||||
|
|
||||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
|
||||||
activatedItem = IMStatus.AVAILABLE;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
|
||||||
activatedItem = IMStatus.BUSY;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
|
|
||||||
activatedItem = IMStatus.HIDDEN;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.AWAY)
|
|
||||||
activatedItem = IMStatus.AWAY;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
|
||||||
activatedItem = IMStatus.IDLE;
|
|
||||||
else
|
|
||||||
activatedItem = IMStatus.OFFLINE;
|
|
||||||
|
|
||||||
this._combo.setActiveItem(activatedItem);
|
|
||||||
for (let i = 0; i < IMStatus.LAST; i++) {
|
|
||||||
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
|
|
||||||
continue; // always visible
|
|
||||||
|
|
||||||
this._combo.setItemVisible(i, i == activatedItem);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_changeIMStatus: function(menuItem, id) {
|
|
||||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
|
||||||
let newPresence, status;
|
|
||||||
|
|
||||||
if (id == IMStatus.AVAILABLE) {
|
|
||||||
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
|
|
||||||
} else if (id == IMStatus.OFFLINE) {
|
|
||||||
newPresence = Tp.ConnectionPresenceType.OFFLINE;
|
|
||||||
} else
|
|
||||||
return;
|
|
||||||
|
|
||||||
status = this._statusForPresence(newPresence);
|
|
||||||
msg = msg ? msg : '';
|
|
||||||
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
|
||||||
},
|
|
||||||
|
|
||||||
getIMPresenceForSessionStatus: function(sessionStatus) {
|
|
||||||
// Restore the last user-set presence when coming back from
|
|
||||||
// BUSY/IDLE (otherwise the last user-set presence matches
|
|
||||||
// the current one)
|
|
||||||
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
|
|
||||||
return global.settings.get_int('saved-im-presence');
|
|
||||||
|
|
||||||
if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
|
|
||||||
// Only change presence if the current one is "more present" than
|
|
||||||
// busy, or if coming back from idle
|
|
||||||
if (this._currentPresence == Tp.ConnectionPresenceType.AVAILABLE ||
|
|
||||||
this._currentPresence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
|
||||||
return Tp.ConnectionPresenceType.BUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
|
|
||||||
// Only change presence if the current one is "more present" than
|
|
||||||
// idle
|
|
||||||
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
|
|
||||||
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
|
|
||||||
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._currentPresence;
|
|
||||||
},
|
|
||||||
|
|
||||||
_sessionStatusChanged: function(sessionStatus) {
|
|
||||||
if (!this._imPresenceRestored)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let savedStatus = global.settings.get_int('saved-session-presence');
|
|
||||||
if (!this._sessionPresenceRestored) {
|
|
||||||
|
|
||||||
// We should never save/restore a status other than AVAILABLE
|
|
||||||
// or BUSY
|
|
||||||
if (savedStatus != GnomeSession.PresenceStatus.AVAILABLE &&
|
|
||||||
savedStatus != GnomeSession.PresenceStatus.BUSY)
|
|
||||||
savedStatus = GnomeSession.PresenceStatus.AVAILABLE;
|
|
||||||
|
|
||||||
if (sessionStatus != savedStatus) {
|
|
||||||
this._presence.status = savedStatus;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._sessionPresenceRestored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((sessionStatus == GnomeSession.PresenceStatus.AVAILABLE ||
|
|
||||||
sessionStatus == GnomeSession.PresenceStatus.BUSY) &&
|
|
||||||
savedStatus != sessionStatus)
|
|
||||||
global.settings.set_int('saved-session-presence', sessionStatus);
|
|
||||||
|
|
||||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
|
||||||
let newPresence, status;
|
|
||||||
|
|
||||||
let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
|
|
||||||
|
|
||||||
if (!newPresence || newPresence == presence)
|
|
||||||
return;
|
|
||||||
|
|
||||||
status = this._statusForPresence(newPresence);
|
|
||||||
msg = msg ? msg : '';
|
|
||||||
|
|
||||||
this._expectedPresence = newPresence;
|
|
||||||
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const UserMenuButton = new Lang.Class({
|
|
||||||
Name: 'UserMenuButton',
|
|
||||||
Extends: PanelMenu.Button,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.parent(0.0);
|
|
||||||
|
|
||||||
this.actor.accessible_role = Atk.Role.MENU;
|
|
||||||
|
|
||||||
let box = new St.BoxLayout({ name: 'panelUserMenu' });
|
|
||||||
this.actor.add_actor(box);
|
|
||||||
|
|
||||||
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
|
||||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
|
||||||
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
|
||||||
|
|
||||||
this._userManager = AccountsService.UserManager.get_default();
|
|
||||||
|
|
||||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
|
||||||
this._presence = new GnomeSession.Presence();
|
|
||||||
this._session = new GnomeSession.SessionManager();
|
|
||||||
this._haveShutdown = true;
|
|
||||||
this._haveSuspend = true;
|
|
||||||
|
|
||||||
this._accountMgr = Tp.AccountManager.dup();
|
|
||||||
|
|
||||||
this._loginManager = LoginManager.getLoginManager();
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
||||||
|
|
||||||
this._iconBox = new St.Bin();
|
|
||||||
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
|
|
||||||
|
|
||||||
let textureCache = St.TextureCache.get_default();
|
|
||||||
this._offlineIcon = new St.Icon({ icon_name: 'user-offline-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._availableIcon = new St.Icon({ icon_name: 'user-available-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._busyIcon = new St.Icon({ icon_name: 'user-busy-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._awayIcon = new St.Icon({ icon_name: 'user-away-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._idleIcon = new St.Icon({ icon_name: 'user-idle-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
|
|
||||||
style_class: 'popup-menu-icon' });
|
|
||||||
|
|
||||||
this._accountMgr.connect('most-available-presence-changed',
|
|
||||||
Lang.bind(this, this._updatePresenceIcon));
|
|
||||||
this._accountMgr.connect('account-enabled',
|
|
||||||
Lang.bind(this, this._onAccountEnabled));
|
|
||||||
this._accountMgr.connect('account-removed',
|
|
||||||
Lang.bind(this, this._onAccountRemoved));
|
|
||||||
this._accountMgr.prepare_async(null, Lang.bind(this,
|
|
||||||
function(mgr) {
|
|
||||||
let [presence, s, msg] = mgr.get_most_available_presence();
|
|
||||||
this._updatePresenceIcon(mgr, presence, s, msg);
|
|
||||||
this._setupAccounts();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._name = new St.Label();
|
|
||||||
this.actor.label_actor = this._name;
|
|
||||||
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
|
|
||||||
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
|
|
||||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
|
|
||||||
this._updateUserName();
|
|
||||||
|
|
||||||
this._createSubMenu();
|
|
||||||
|
|
||||||
this._updateSwitch(this._presence.status);
|
|
||||||
this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
|
|
||||||
this._updateSwitch(status);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._userManager.connect('notify::is-loaded',
|
|
||||||
Lang.bind(this, this._updateMultiUser));
|
|
||||||
this._userManager.connect('notify::has-multiple-users',
|
|
||||||
Lang.bind(this, this._updateMultiUser));
|
|
||||||
this._userManager.connect('user-added',
|
|
||||||
Lang.bind(this, this._updateMultiUser));
|
|
||||||
this._userManager.connect('user-removed',
|
|
||||||
Lang.bind(this, this._updateMultiUser));
|
|
||||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
|
||||||
Lang.bind(this, this._updateSwitchUser));
|
|
||||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
|
||||||
Lang.bind(this, this._updateLogout));
|
|
||||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
|
||||||
Lang.bind(this, this._updateLockScreen));
|
|
||||||
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
|
|
||||||
Lang.bind(this, this._updateLogout));
|
|
||||||
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
|
||||||
Lang.bind(this, this._updateUserName));
|
|
||||||
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
|
||||||
Lang.bind(this, this._updateUserName));
|
|
||||||
this._updateSwitchUser();
|
|
||||||
this._updateLogout();
|
|
||||||
this._updateLockScreen();
|
|
||||||
|
|
||||||
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
|
|
||||||
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
|
|
||||||
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
|
|
||||||
|
|
||||||
// Whether shutdown is available or not depends on both lockdown
|
|
||||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
|
||||||
// notify, so we update the menu item each time the menu opens or
|
|
||||||
// the lockdown setting changes, which should be close enough.
|
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
|
||||||
function(menu, open) {
|
|
||||||
if (!open)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._updateHaveShutdown();
|
|
||||||
this._updateHaveSuspend();
|
|
||||||
}));
|
|
||||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
|
||||||
Lang.bind(this, this._updateHaveShutdown));
|
|
||||||
|
|
||||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
|
||||||
if (Main.screenShield)
|
|
||||||
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
|
|
||||||
this._sessionUpdated();
|
|
||||||
},
|
|
||||||
|
|
||||||
_sessionUpdated: function() {
|
|
||||||
this.actor.visible = !Main.sessionMode.isGreeter;
|
|
||||||
|
|
||||||
let allowSettings = Main.sessionMode.allowSettings;
|
|
||||||
this._statusChooser.setSensitive(allowSettings);
|
|
||||||
this._systemSettings.visible = allowSettings;
|
|
||||||
|
|
||||||
this.setSensitive(!Main.sessionMode.isLocked);
|
|
||||||
this._updatePresenceIcon();
|
|
||||||
this._updateUserName();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
this._user.disconnect(this._userLoadedId);
|
|
||||||
this._user.disconnect(this._userChangedId);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateUserName: function() {
|
|
||||||
let settings = this._privacySettings;
|
|
||||||
if (Main.sessionMode.isLocked)
|
|
||||||
settings = this._screenSaverSettings;
|
|
||||||
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
|
|
||||||
this._name.set_text(this._user.get_real_name());
|
|
||||||
else
|
|
||||||
this._name.set_text("");
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateMultiUser: function() {
|
|
||||||
this._updateSwitchUser();
|
|
||||||
this._updateLogout();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSwitchUser: function() {
|
|
||||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
|
||||||
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
|
|
||||||
|
|
||||||
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateLogout: function() {
|
|
||||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
|
||||||
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
|
|
||||||
let systemAccount = this._user.system_account;
|
|
||||||
let localAccount = this._user.local_account;
|
|
||||||
let multiUser = this._userManager.has_multiple_users;
|
|
||||||
let multiSession = Gdm.get_session_ids().length > 1;
|
|
||||||
|
|
||||||
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateLockScreen: function() {
|
|
||||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
|
||||||
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateInstallUpdates: function() {
|
|
||||||
let haveUpdates = this._updatesFile.query_exists(null);
|
|
||||||
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateHaveShutdown: function() {
|
|
||||||
this._session.CanShutdownRemote(Lang.bind(this,
|
|
||||||
function(result, error) {
|
|
||||||
if (!error) {
|
|
||||||
this._haveShutdown = result[0];
|
|
||||||
this._updateInstallUpdates();
|
|
||||||
this._updateSuspendOrPowerOff();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateHaveSuspend: function() {
|
|
||||||
this._loginManager.canSuspend(Lang.bind(this,
|
|
||||||
function(result) {
|
|
||||||
this._haveSuspend = result;
|
|
||||||
this._updateSuspendOrPowerOff();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSuspendOrPowerOff: function() {
|
|
||||||
if (!this._suspendOrPowerOffItem)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
|
|
||||||
|
|
||||||
// If we can't power off show Suspend instead
|
|
||||||
// and disable the alt key
|
|
||||||
if (!this._haveShutdown) {
|
|
||||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
|
|
||||||
} else if (!this._haveSuspend) {
|
|
||||||
this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
|
|
||||||
} else {
|
|
||||||
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSwitch: function(status) {
|
|
||||||
let active = status == GnomeSession.PresenceStatus.AVAILABLE;
|
|
||||||
this._notificationsSwitch.setToggleState(active);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updatePresenceIcon: function(accountMgr, presence, status, message) {
|
|
||||||
if (Main.sessionMode.isLocked)
|
|
||||||
this._iconBox.child = this._lockedIcon;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
|
||||||
this._iconBox.child = this._availableIcon;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
|
||||||
this._iconBox.child = this._busyIcon;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
|
|
||||||
this._iconBox.child = this._invisibleIcon;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.AWAY)
|
|
||||||
this._iconBox.child = this._awayIcon;
|
|
||||||
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
|
||||||
this._iconBox.child = this._idleIcon;
|
|
||||||
else
|
|
||||||
this._iconBox.child = this._offlineIcon;
|
|
||||||
|
|
||||||
if (Main.sessionMode.isLocked)
|
|
||||||
this._iconBox.visible = Main.screenShield.locked;
|
|
||||||
else
|
|
||||||
this._iconBox.visible = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setupAccounts: function() {
|
|
||||||
let accounts = this._accountMgr.get_valid_accounts();
|
|
||||||
for (let i = 0; i < accounts.length; i++) {
|
|
||||||
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
|
|
||||||
Lang.bind(this, this._updateChangingPresence));
|
|
||||||
}
|
|
||||||
this._updateChangingPresence();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onAccountEnabled: function(accountMgr, account) {
|
|
||||||
if (!account._changingId)
|
|
||||||
account._changingId = account.connect('notify::connection-status',
|
|
||||||
Lang.bind(this, this._updateChangingPresence));
|
|
||||||
this._updateChangingPresence();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onAccountRemoved: function(accountMgr, account) {
|
|
||||||
if (account._changingId) {
|
|
||||||
account.disconnect(account._changingId);
|
|
||||||
account._changingId = 0;
|
|
||||||
}
|
|
||||||
this._updateChangingPresence();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateChangingPresence: function() {
|
|
||||||
let accounts = this._accountMgr.get_valid_accounts();
|
|
||||||
let changing = false;
|
|
||||||
for (let i = 0; i < accounts.length; i++) {
|
|
||||||
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
|
|
||||||
changing = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changing) {
|
|
||||||
this._iconBox.child = this._pendingIcon;
|
|
||||||
} else {
|
|
||||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
|
||||||
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_createSubMenu: function() {
|
|
||||||
let item;
|
|
||||||
|
|
||||||
item = new IMStatusChooserItem();
|
|
||||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._statusChooser = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
|
|
||||||
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._notificationsSwitch = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Settings"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._systemSettings = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._loginScreenItem = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._logoutItem = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Lock"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._lockScreenItem = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
|
|
||||||
_("Suspend"));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
|
||||||
this._suspendOrPowerOffItem = item;
|
|
||||||
this._updateSuspendOrPowerOff();
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._installUpdatesItem = item;
|
|
||||||
},
|
|
||||||
|
|
||||||
_updatePresenceStatus: function(item, event) {
|
|
||||||
let status;
|
|
||||||
|
|
||||||
if (item.state) {
|
|
||||||
status = GnomeSession.PresenceStatus.AVAILABLE;
|
|
||||||
} else {
|
|
||||||
status = GnomeSession.PresenceStatus.BUSY;
|
|
||||||
|
|
||||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
|
||||||
let newPresence = this._statusChooser.getIMPresenceForSessionStatus(status);
|
|
||||||
if (newPresence != presence &&
|
|
||||||
newPresence == Tp.ConnectionPresenceType.BUSY)
|
|
||||||
Main.notify(_("Your chat status will be set to busy"),
|
|
||||||
_("Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._presence.status = status;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMyAccountActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
|
|
||||||
app.activate();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onPreferencesActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
|
|
||||||
app.activate();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onLockScreenActivate: function() {
|
|
||||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
|
||||||
Main.overview.hide();
|
|
||||||
Main.screenShield.lock(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onLoginScreenActivate: function() {
|
|
||||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
|
||||||
Main.overview.hide();
|
|
||||||
if (Main.screenShield)
|
|
||||||
Main.screenShield.lock(false);
|
|
||||||
Gdm.goto_login_session_sync(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onQuitSessionActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
this._session.LogoutRemote(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onInstallUpdatesActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
|
|
||||||
|
|
||||||
this._session.RebootRemote();
|
|
||||||
},
|
|
||||||
|
|
||||||
_openSessionWarnDialog: function(sessions) {
|
|
||||||
let dialog = new ModalDialog.ModalDialog();
|
|
||||||
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
|
|
||||||
text: _("Other users are logged in.") });
|
|
||||||
dialog.contentLayout.add(subjectLabel, { y_fill: true,
|
|
||||||
y_align: St.Align.START });
|
|
||||||
|
|
||||||
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
|
|
||||||
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
|
|
||||||
descriptionLabel.clutter_text.line_wrap = true;
|
|
||||||
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
y_align: St.Align.START });
|
|
||||||
|
|
||||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
|
|
||||||
scrollView.add_style_class_name('vfade');
|
|
||||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
||||||
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
|
|
||||||
|
|
||||||
let userList = new St.BoxLayout({ vertical: true });
|
|
||||||
scrollView.add_actor(userList);
|
|
||||||
|
|
||||||
for (let i = 0; i < sessions.length; i++) {
|
|
||||||
let session = sessions[i];
|
|
||||||
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
|
|
||||||
vertical: false });
|
|
||||||
let avatar = new UserAvatarWidget(session.user);
|
|
||||||
avatar.update();
|
|
||||||
userEntry.add(avatar.actor);
|
|
||||||
|
|
||||||
let userLabelText = "";;
|
|
||||||
let userName = session.user.get_real_name() ?
|
|
||||||
session.user.get_real_name() : session.username;
|
|
||||||
|
|
||||||
if (session.info.remote)
|
|
||||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
|
||||||
userLabelText = _("%s (remote)").format(userName);
|
|
||||||
else if (session.info.type == "tty")
|
|
||||||
/* Translators: Console here refers to a tty like a VT console */
|
|
||||||
userLabelText = _("%s (console)").format(userName);
|
|
||||||
else
|
|
||||||
userLabelText = userName;
|
|
||||||
|
|
||||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
|
||||||
vertical: true });
|
|
||||||
textLayout.add(new St.Label({ text: userLabelText }),
|
|
||||||
{ y_fill: false,
|
|
||||||
y_align: St.Align.MIDDLE,
|
|
||||||
expand: true });
|
|
||||||
userEntry.add(textLayout, { expand: true });
|
|
||||||
userList.add(userEntry, { x_fill: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
let cancelButton = { label: _("Cancel"),
|
|
||||||
action: function() { dialog.close(); },
|
|
||||||
key: Clutter.Escape };
|
|
||||||
|
|
||||||
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
|
|
||||||
dialog.close();
|
|
||||||
this._session.ShutdownRemote();
|
|
||||||
}), default: true };
|
|
||||||
|
|
||||||
dialog.setButtons([cancelButton, powerOffButton]);
|
|
||||||
|
|
||||||
dialog.open();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onSuspendOrPowerOffActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
|
|
||||||
if (this._haveShutdown &&
|
|
||||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
|
||||||
this._loginManager.listSessions(Lang.bind(this,
|
|
||||||
function(result) {
|
|
||||||
let sessions = [];
|
|
||||||
let n = 0;
|
|
||||||
for (let i = 0; i < result.length; i++) {
|
|
||||||
let[id, uid, userName, seat, sessionPath] = result[i];
|
|
||||||
let proxy = new SystemdLoginSession(Gio.DBus.system,
|
|
||||||
'org.freedesktop.login1',
|
|
||||||
sessionPath);
|
|
||||||
|
|
||||||
if (proxy.Class != 'user')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (proxy.State == 'closing')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sessions.push({ user: this._userManager.get_user(userName),
|
|
||||||
username: userName,
|
|
||||||
info: { type: proxy.Type,
|
|
||||||
remote: proxy.Remote }
|
|
||||||
});
|
|
||||||
|
|
||||||
// limit the number of entries
|
|
||||||
n++;
|
|
||||||
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n != 0)
|
|
||||||
this._openSessionWarnDialog(sessions);
|
|
||||||
else
|
|
||||||
this._session.ShutdownRemote();
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
|
||||||
this._loginManager.suspend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
@ -3,10 +3,56 @@
|
|||||||
//
|
//
|
||||||
// A widget showing the user avatar and name
|
// A widget showing the user avatar and name
|
||||||
const AccountsService = imports.gi.AccountsService;
|
const AccountsService = imports.gi.AccountsService;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const UserMenu = imports.ui.userMenu;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const AVATAR_ICON_SIZE = 64;
|
||||||
|
|
||||||
|
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||||
|
//
|
||||||
|
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||||
|
// Copyright (C) 2008,2009 Red Hat, Inc.
|
||||||
|
|
||||||
|
const Avatar = new Lang.Class({
|
||||||
|
Name: 'Avatar',
|
||||||
|
|
||||||
|
_init: function(user, params) {
|
||||||
|
this._user = user;
|
||||||
|
params = Params.parse(params, { reactive: false,
|
||||||
|
iconSize: AVATAR_ICON_SIZE,
|
||||||
|
styleClass: 'framed-user-icon' });
|
||||||
|
this._iconSize = params.iconSize;
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ style_class: params.styleClass,
|
||||||
|
track_hover: params.reactive,
|
||||||
|
reactive: params.reactive });
|
||||||
|
},
|
||||||
|
|
||||||
|
setSensitive: function(sensitive) {
|
||||||
|
this.actor.can_focus = sensitive;
|
||||||
|
this.actor.reactive = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function() {
|
||||||
|
let iconFile = this._user.get_icon_file();
|
||||||
|
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||||
|
iconFile = null;
|
||||||
|
|
||||||
|
if (iconFile) {
|
||||||
|
let file = Gio.File.new_for_path(iconFile);
|
||||||
|
this.actor.child = null;
|
||||||
|
this.actor.style = 'background-image: url("%s");'.format(iconFile);
|
||||||
|
} else {
|
||||||
|
this.actor.style = null;
|
||||||
|
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
|
||||||
|
icon_size: this._iconSize });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const UserWidget = new Lang.Class({
|
const UserWidget = new Lang.Class({
|
||||||
Name: 'UserWidget',
|
Name: 'UserWidget',
|
||||||
@ -16,8 +62,9 @@ const UserWidget = new Lang.Class({
|
|||||||
|
|
||||||
this.actor = new St.BoxLayout({ style_class: 'user-widget',
|
this.actor = new St.BoxLayout({ style_class: 'user-widget',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
this._avatar = new UserMenu.UserAvatarWidget(user);
|
this._avatar = new Avatar(user);
|
||||||
this.actor.add(this._avatar.actor,
|
this.actor.add(this._avatar.actor,
|
||||||
{ x_fill: true, y_fill: true });
|
{ x_fill: true, y_fill: true });
|
||||||
|
|
||||||
@ -36,7 +83,7 @@ const UserWidget = new Lang.Class({
|
|||||||
this._updateUser();
|
this._updateUser();
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
_onDestroy: function() {
|
||||||
if (this._userLoadedId != 0) {
|
if (this._userLoadedId != 0) {
|
||||||
this._user.disconnect(this._userLoadedId);
|
this._user.disconnect(this._userLoadedId);
|
||||||
this._userLoadedId = 0;
|
this._userLoadedId = 0;
|
||||||
@ -46,8 +93,6 @@ const UserWidget = new Lang.Class({
|
|||||||
this._user.disconnect(this._userChangedId);
|
this._user.disconnect(this._userChangedId);
|
||||||
this._userChangedId = 0;
|
this._userChangedId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.actor.destroy();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateUser: function() {
|
_updateUser: function() {
|
||||||
|
@ -150,6 +150,14 @@ const ViewSelector = new Lang.Class({
|
|||||||
Shell.KeyBindingMode.NORMAL |
|
Shell.KeyBindingMode.NORMAL |
|
||||||
Shell.KeyBindingMode.OVERVIEW,
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
Lang.bind(this, this._toggleAppsPage));
|
Lang.bind(this, this._toggleAppsPage));
|
||||||
|
|
||||||
|
Main.wm.addKeybinding('toggle-overview',
|
||||||
|
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||||
|
Meta.KeyBindingFlags.NONE,
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(Main.overview, Main.overview.toggle));
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_toggleAppsPage: function() {
|
_toggleAppsPage: function() {
|
||||||
@ -178,6 +186,10 @@ const ViewSelector = new Lang.Class({
|
|||||||
Main.overview.fadeInDesktop();
|
Main.overview.fadeInDesktop();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setWorkspacesFullGeometry: function(geom) {
|
||||||
|
this._workspacesDisplay.setWorkspacesFullGeometry(geom);
|
||||||
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
this._workspacesDisplay.hide();
|
this._workspacesDisplay.hide();
|
||||||
},
|
},
|
||||||
@ -496,12 +508,12 @@ const ViewSelector = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._searchSystem.registerProvider(provider);
|
this._searchSystem.registerProvider(provider);
|
||||||
this._searchResults.createProviderMeta(provider);
|
this._searchResults.createProviderDisplay(provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeSearchProvider: function(provider) {
|
removeSearchProvider: function(provider) {
|
||||||
this._searchSystem.unregisterProvider(provider);
|
this._searchSystem.unregisterProvider(provider);
|
||||||
this._searchResults.destroyProviderMeta(provider);
|
this._searchResults.destroyProviderDisplay(provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
getActivePage: function() {
|
getActivePage: function() {
|
||||||
|
@ -134,9 +134,9 @@ const WandaSearchProvider = new Lang.Class({
|
|||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
if (terms.join(' ') == MAGIC_FISH_KEY) {
|
if (terms.join(' ') == MAGIC_FISH_KEY) {
|
||||||
this.searchSystem.pushResults(this, [ FISH_NAME ]);
|
this.searchSystem.setResults(this, [ FISH_NAME ]);
|
||||||
} else {
|
} else {
|
||||||
this.searchSystem.pushResults(this, []);
|
this.searchSystem.setResults(this, []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ 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 Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
@ -66,6 +67,209 @@ function getWindowDimmer(actor) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the last window closed on a workspace is a dialog or splash
|
||||||
|
* screen, we assume that it might be an initial window shown before
|
||||||
|
* the main window of an application, and give the app a grace period
|
||||||
|
* where it can map another window before we remove the workspace.
|
||||||
|
*/
|
||||||
|
const LAST_WINDOW_GRACE_TIME = 1000;
|
||||||
|
|
||||||
|
const WorkspaceTracker = new Lang.Class({
|
||||||
|
Name: 'WorkspaceTracker',
|
||||||
|
|
||||||
|
_init: function(wm) {
|
||||||
|
this._wm = wm;
|
||||||
|
|
||||||
|
this._workspaces = [];
|
||||||
|
this._checkWorkspacesId = 0;
|
||||||
|
|
||||||
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces));
|
||||||
|
|
||||||
|
global.screen.connect('notify::n-workspaces', Lang.bind(this, this._nWorkspacesChanged));
|
||||||
|
|
||||||
|
global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor));
|
||||||
|
global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor));
|
||||||
|
global.screen.connect('restacked', Lang.bind(this, this._windowsRestacked));
|
||||||
|
|
||||||
|
this._workspaceSettings = new Gio.Settings({ schema: Main.dynamicWorkspacesSchema });
|
||||||
|
this._workspaceSettings.connect('changed::dynamic-workspaces', Lang.bind(this, this._queueCheckWorkspaces));
|
||||||
|
|
||||||
|
this._nWorkspacesChanged();
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkWorkspaces: function() {
|
||||||
|
let i;
|
||||||
|
let emptyWorkspaces = [];
|
||||||
|
|
||||||
|
if (!Meta.prefs_get_dynamic_workspaces()) {
|
||||||
|
this._checkWorkspacesId = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < this._workspaces.length; i++) {
|
||||||
|
let lastRemoved = this._workspaces[i]._lastRemovedWindow;
|
||||||
|
if ((lastRemoved &&
|
||||||
|
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
||||||
|
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
||||||
|
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
||||||
|
this._workspaces[i]._keepAliveId)
|
||||||
|
emptyWorkspaces[i] = false;
|
||||||
|
else
|
||||||
|
emptyWorkspaces[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
||||||
|
for (i = 0; i < sequences.length; i++) {
|
||||||
|
let index = sequences[i].get_workspace();
|
||||||
|
if (index >= 0 && index <= global.screen.n_workspaces)
|
||||||
|
emptyWorkspaces[index] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let windows = global.get_window_actors();
|
||||||
|
for (i = 0; i < windows.length; i++) {
|
||||||
|
let win = windows[i];
|
||||||
|
|
||||||
|
if (win.get_meta_window().is_on_all_workspaces())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let workspaceIndex = win.get_workspace();
|
||||||
|
emptyWorkspaces[workspaceIndex] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have an empty workspace at the end, add one
|
||||||
|
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
||||||
|
global.screen.append_new_workspace(false, global.get_current_time());
|
||||||
|
emptyWorkspaces.push(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
|
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
|
||||||
|
activeWorkspaceIndex < emptyWorkspaces.length - 1);
|
||||||
|
// Don't enter the overview when removing multiple empty workspaces at startup
|
||||||
|
let showOverview = (removingCurrentWorkspace &&
|
||||||
|
!emptyWorkspaces.every(function(x) { return x; }));
|
||||||
|
|
||||||
|
if (removingCurrentWorkspace) {
|
||||||
|
// "Merge" the empty workspace we are removing with the one at the end
|
||||||
|
this._wm.blockAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete other empty workspaces; do it from the end to avoid index changes
|
||||||
|
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
||||||
|
if (emptyWorkspaces[i])
|
||||||
|
global.screen.remove_workspace(this._workspaces[i], global.get_current_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removingCurrentWorkspace) {
|
||||||
|
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
||||||
|
this._wm.unblockAnimations();
|
||||||
|
|
||||||
|
if (!Main.overview.visible && showOverview)
|
||||||
|
Main.overview.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._checkWorkspacesId = 0;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
keepWorkspaceAlive: function(workspace, duration) {
|
||||||
|
if (workspace._keepAliveId)
|
||||||
|
Mainloop.source_remove(workspace._keepAliveId);
|
||||||
|
|
||||||
|
workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() {
|
||||||
|
workspace._keepAliveId = 0;
|
||||||
|
this._queueCheckWorkspaces();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowRemoved: function(workspace, window) {
|
||||||
|
workspace._lastRemovedWindow = window;
|
||||||
|
this._queueCheckWorkspaces();
|
||||||
|
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, Lang.bind(this, function() {
|
||||||
|
if (workspace._lastRemovedWindow == window) {
|
||||||
|
workspace._lastRemovedWindow = null;
|
||||||
|
this._queueCheckWorkspaces();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowLeftMonitor: function(metaScreen, monitorIndex, metaWin) {
|
||||||
|
// If the window left the primary monitor, that
|
||||||
|
// might make that workspace empty
|
||||||
|
if (monitorIndex == Main.layoutManager.primaryIndex)
|
||||||
|
this._queueCheckWorkspaces();
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowEnteredMonitor: function(metaScreen, monitorIndex, metaWin) {
|
||||||
|
// If the window entered the primary monitor, that
|
||||||
|
// might make that workspace non-empty
|
||||||
|
if (monitorIndex == Main.layoutManager.primaryIndex)
|
||||||
|
this._queueCheckWorkspaces();
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowsRestacked: function() {
|
||||||
|
// Figure out where the pointer is in case we lost track of
|
||||||
|
// it during a grab. (In particular, if a trayicon popup menu
|
||||||
|
// is dismissed, see if we need to close the message tray.)
|
||||||
|
global.sync_pointer();
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueCheckWorkspaces: function() {
|
||||||
|
if (this._checkWorkspacesId == 0)
|
||||||
|
this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this._checkWorkspaces));
|
||||||
|
},
|
||||||
|
|
||||||
|
_nWorkspacesChanged: function() {
|
||||||
|
let oldNumWorkspaces = this._workspaces.length;
|
||||||
|
let newNumWorkspaces = global.screen.n_workspaces;
|
||||||
|
|
||||||
|
if (oldNumWorkspaces == newNumWorkspaces)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let lostWorkspaces = [];
|
||||||
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||||
|
let w;
|
||||||
|
|
||||||
|
// Assume workspaces are only added at the end
|
||||||
|
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
||||||
|
this._workspaces[w] = global.screen.get_workspace_by_index(w);
|
||||||
|
|
||||||
|
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||||
|
let workspace = this._workspaces[w];
|
||||||
|
workspace._windowAddedId = workspace.connect('window-added', Lang.bind(this, this._queueCheckWorkspaces));
|
||||||
|
workspace._windowRemovedId = workspace.connect('window-removed', Lang.bind(this, this._windowRemoved));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Assume workspaces are only removed sequentially
|
||||||
|
// (e.g. 2,3,4 - not 2,4,7)
|
||||||
|
let removedIndex;
|
||||||
|
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
||||||
|
for (let w = 0; w < oldNumWorkspaces; w++) {
|
||||||
|
let workspace = global.screen.get_workspace_by_index(w);
|
||||||
|
if (this._workspaces[w] != workspace) {
|
||||||
|
removedIndex = w;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lostWorkspaces = this._workspaces.splice(removedIndex, removedNum);
|
||||||
|
lostWorkspaces.forEach(function(workspace) {
|
||||||
|
workspace.disconnect(workspace._windowAddedId);
|
||||||
|
workspace.disconnect(workspace._windowRemovedId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._queueCheckWorkspaces();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const WindowManager = new Lang.Class({
|
const WindowManager = new Lang.Class({
|
||||||
Name: 'WindowManager',
|
Name: 'WindowManager',
|
||||||
|
|
||||||
@ -136,6 +340,90 @@ const WindowManager = new Lang.Class({
|
|||||||
Shell.KeyBindingMode.NORMAL |
|
Shell.KeyBindingMode.NORMAL |
|
||||||
Shell.KeyBindingMode.OVERVIEW,
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-1',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-2',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-3',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-4',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-5',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-6',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-7',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-8',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-9',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-10',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-11',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('switch-to-workspace-12',
|
||||||
|
Shell.KeyBindingMode.NORMAL |
|
||||||
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-1',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-2',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-3',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-4',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-5',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-6',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-7',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-8',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-9',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-10',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-11',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
|
this.setCustomKeybindingHandler('move-to-workspace-12',
|
||||||
|
Shell.KeyBindingMode.NORMAL,
|
||||||
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
this.setCustomKeybindingHandler('switch-applications',
|
this.setCustomKeybindingHandler('switch-applications',
|
||||||
Shell.KeyBindingMode.NORMAL,
|
Shell.KeyBindingMode.NORMAL,
|
||||||
Lang.bind(this, this._startAppSwitcher));
|
Lang.bind(this, this._startAppSwitcher));
|
||||||
@ -172,8 +460,9 @@ const WindowManager = new Lang.Class({
|
|||||||
this.addKeybinding('open-application-menu',
|
this.addKeybinding('open-application-menu',
|
||||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||||
Meta.KeyBindingFlags.NONE,
|
Meta.KeyBindingFlags.NONE,
|
||||||
Shell.KeyBindingMode.NORMAL,
|
Shell.KeyBindingMode.NORMAL |
|
||||||
Lang.bind(this, this._openAppMenu));
|
Shell.KeyBindingMode.TOPBAR_POPUP,
|
||||||
|
Lang.bind(this, this._toggleAppMenu));
|
||||||
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||||
@ -183,6 +472,19 @@ const WindowManager = new Lang.Class({
|
|||||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||||
this._dimWindow(this._dimmedWindows[i]);
|
this._dimWindow(this._dimmedWindows[i]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (Main.sessionMode.hasWorkspaces)
|
||||||
|
this._workspaceTracker = new WorkspaceTracker(this);
|
||||||
|
|
||||||
|
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||||
|
false, -1, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
keepWorkspaceAlive: function(workspace, duration) {
|
||||||
|
if (!this._workspaceTracker)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
|
||||||
},
|
},
|
||||||
|
|
||||||
setCustomKeybindingHandler: function(name, modes, handler) {
|
setCustomKeybindingHandler: function(name, modes, handler) {
|
||||||
@ -531,7 +833,7 @@ const WindowManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_switchWorkspace : function(shellwm, from, to, direction) {
|
_switchWorkspace : function(shellwm, from, to, direction) {
|
||||||
if (!this._shouldAnimate()) {
|
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
|
||||||
shellwm.completed_switch_workspace();
|
shellwm.completed_switch_workspace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -679,27 +981,42 @@ const WindowManager = new Lang.Class({
|
|||||||
Main.ctrlAltTabManager.popup(backwards, binding.get_name(), binding.get_mask());
|
Main.ctrlAltTabManager.popup(backwards, binding.get_name(), binding.get_mask());
|
||||||
},
|
},
|
||||||
|
|
||||||
_openAppMenu : function(display, screen, window, event, binding) {
|
_toggleAppMenu : function(display, screen, window, event, binding) {
|
||||||
Main.panel.openAppMenu();
|
Main.panel.toggleAppMenu();
|
||||||
},
|
},
|
||||||
|
|
||||||
_showWorkspaceSwitcher : function(display, screen, window, binding) {
|
_showWorkspaceSwitcher : function(display, screen, window, binding) {
|
||||||
|
if (!Main.sessionMode.hasWorkspaces)
|
||||||
|
return;
|
||||||
|
|
||||||
if (screen.n_workspaces == 1)
|
if (screen.n_workspaces == 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let [action,,,direction] = binding.get_name().split('-');
|
let [action,,,target] = binding.get_name().split('-');
|
||||||
let direction = Meta.MotionDirection[direction.toUpperCase()];
|
|
||||||
let newWs;
|
let newWs;
|
||||||
|
let direction;
|
||||||
|
|
||||||
|
if (isNaN(target)) {
|
||||||
|
direction = Meta.MotionDirection[target.toUpperCase()];
|
||||||
|
newWs = screen.get_active_workspace().get_neighbor(direction);
|
||||||
|
} else if (target > 0) {
|
||||||
|
target--;
|
||||||
|
newWs = screen.get_workspace_by_index(target);
|
||||||
|
|
||||||
|
if (screen.get_active_workspace().index() > target)
|
||||||
|
direction = Meta.MotionDirection.UP;
|
||||||
|
else
|
||||||
|
direction = Meta.MotionDirection.DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
if (direction != Meta.MotionDirection.UP &&
|
if (direction != Meta.MotionDirection.UP &&
|
||||||
direction != Meta.MotionDirection.DOWN)
|
direction != Meta.MotionDirection.DOWN)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (action == 'switch')
|
if (action == 'switch')
|
||||||
newWs = this.actionMoveWorkspace(direction);
|
this.actionMoveWorkspace(newWs);
|
||||||
else
|
else
|
||||||
newWs = this.actionMoveWindow(window, direction);
|
this.actionMoveWindow(window, newWs);
|
||||||
|
|
||||||
if (!Main.overview.visible) {
|
if (!Main.overview.visible) {
|
||||||
if (this._workspaceSwitcherPopup == null) {
|
if (this._workspaceSwitcherPopup == null) {
|
||||||
@ -712,31 +1029,31 @@ const WindowManager = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actionMoveWorkspace: function(direction) {
|
actionMoveWorkspace: function(workspace) {
|
||||||
|
if (!Main.sessionMode.hasWorkspaces)
|
||||||
|
return;
|
||||||
|
|
||||||
let activeWorkspace = global.screen.get_active_workspace();
|
let activeWorkspace = global.screen.get_active_workspace();
|
||||||
let toActivate = activeWorkspace.get_neighbor(direction);
|
|
||||||
|
|
||||||
if (activeWorkspace != toActivate)
|
if (activeWorkspace != workspace)
|
||||||
toActivate.activate(global.get_current_time());
|
workspace.activate(global.get_current_time());
|
||||||
|
|
||||||
return toActivate;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actionMoveWindow: function(window, direction) {
|
actionMoveWindow: function(window, workspace) {
|
||||||
let activeWorkspace = global.screen.get_active_workspace();
|
if (!Main.sessionMode.hasWorkspaces)
|
||||||
let toActivate = activeWorkspace.get_neighbor(direction);
|
return;
|
||||||
|
|
||||||
if (activeWorkspace != toActivate) {
|
let activeWorkspace = global.screen.get_active_workspace();
|
||||||
|
|
||||||
|
if (activeWorkspace != workspace) {
|
||||||
// This won't have any effect for "always sticky" windows
|
// This won't have any effect for "always sticky" windows
|
||||||
// (like desktop windows or docks)
|
// (like desktop windows or docks)
|
||||||
|
|
||||||
this._movingWindow = window;
|
this._movingWindow = window;
|
||||||
window.change_workspace(toActivate);
|
window.change_workspace(workspace);
|
||||||
|
|
||||||
global.display.clear_mouse_mode();
|
global.display.clear_mouse_mode();
|
||||||
toActivate.activate_with_focus (window, global.get_current_time());
|
workspace.activate_with_focus (window, global.get_current_time());
|
||||||
}
|
}
|
||||||
|
|
||||||
return toActivate;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -127,7 +127,7 @@ const WindowClone = new Lang.Class({
|
|||||||
if (this._stackAbove == null)
|
if (this._stackAbove == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (this.inDrag || this._zooming) {
|
if (this.inDrag) {
|
||||||
if (this._stackAbove._delegate)
|
if (this._stackAbove._delegate)
|
||||||
return this._stackAbove._delegate.getActualStackAbove();
|
return this._stackAbove._delegate.getActualStackAbove();
|
||||||
else
|
else
|
||||||
@ -453,6 +453,10 @@ const WindowOverlay = new Lang.Class({
|
|||||||
metaWindow.delete(global.get_current_time());
|
metaWindow.delete(global.get_current_time());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_windowCanClose: function() {
|
||||||
|
return this._windowClone.metaWindow.can_close();
|
||||||
|
},
|
||||||
|
|
||||||
_onWindowAdded: function(workspace, win) {
|
_onWindowAdded: function(workspace, win) {
|
||||||
let metaWindow = this._windowClone.metaWindow;
|
let metaWindow = this._windowClone.metaWindow;
|
||||||
|
|
||||||
@ -488,12 +492,14 @@ const WindowOverlay = new Lang.Class({
|
|||||||
_animateVisible: function() {
|
_animateVisible: function() {
|
||||||
this._parentActor.raise_top();
|
this._parentActor.raise_top();
|
||||||
|
|
||||||
this.closeButton.show();
|
if (this._windowCanClose()) {
|
||||||
this.closeButton.opacity = 0;
|
this.closeButton.show();
|
||||||
Tweener.addTween(this.closeButton,
|
this.closeButton.opacity = 0;
|
||||||
{ opacity: 255,
|
Tweener.addTween(this.closeButton,
|
||||||
time: CLOSE_BUTTON_FADE_TIME,
|
{ opacity: 255,
|
||||||
transition: 'easeOutQuad' });
|
time: CLOSE_BUTTON_FADE_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
}
|
||||||
|
|
||||||
this.border.show();
|
this.border.show();
|
||||||
this.border.opacity = 0;
|
this.border.opacity = 0;
|
||||||
@ -753,13 +759,6 @@ const LayoutStrategy = new Lang.Class({
|
|||||||
layout.space = space;
|
layout.space = space;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getDistance: function (row, actor) {
|
|
||||||
let dist_x = actor.x - row.x;
|
|
||||||
let dist_y = actor.y - row.y;
|
|
||||||
|
|
||||||
return Math.sqrt(Math.pow(dist_x, 2) + Math.pow(dist_y, 2));
|
|
||||||
},
|
|
||||||
|
|
||||||
computeWindowSlots: function(layout, area) {
|
computeWindowSlots: function(layout, area) {
|
||||||
this._computeRowSizes(layout);
|
this._computeRowSizes(layout);
|
||||||
|
|
||||||
@ -767,28 +766,36 @@ const LayoutStrategy = new Lang.Class({
|
|||||||
|
|
||||||
let slots = [];
|
let slots = [];
|
||||||
|
|
||||||
let y = 0;
|
// Do this in three parts.
|
||||||
|
let height = 0;
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
let row = rows[i];
|
let row = rows[i];
|
||||||
row.x = area.x + (area.width - row.width) / 2;
|
height += row.height + this._rowSpacing;
|
||||||
row.y = area.y + y;
|
|
||||||
y += row.height + this._rowSpacing;
|
|
||||||
row.windows.sort(Lang.bind(this, function(a, b) {
|
|
||||||
return this._getDistance(row, a.realWindow) - this._getDistance(row, b.realWindow);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let height = y - this._rowSpacing;
|
height -= this._rowSpacing;
|
||||||
let baseY = (area.height - height) / 2;
|
|
||||||
|
let y = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
let row = rows[i];
|
||||||
|
|
||||||
|
// If this window layout row doesn't fit in the actual
|
||||||
|
// geometry, then apply an additional scale to it.
|
||||||
|
row.additionalScale = Math.min(1, area.width / row.width, area.height / height);
|
||||||
|
|
||||||
|
row.x = area.x + (Math.max(area.width - row.width, 0) / 2) * row.additionalScale;
|
||||||
|
row.y = area.y + (y + Math.max(area.height - height, 0) / 2) * row.additionalScale;
|
||||||
|
y += row.height + this._rowSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
let row = rows[i];
|
let row = rows[i];
|
||||||
row.y += baseY;
|
|
||||||
let x = row.x;
|
let x = row.x;
|
||||||
for (let j = 0; j < row.windows.length; j++) {
|
for (let j = 0; j < row.windows.length; j++) {
|
||||||
let window = row.windows[j];
|
let window = row.windows[j];
|
||||||
|
|
||||||
let s = scale * this._computeWindowScale(window);
|
let s = scale * this._computeWindowScale(window) * row.additionalScale;
|
||||||
let cellWidth = window.actor.width * s;
|
let cellWidth = window.actor.width * s;
|
||||||
let cellHeight = window.actor.height * s;
|
let cellHeight = window.actor.height * s;
|
||||||
|
|
||||||
@ -832,6 +839,13 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_sortRow: function(row) {
|
||||||
|
// Sort windows horizontally to minimize travel distance
|
||||||
|
row.windows.sort(function(a, b) {
|
||||||
|
return a.realWindow.x - b.realWindow.x;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
computeLayout: function(windows, layout) {
|
computeLayout: function(windows, layout) {
|
||||||
let numRows = layout.numRows;
|
let numRows = layout.numRows;
|
||||||
|
|
||||||
@ -862,6 +876,7 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
|||||||
row.windows.push(window);
|
row.windows.push(window);
|
||||||
row.fullWidth += width;
|
row.fullWidth += width;
|
||||||
} else {
|
} else {
|
||||||
|
this._sortRow(row);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -883,6 +898,14 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function padArea(area, padding) {
|
||||||
|
return {
|
||||||
|
x: area.x + padding.left,
|
||||||
|
y: area.y + padding.top,
|
||||||
|
width: area.width - padding.left - padding.right,
|
||||||
|
height: area.height - padding.top - padding.bottom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @metaWorkspace: a #Meta.Workspace, or null
|
* @metaWorkspace: a #Meta.Workspace, or null
|
||||||
@ -894,10 +917,19 @@ const Workspace = new Lang.Class({
|
|||||||
// When dragging a window, we use this slot for reserve space.
|
// When dragging a window, we use this slot for reserve space.
|
||||||
this._reservedSlot = null;
|
this._reservedSlot = null;
|
||||||
this.metaWorkspace = metaWorkspace;
|
this.metaWorkspace = metaWorkspace;
|
||||||
this._x = 0;
|
|
||||||
this._y = 0;
|
// The full geometry is the geometry we should try and position
|
||||||
this._width = 0;
|
// windows for. The actual geometry we allocate may be less than
|
||||||
this._height = 0;
|
// this, like if the workspace switcher is slid out.
|
||||||
|
this._fullGeometry = null;
|
||||||
|
|
||||||
|
// The actual geometry is the geometry we need to arrange windows
|
||||||
|
// in. If this is a smaller area than the full geometry, we'll
|
||||||
|
// do some simple aspect ratio like math to fit the layout calculated
|
||||||
|
// for the full geometry into this area.
|
||||||
|
this._actualGeometry = null;
|
||||||
|
|
||||||
|
this._currentLayout = null;
|
||||||
|
|
||||||
this.monitorIndex = monitorIndex;
|
this.monitorIndex = monitorIndex;
|
||||||
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
|
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
|
||||||
@ -910,7 +942,7 @@ const Workspace = new Lang.Class({
|
|||||||
this.actor.add_style_class_name('external-monitor');
|
this.actor.add_style_class_name('external-monitor');
|
||||||
this.actor.set_size(0, 0);
|
this.actor.set_size(0, 0);
|
||||||
|
|
||||||
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
|
this._dropRect = new Clutter.Actor({ opacity: 0 });
|
||||||
this._dropRect._delegate = this;
|
this._dropRect._delegate = this;
|
||||||
|
|
||||||
this.actor.add_actor(this._dropRect);
|
this.actor.add_actor(this._dropRect);
|
||||||
@ -947,23 +979,29 @@ const Workspace = new Lang.Class({
|
|||||||
|
|
||||||
this._positionWindowsFlags = 0;
|
this._positionWindowsFlags = 0;
|
||||||
this._positionWindowsId = 0;
|
this._positionWindowsId = 0;
|
||||||
|
|
||||||
this._currentLayout = null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setGeometry: function(x, y, width, height) {
|
setFullGeometry: function(geom) {
|
||||||
this._x = x;
|
this._fullGeometry = geom;
|
||||||
this._y = y;
|
this._recalculateWindowPositions(WindowPositionFlags.NONE);
|
||||||
this._width = width;
|
},
|
||||||
this._height = height;
|
|
||||||
|
|
||||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
setActualGeometry: function(geom) {
|
||||||
this._dropRect.set_position(x, y);
|
this._actualGeometry = geom;
|
||||||
this._dropRect.set_size(width, height);
|
|
||||||
|
if (this._actualGeometryLater)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._actualGeometryLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||||
|
let geom = this._actualGeometry;
|
||||||
|
|
||||||
|
this._dropRect.set_position(geom.x, geom.y);
|
||||||
|
this._dropRect.set_size(geom.width, geom.height);
|
||||||
|
this._updateWindowPositions(Main.overview.animationInProgress ? WindowPositionFlags.ANIMATE : WindowPositionFlags.NONE);
|
||||||
|
|
||||||
|
this._actualGeometryLater = 0;
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.positionWindows(WindowPositionFlags.NONE);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_lookupIndex: function (metaWindow) {
|
_lookupIndex: function (metaWindow) {
|
||||||
@ -991,37 +1029,32 @@ const Workspace = new Lang.Class({
|
|||||||
clone = null;
|
clone = null;
|
||||||
|
|
||||||
this._reservedSlot = clone;
|
this._reservedSlot = clone;
|
||||||
this._currentLayout = null;
|
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
|
||||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
_recalculateWindowPositions: function(flags) {
|
||||||
* positionWindows:
|
|
||||||
* @flags:
|
|
||||||
* INITIAL - this is the initial positioning of the windows.
|
|
||||||
* ANIMATE - Indicates that we need animate changing position.
|
|
||||||
*/
|
|
||||||
positionWindows: function(flags) {
|
|
||||||
this._positionWindowsFlags |= flags;
|
this._positionWindowsFlags |= flags;
|
||||||
|
|
||||||
if (this._positionWindowsId > 0)
|
if (this._positionWindowsId > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._positionWindowsId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
this._positionWindowsId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||||
this._realPositionWindows(this._positionWindowsFlags);
|
this._realRecalculateWindowPositions(this._positionWindowsFlags);
|
||||||
this._positionWindowsFlags = 0;
|
this._positionWindowsFlags = 0;
|
||||||
this._positionWindowsId = 0;
|
this._positionWindowsId = 0;
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_realPositionWindows : function(flags) {
|
_realRecalculateWindowPositions: function(flags) {
|
||||||
if (this._repositionWindowsId > 0) {
|
if (this._repositionWindowsId > 0) {
|
||||||
Mainloop.source_remove(this._repositionWindowsId);
|
Mainloop.source_remove(this._repositionWindowsId);
|
||||||
this._repositionWindowsId = 0;
|
this._repositionWindowsId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let clones = this._windows.slice();
|
let clones = this._windows.slice();
|
||||||
|
if (clones.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
clones.sort(function(a, b) {
|
clones.sort(function(a, b) {
|
||||||
return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence();
|
return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence();
|
||||||
@ -1030,11 +1063,25 @@ const Workspace = new Lang.Class({
|
|||||||
if (this._reservedSlot)
|
if (this._reservedSlot)
|
||||||
clones.push(this._reservedSlot);
|
clones.push(this._reservedSlot);
|
||||||
|
|
||||||
|
this._currentLayout = this._computeLayout(clones);
|
||||||
|
this._updateWindowPositions(flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateWindowPositions: function(flags) {
|
||||||
|
if (this._currentLayout == null) {
|
||||||
|
this._recalculateWindowPositions(flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let initialPositioning = flags & WindowPositionFlags.INITIAL;
|
let initialPositioning = flags & WindowPositionFlags.INITIAL;
|
||||||
let animate = flags & WindowPositionFlags.ANIMATE;
|
let animate = flags & WindowPositionFlags.ANIMATE;
|
||||||
|
|
||||||
// Start the animations
|
let layout = this._currentLayout;
|
||||||
let slots = this._computeAllWindowSlots(clones);
|
let strategy = layout.strategy;
|
||||||
|
|
||||||
|
let [, , padding] = this._getSpacingAndPadding();
|
||||||
|
let area = padArea(this._actualGeometry, padding);
|
||||||
|
let slots = strategy.computeWindowSlots(layout, area);
|
||||||
|
|
||||||
let currentWorkspace = global.screen.get_active_workspace();
|
let currentWorkspace = global.screen.get_active_workspace();
|
||||||
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
|
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
|
||||||
@ -1148,8 +1195,8 @@ const Workspace = new Lang.Class({
|
|||||||
let [x, y, mask] = global.get_pointer();
|
let [x, y, mask] = global.get_pointer();
|
||||||
|
|
||||||
let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
|
let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
|
||||||
let inWorkspace = (this._x < x && x < this._x + this._width &&
|
let inWorkspace = (this._fullGeometry.x < x && x < this._fullGeometry.x + this._fullGeometry.width &&
|
||||||
this._y < y && y < this._y + this._height);
|
this._fullGeometry.y < y && y < this._fullGeometry.y + this._fullGeometry.height);
|
||||||
|
|
||||||
if (pointerHasMoved && inWorkspace) {
|
if (pointerHasMoved && inWorkspace) {
|
||||||
// store current cursor position
|
// store current cursor position
|
||||||
@ -1164,7 +1211,7 @@ const Workspace = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1269,7 +1316,7 @@ const Workspace = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._currentLayout = null;
|
this._currentLayout = null;
|
||||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
|
||||||
},
|
},
|
||||||
|
|
||||||
_windowAdded : function(metaWorkspace, metaWin) {
|
_windowAdded : function(metaWorkspace, metaWin) {
|
||||||
@ -1306,13 +1353,8 @@ const Workspace = new Lang.Class({
|
|||||||
|
|
||||||
// Animate the full-screen to Overview transition.
|
// Animate the full-screen to Overview transition.
|
||||||
zoomToOverview : function() {
|
zoomToOverview : function() {
|
||||||
this._currentLayout = null;
|
|
||||||
|
|
||||||
// Position and scale the windows.
|
// Position and scale the windows.
|
||||||
if (Main.overview.animationInProgress)
|
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
|
||||||
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
|
|
||||||
else
|
|
||||||
this.positionWindows(WindowPositionFlags.INITIAL);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Animates the return from Overview mode
|
// Animates the return from Overview mode
|
||||||
@ -1436,7 +1478,7 @@ const Workspace = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
clone.connect('size-changed',
|
clone.connect('size-changed',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.positionWindows(0);
|
this._recalculateWindowPositions(WindowPositionFlags.NONE);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.actor.add_actor(clone.actor);
|
this.actor.add_actor(clone.actor);
|
||||||
@ -1485,12 +1527,14 @@ const Workspace = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeLayout: function(windows, area, rowSpacing, columnSpacing) {
|
_getBestLayout: function(windows, area, rowSpacing, columnSpacing) {
|
||||||
// We look for the largest scale that allows us to fit the
|
// We look for the largest scale that allows us to fit the
|
||||||
// largest row/tallest column on the workspace.
|
// largest row/tallest column on the workspace.
|
||||||
|
|
||||||
let lastLayout = {};
|
let lastLayout = {};
|
||||||
|
|
||||||
|
let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing);
|
||||||
|
|
||||||
for (let numRows = 1; ; numRows++) {
|
for (let numRows = 1; ; numRows++) {
|
||||||
let numColumns = Math.ceil(windows.length / numRows);
|
let numColumns = Math.ceil(windows.length / numRows);
|
||||||
|
|
||||||
@ -1500,8 +1544,6 @@ const Workspace = new Lang.Class({
|
|||||||
if (numColumns == lastLayout.numColumns)
|
if (numColumns == lastLayout.numColumns)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing);
|
|
||||||
|
|
||||||
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
|
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
|
||||||
strategy.computeLayout(windows, layout);
|
strategy.computeLayout(windows, layout);
|
||||||
strategy.computeScaleAndSpace(layout);
|
strategy.computeScaleAndSpace(layout);
|
||||||
@ -1515,18 +1557,7 @@ const Workspace = new Lang.Class({
|
|||||||
return lastLayout;
|
return lastLayout;
|
||||||
},
|
},
|
||||||
|
|
||||||
_rectEqual: function(one, two) {
|
_getSpacingAndPadding: function() {
|
||||||
if (one == two)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return (one.x == two.x &&
|
|
||||||
one.y == two.y &&
|
|
||||||
one.width == two.width &&
|
|
||||||
one.height == two.height);
|
|
||||||
},
|
|
||||||
|
|
||||||
_computeAllWindowSlots: function(windows) {
|
|
||||||
let totalWindows = windows.length;
|
|
||||||
let node = this.actor.get_theme_node();
|
let node = this.actor.get_theme_node();
|
||||||
|
|
||||||
// Window grid spacing
|
// Window grid spacing
|
||||||
@ -1539,21 +1570,14 @@ const Workspace = new Lang.Class({
|
|||||||
right: node.get_padding(St.Side.RIGHT),
|
right: node.get_padding(St.Side.RIGHT),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!totalWindows)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
let closeButtonHeight, captionHeight;
|
let closeButtonHeight, captionHeight;
|
||||||
let leftBorder, rightBorder;
|
let leftBorder, rightBorder;
|
||||||
if (this._windowOverlays.length) {
|
|
||||||
// All of the overlays have the same chrome sizes,
|
// All of the overlays have the same chrome sizes,
|
||||||
// so just pick the first one.
|
// so just pick the first one.
|
||||||
let overlay = this._windowOverlays[0];
|
let overlay = this._windowOverlays[0];
|
||||||
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
|
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
|
||||||
[leftBorder, rightBorder] = overlay.chromeWidths();
|
[leftBorder, rightBorder] = overlay.chromeWidths();
|
||||||
} else {
|
|
||||||
[closeButtonHeight, captionHeight] = [0, 0];
|
|
||||||
[leftBorder, rightBorder] = [0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
rowSpacing += captionHeight;
|
rowSpacing += captionHeight;
|
||||||
columnSpacing += (rightBorder + leftBorder) / 2;
|
columnSpacing += (rightBorder + leftBorder) / 2;
|
||||||
@ -1562,25 +1586,13 @@ const Workspace = new Lang.Class({
|
|||||||
padding.left += leftBorder;
|
padding.left += leftBorder;
|
||||||
padding.right += rightBorder;
|
padding.right += rightBorder;
|
||||||
|
|
||||||
let area = {
|
return [rowSpacing, columnSpacing, padding];
|
||||||
x: this._x + padding.left,
|
},
|
||||||
y: this._y + padding.top,
|
|
||||||
width: this._width - padding.left - padding.right,
|
|
||||||
height: this._height - padding.top - padding.bottom,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!this._currentLayout)
|
_computeLayout: function(windows) {
|
||||||
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
|
let [rowSpacing, columnSpacing, padding] = this._getSpacingAndPadding();
|
||||||
|
let area = padArea(this._fullGeometry, padding);
|
||||||
let layout = this._currentLayout;
|
return this._getBestLayout(windows, area, rowSpacing, columnSpacing);
|
||||||
let strategy = layout.strategy;
|
|
||||||
|
|
||||||
if (!this._rectEqual(area, layout.area)) {
|
|
||||||
layout.area = area;
|
|
||||||
strategy.computeScaleAndSpace(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return strategy.computeWindowSlots(layout, area);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCloneSelected : function (clone, time) {
|
_onCloneSelected : function (clone, time) {
|
||||||
|
@ -764,8 +764,8 @@ const ThumbnailsBox = new Lang.Class({
|
|||||||
// to open its first window within some time, as tracked by Shell.WindowTracker.
|
// to open its first window within some time, as tracked by Shell.WindowTracker.
|
||||||
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
|
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
|
||||||
// workspace while we wait for the startup sequence to load.
|
// workspace while we wait for the startup sequence to load.
|
||||||
Main.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
|
Main.wm.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
|
||||||
WORKSPACE_KEEP_ALIVE_TIME);
|
WORKSPACE_KEEP_ALIVE_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the animation on the workspace (which is actually
|
// Start the animation on the workspace (which is actually
|
||||||
|
@ -23,6 +23,18 @@ const MAX_WORKSPACES = 16;
|
|||||||
|
|
||||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
|
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
|
||||||
|
|
||||||
|
function rectEqual(one, two) {
|
||||||
|
if (one == two)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!one || !two)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (one.x == two.x &&
|
||||||
|
one.y == two.y &&
|
||||||
|
one.width == two.width &&
|
||||||
|
one.height == two.height);
|
||||||
|
}
|
||||||
|
|
||||||
const WorkspacesView = new Lang.Class({
|
const WorkspacesView = new Lang.Class({
|
||||||
Name: 'WorkspacesView',
|
Name: 'WorkspacesView',
|
||||||
@ -43,10 +55,9 @@ const WorkspacesView = new Lang.Class({
|
|||||||
this._updateWorkspaceActors(false);
|
this._updateWorkspaceActors(false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._width = 0;
|
this._fullGeometry = null;
|
||||||
this._height = 0;
|
this._actualGeometry = null;
|
||||||
this._x = 0;
|
|
||||||
this._y = 0;
|
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
this._animating = false; // tweening
|
this._animating = false; // tweening
|
||||||
this._scrolling = false; // swipe-scrolling
|
this._scrolling = false; // swipe-scrolling
|
||||||
@ -85,8 +96,8 @@ const WorkspacesView = new Lang.Class({
|
|||||||
this._overviewShownId =
|
this._overviewShownId =
|
||||||
Main.overview.connect('shown',
|
Main.overview.connect('shown',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.actor.set_clip(this._x, this._y,
|
this.actor.set_clip(this._fullGeometry.x, this._fullGeometry.y,
|
||||||
this._width, this._height);
|
this._fullGeometry.width, this._fullGeometry.height);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
||||||
@ -124,11 +135,9 @@ const WorkspacesView = new Lang.Class({
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
let ws = new Workspace.Workspace(null, i);
|
let ws = new Workspace.Workspace(null, i);
|
||||||
ws.setGeometry(monitors[i].x,
|
ws.setFullGeometry(monitors[i]);
|
||||||
monitors[i].y,
|
ws.setActualGeometry(monitors[i]);
|
||||||
monitors[i].width,
|
Main.layoutManager.overviewGroup.add_actor(ws.actor);
|
||||||
monitors[i].height);
|
|
||||||
global.overlay_group.add_actor(ws.actor);
|
|
||||||
this._extraWorkspaces.push(ws);
|
this._extraWorkspaces.push(ws);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -139,18 +148,24 @@ const WorkspacesView = new Lang.Class({
|
|||||||
this._extraWorkspaces = [];
|
this._extraWorkspaces = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
setGeometry: function(x, y, width, height) {
|
setFullGeometry: function(geom) {
|
||||||
if (this._x == x && this._y == y &&
|
if (rectEqual(this._fullGeometry, geom))
|
||||||
this._width == width && this._height == height)
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
this._width = width;
|
this._fullGeometry = geom;
|
||||||
this._height = height;
|
|
||||||
this._x = x;
|
|
||||||
this._y = y;
|
|
||||||
|
|
||||||
for (let i = 0; i < this._workspaces.length; i++)
|
for (let i = 0; i < this._workspaces.length; i++)
|
||||||
this._workspaces[i].setGeometry(x, y, width, height);
|
this._workspaces[i].setFullGeometry(geom);
|
||||||
|
},
|
||||||
|
|
||||||
|
setActualGeometry: function(geom) {
|
||||||
|
if (rectEqual(this._actualGeometry, geom))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._actualGeometry = geom;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._workspaces.length; i++)
|
||||||
|
this._workspaces[i].setActualGeometry(geom);
|
||||||
},
|
},
|
||||||
|
|
||||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||||
@ -210,7 +225,7 @@ const WorkspacesView = new Lang.Class({
|
|||||||
|
|
||||||
Tweener.removeTweens(workspace.actor);
|
Tweener.removeTweens(workspace.actor);
|
||||||
|
|
||||||
let y = (w - active) * (this._height + this._spacing);
|
let y = (w - active) * (this._fullGeometry.height + this._spacing);
|
||||||
|
|
||||||
if (showAnimation) {
|
if (showAnimation) {
|
||||||
let params = { y: y,
|
let params = { y: y,
|
||||||
@ -281,8 +296,9 @@ const WorkspacesView = new Lang.Class({
|
|||||||
|
|
||||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||||
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||||
this._workspaces[w].setGeometry(this._x, this._y,
|
this._workspaces[w].setFullGeometry(this._fullGeometry);
|
||||||
this._width, this._height);
|
if (this._actualGeometry)
|
||||||
|
this._workspaces[w].setActualGeometry(this._actualGeometry);
|
||||||
this.actor.add_actor(this._workspaces[w].actor);
|
this.actor.add_actor(this._workspaces[w].actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +446,7 @@ const WorkspacesDisplay = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new St.Widget({ clip_to_allocation: true });
|
this.actor = new St.Widget({ clip_to_allocation: true });
|
||||||
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
|
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry));
|
||||||
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
|
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
|
||||||
|
|
||||||
let clickAction = new Clutter.ClickAction()
|
let clickAction = new Clutter.ClickAction()
|
||||||
@ -484,6 +500,8 @@ const WorkspacesDisplay = new Lang.Class({
|
|||||||
|
|
||||||
this._notifyOpacityId = 0;
|
this._notifyOpacityId = 0;
|
||||||
this._scrollEventId = 0;
|
this._scrollEventId = 0;
|
||||||
|
|
||||||
|
this._fullGeometry = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onPan: function(action) {
|
_onPan: function(action) {
|
||||||
@ -572,10 +590,11 @@ const WorkspacesDisplay = new Lang.Class({
|
|||||||
this._workspacesViews.push(view);
|
this._workspacesViews.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateWorkspacesGeometry();
|
this._updateWorkspacesFullGeometry();
|
||||||
|
this._updateWorkspacesActualGeometry();
|
||||||
|
|
||||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||||
global.overlay_group.add_actor(this._workspacesViews[i].actor);
|
Main.layoutManager.overviewGroup.add_actor(this._workspacesViews[i].actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollValueChanged: function() {
|
_scrollValueChanged: function() {
|
||||||
@ -619,7 +638,7 @@ const WorkspacesDisplay = new Lang.Class({
|
|||||||
|
|
||||||
// This is kinda hackish - we want the primary view to
|
// This is kinda hackish - we want the primary view to
|
||||||
// appear as parent of this.actor, though in reality it
|
// appear as parent of this.actor, though in reality it
|
||||||
// is added directly to overlay_group
|
// is added directly to Main.layoutManager.overviewGroup
|
||||||
this._notifyOpacityId = newParent.connect('notify::opacity',
|
this._notifyOpacityId = newParent.connect('notify::opacity',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
let opacity = this.actor.get_parent().opacity;
|
let opacity = this.actor.get_parent().opacity;
|
||||||
@ -632,31 +651,48 @@ const WorkspacesDisplay = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateWorkspacesGeometry: function() {
|
// This geometry should always be the fullest geometry
|
||||||
|
// the workspaces switcher can ever be allocated, as if
|
||||||
|
// the sliding controls were never slid in at all.
|
||||||
|
setWorkspacesFullGeometry: function(geom) {
|
||||||
|
this._fullGeometry = geom;
|
||||||
|
this._updateWorkspacesFullGeometry();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateWorkspacesFullGeometry: function() {
|
||||||
if (!this._workspacesViews.length)
|
if (!this._workspacesViews.length)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
|
|
||||||
let fullHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
|
||||||
|
|
||||||
let width = fullWidth;
|
|
||||||
let height = fullHeight;
|
|
||||||
|
|
||||||
let [x, y] = this.actor.get_transformed_position();
|
|
||||||
|
|
||||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
|
||||||
|
|
||||||
let monitors = Main.layoutManager.monitors;
|
let monitors = Main.layoutManager.monitors;
|
||||||
let m = 0;
|
let m = 0;
|
||||||
for (let i = 0; i < monitors.length; i++) {
|
for (let i = 0; i < monitors.length; i++) {
|
||||||
if (i == this._primaryIndex) {
|
if (i == this._primaryIndex) {
|
||||||
this._workspacesViews[m].setGeometry(x, y, width, height);
|
this._workspacesViews[m].setFullGeometry(this._fullGeometry);
|
||||||
m++;
|
m++;
|
||||||
} else if (!this._workspacesOnlyOnPrimary) {
|
} else if (!this._workspacesOnlyOnPrimary) {
|
||||||
this._workspacesViews[m].setGeometry(monitors[i].x,
|
this._workspacesViews[m].setFullGeometry(monitors[i]);
|
||||||
monitors[i].y,
|
m++;
|
||||||
monitors[i].width,
|
}
|
||||||
monitors[i].height);
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateWorkspacesActualGeometry: function() {
|
||||||
|
if (!this._workspacesViews.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let [x, y] = this.actor.get_transformed_position();
|
||||||
|
let width = this.actor.allocation.x2 - this.actor.allocation.x1;
|
||||||
|
let height = this.actor.allocation.y2 - this.actor.allocation.y1;
|
||||||
|
let geometry = { x: x, y: y, width: width, height: height };
|
||||||
|
|
||||||
|
let monitors = Main.layoutManager.monitors;
|
||||||
|
let m = 0;
|
||||||
|
for (let i = 0; i < monitors.length; i++) {
|
||||||
|
if (i == this._primaryIndex) {
|
||||||
|
this._workspacesViews[m].setActualGeometry(geometry);
|
||||||
|
m++;
|
||||||
|
} else if (!this._workspacesOnlyOnPrimary) {
|
||||||
|
this._workspacesViews[m].setActualGeometry(monitors[i]);
|
||||||
m++;
|
m++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -724,15 +760,20 @@ const WorkspacesDisplay = new Lang.Class({
|
|||||||
_onScrollEvent: function(actor, event) {
|
_onScrollEvent: function(actor, event) {
|
||||||
if (!this.actor.mapped)
|
if (!this.actor.mapped)
|
||||||
return false;
|
return false;
|
||||||
|
let activeWs = global.screen.get_active_workspace();
|
||||||
|
let ws;
|
||||||
switch (event.get_scroll_direction()) {
|
switch (event.get_scroll_direction()) {
|
||||||
case Clutter.ScrollDirection.UP:
|
case Clutter.ScrollDirection.UP:
|
||||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
ws = activeWs.get_neighbor(Meta.MotionDirection.UP);
|
||||||
return true;
|
break;
|
||||||
case Clutter.ScrollDirection.DOWN:
|
case Clutter.ScrollDirection.DOWN:
|
||||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
|
||||||
return true;
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
Main.wm.actionMoveWorkspace(ws);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(WorkspacesDisplay.prototype);
|
Signals.addSignalMethods(WorkspacesDisplay.prototype);
|
||||||
|
@ -16,7 +16,7 @@ const XdndHandler = new Lang.Class({
|
|||||||
this._cursorWindowClone = null;
|
this._cursorWindowClone = null;
|
||||||
|
|
||||||
// Used as a drag actor in case we don't have a cursor window clone
|
// Used as a drag actor in case we don't have a cursor window clone
|
||||||
this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
|
this._dummy = new Clutter.Actor({ width: 1, height: 1, opacity: 0 });
|
||||||
Main.uiGroup.add_actor(this._dummy);
|
Main.uiGroup.add_actor(this._dummy);
|
||||||
Shell.util_set_hidden_from_pick(this._dummy, true);
|
Shell.util_set_hidden_from_pick(this._dummy, true);
|
||||||
this._dummy.hide();
|
this._dummy.hide();
|
||||||
|
@ -125,6 +125,12 @@
|
|||||||
<listitem><para>List possible modes and exit</para></listitem>
|
<listitem><para>List possible modes and exit</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--clutter-display=<replaceable>DISPLAY</replaceable></option></term>
|
||||||
|
|
||||||
|
<listitem><para>Clutter the option display (otherwise ignored)</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -28,6 +28,7 @@ gu
|
|||||||
he
|
he
|
||||||
hi
|
hi
|
||||||
hu
|
hu
|
||||||
|
ia
|
||||||
id
|
id
|
||||||
it
|
it
|
||||||
ja
|
ja
|
||||||
|
@ -7,6 +7,7 @@ data/gnome-shell.desktop.in.in
|
|||||||
data/gnome-shell-extension-prefs.desktop.in.in
|
data/gnome-shell-extension-prefs.desktop.in.in
|
||||||
data/org.gnome.shell.gschema.xml.in.in
|
data/org.gnome.shell.gschema.xml.in.in
|
||||||
js/extensionPrefs/main.js
|
js/extensionPrefs/main.js
|
||||||
|
js/gdm/authPrompt.js
|
||||||
js/gdm/loginDialog.js
|
js/gdm/loginDialog.js
|
||||||
js/gdm/powerMenu.js
|
js/gdm/powerMenu.js
|
||||||
js/gdm/util.js
|
js/gdm/util.js
|
||||||
@ -48,9 +49,9 @@ js/ui/status/keyboard.js
|
|||||||
js/ui/status/lockScreenMenu.js
|
js/ui/status/lockScreenMenu.js
|
||||||
js/ui/status/network.js
|
js/ui/status/network.js
|
||||||
js/ui/status/power.js
|
js/ui/status/power.js
|
||||||
|
js/ui/status/system.js
|
||||||
js/ui/status/volume.js
|
js/ui/status/volume.js
|
||||||
js/ui/unlockDialog.js
|
js/ui/unlockDialog.js
|
||||||
js/ui/userMenu.js
|
|
||||||
js/ui/viewSelector.js
|
js/ui/viewSelector.js
|
||||||
js/ui/wanda.js
|
js/ui/wanda.js
|
||||||
js/ui/windowAttentionHandler.js
|
js/ui/windowAttentionHandler.js
|
||||||
|
4
po/ca.po
4
po/ca.po
@ -11,7 +11,7 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"shell&keywords=I18N+L10N&component=general\n"
|
"shell&keywords=I18N+L10N&component=general\n"
|
||||||
"POT-Creation-Date: 2013-03-11 21:29+0000\n"
|
"POT-Creation-Date: 2013-03-11 21:29+0000\n"
|
||||||
"PO-Revision-Date: 2013-03-11 00:03+0100\n"
|
"PO-Revision-Date: 2013-05-26 08:21+0200\n"
|
||||||
"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
|
"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
|
||||||
"Language-Team: Catalan <tradgnome@softcatala.org>\n"
|
"Language-Team: Catalan <tradgnome@softcatala.org>\n"
|
||||||
"Language: ca\n"
|
"Language: ca\n"
|
||||||
@ -1048,7 +1048,7 @@ msgstr "Obre els rellotges"
|
|||||||
|
|
||||||
#: ../js/ui/dateMenu.js:105
|
#: ../js/ui/dateMenu.js:105
|
||||||
msgid "Date & Time Settings"
|
msgid "Date & Time Settings"
|
||||||
msgstr "Configuració de la data i l'hora"
|
msgstr "Configuració de la data i de l'hora"
|
||||||
|
|
||||||
#. 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").
|
||||||
|
239
po/el.po
239
po/el.po
@ -5,8 +5,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: gnome-shell.po.master\n"
|
"Project-Id-Version: gnome-shell.po.master\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"shell&keywords=I18N+L10N&component=general\n"
|
"shell&keywords=I18N+L10N&component=general\n"
|
||||||
"POT-Creation-Date: 2013-03-02 23:02+0000\n"
|
"POT-Creation-Date: 2013-04-19 02:48+0000\n"
|
||||||
"PO-Revision-Date: 2013-03-03 13:22+0300\n"
|
"PO-Revision-Date: 2013-04-25 08:11+0300\n"
|
||||||
"Last-Translator: Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>\n"
|
"Last-Translator: Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>\n"
|
||||||
"Language-Team: team@gnome.gr\n"
|
"Language-Team: team@gnome.gr\n"
|
||||||
"Language: el\n"
|
"Language: el\n"
|
||||||
@ -40,10 +40,14 @@ msgid "Focus the active notification"
|
|||||||
msgstr "Εστίαση στην ενεργή ειδοποίηση"
|
msgstr "Εστίαση στην ενεργή ειδοποίηση"
|
||||||
|
|
||||||
#: ../data/50-gnome-shell-system.xml.in.h:4
|
#: ../data/50-gnome-shell-system.xml.in.h:4
|
||||||
|
msgid "Show the overview"
|
||||||
|
msgstr "Εμφάνιση της επισκόπησης"
|
||||||
|
|
||||||
|
#: ../data/50-gnome-shell-system.xml.in.h:5
|
||||||
msgid "Show all applications"
|
msgid "Show all applications"
|
||||||
msgstr "Προβολή όλων των εφαρμογών"
|
msgstr "Προβολή όλων των εφαρμογών"
|
||||||
|
|
||||||
#: ../data/50-gnome-shell-system.xml.in.h:5
|
#: ../data/50-gnome-shell-system.xml.in.h:6
|
||||||
msgid "Open the application menu"
|
msgid "Open the application menu"
|
||||||
msgstr "Άνοιγμα του μενού εφαρμογών"
|
msgstr "Άνοιγμα του μενού εφαρμογών"
|
||||||
|
|
||||||
@ -223,45 +227,55 @@ msgstr ""
|
|||||||
"επισκόπησης ενεργειών."
|
"επισκόπησης ενεργειών."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
|
||||||
|
#| msgid "Keybinding to open the \"Show Applications\" view"
|
||||||
|
msgid "Keybinding to open the overview"
|
||||||
|
msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της επισκόπησης"
|
||||||
|
|
||||||
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
|
||||||
|
#| msgid "Keybinding to open the \"Show Applications\" view"
|
||||||
|
msgid "Keybinding to open the Activities Overview."
|
||||||
|
msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της προβολής ενέργειες."
|
||||||
|
|
||||||
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
|
||||||
msgid "Keybinding to toggle the visibility of the message tray"
|
msgid "Keybinding to toggle the visibility of the message tray"
|
||||||
msgstr "Συνδυασμός πλήκτρων για την ορατότητα της περιοχής ειδοποιήσεων"
|
msgstr "Συνδυασμός πλήκτρων για την ορατότητα της περιοχής ειδοποιήσεων"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
|
||||||
msgid "Keybinding to toggle the visibility of the message tray."
|
msgid "Keybinding to toggle the visibility of the message tray."
|
||||||
msgstr "Συνδυασμός πλήκτρων για την ορατότητα της περιοχής ειδοποιήσεων."
|
msgstr "Συνδυασμός πλήκτρων για την ορατότητα της περιοχής ειδοποιήσεων."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
|
||||||
msgid "Keybinding to focus the active notification"
|
msgid "Keybinding to focus the active notification"
|
||||||
msgstr "Ο συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης"
|
msgstr "Ο συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
|
||||||
msgid "Keybinding to focus the active notification."
|
msgid "Keybinding to focus the active notification."
|
||||||
msgstr "Ο συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης."
|
msgstr "Ο συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
|
||||||
msgid "Keybinding to toggle the screen recorder"
|
msgid "Keybinding to toggle the screen recorder"
|
||||||
msgstr "Συνδυασμός πλήκτρων για την εναλλαγή της μαγνητοσκόπησης οθόνης"
|
msgstr "Συνδυασμός πλήκτρων για την εναλλαγή της μαγνητοσκόπησης οθόνης"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
|
||||||
msgid "Keybinding to start/stop the builtin screen recorder."
|
msgid "Keybinding to start/stop the builtin screen recorder."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Συνδυασμός πλήκτρων για το άνοιγμα/κλείσιμο της ενσωματωμένης "
|
"Συνδυασμός πλήκτρων για το άνοιγμα/κλείσιμο της ενσωματωμένης "
|
||||||
"μαγνητοσκόπησης οθόνης."
|
"μαγνητοσκόπησης οθόνης."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
|
||||||
msgid "Which keyboard to use"
|
msgid "Which keyboard to use"
|
||||||
msgstr "Ποιο πληκτρολόγιο θα χρησιμοποιηθεί"
|
msgstr "Ποιο πληκτρολόγιο θα χρησιμοποιηθεί"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
|
||||||
msgid "The type of keyboard to use."
|
msgid "The type of keyboard to use."
|
||||||
msgstr "Ο τύπος του πληκτρολογίου που θα χρησιμοποιηθεί."
|
msgstr "Ο τύπος του πληκτρολογίου που θα χρησιμοποιηθεί."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
|
||||||
msgid "Framerate used for recording screencasts."
|
msgid "Framerate used for recording screencasts."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ο ρυθμός καρέ που θα χρησιμοποιηθεί για την καταγραφή του βίντεο οθόνης."
|
"Ο ρυθμός καρέ που θα χρησιμοποιηθεί για την καταγραφή του βίντεο οθόνης."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
|
||||||
msgid ""
|
msgid ""
|
||||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||||
"screencast recorder in frames-per-second."
|
"screencast recorder in frames-per-second."
|
||||||
@ -269,13 +283,13 @@ msgstr ""
|
|||||||
"Ο ρυθμός καρέ του στιγμιότυπου που παράγεται από τον εγγραφέα βίντεο οθόνης "
|
"Ο ρυθμός καρέ του στιγμιότυπου που παράγεται από τον εγγραφέα βίντεο οθόνης "
|
||||||
"του GNOME Shell σε καρέ ανά δευτερόλεπτο."
|
"του GNOME Shell σε καρέ ανά δευτερόλεπτο."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
|
||||||
msgid "The gstreamer pipeline used to encode the screencast"
|
msgid "The gstreamer pipeline used to encode the screencast"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ο δίαυλος του gstreamer που χρησιμοποιήθηκε για την κωδικοποίηση του βίντεο "
|
"Ο δίαυλος του gstreamer που χρησιμοποιήθηκε για την κωδικοποίηση του βίντεο "
|
||||||
"οθόνης"
|
"οθόνης"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
|
||||||
#, no-c-format
|
#, no-c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||||
@ -303,12 +317,12 @@ msgstr ""
|
|||||||
"χρησιμοποιείται ως παράδειγμα για το πιθανό βέλτιστο αριθμό πυρήνων του "
|
"χρησιμοποιείται ως παράδειγμα για το πιθανό βέλτιστο αριθμό πυρήνων του "
|
||||||
"συστήματος."
|
"συστήματος."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
|
||||||
msgid "File extension used for storing the screencast"
|
msgid "File extension used for storing the screencast"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Επέκταση αρχείου που θα χρησιμοποιηθεί για την αποθήκευση του βίντεο οθόνης"
|
"Επέκταση αρχείου που θα χρησιμοποιηθεί για την αποθήκευση του βίντεο οθόνης"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
|
||||||
msgid ""
|
msgid ""
|
||||||
"The filename for recorded screencasts will be a unique filename based on the "
|
"The filename for recorded screencasts will be a unique filename based on the "
|
||||||
"current date, and use this extension. It should be changed when recording to "
|
"current date, and use this extension. It should be changed when recording to "
|
||||||
@ -318,11 +332,11 @@ msgstr ""
|
|||||||
"βασισμένο στην τρέχουσα ημερομηνία και θα χρησιμοποιεί αυτή την επέκταση. Θα "
|
"βασισμένο στην τρέχουσα ημερομηνία και θα χρησιμοποιεί αυτή την επέκταση. Θα "
|
||||||
"πρέπει να αλλάζει όταν γίνεται εγγραφή σε διαφορετικό πρότυπο περιέκτη."
|
"πρέπει να αλλάζει όταν γίνεται εγγραφή σε διαφορετικό πρότυπο περιέκτη."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
|
||||||
msgid "The application icon mode."
|
msgid "The application icon mode."
|
||||||
msgstr "Η κατάσταση εικονιδίου εφαρμογής."
|
msgstr "Η κατάσταση εικονιδίου εφαρμογής."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
|
||||||
msgid ""
|
msgid ""
|
||||||
"Configures how the windows are shown in the switcher. Valid possibilities "
|
"Configures how the windows are shown in the switcher. Valid possibilities "
|
||||||
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
||||||
@ -333,22 +347,22 @@ msgstr ""
|
|||||||
"παραθύρου), 'app-icon-only' (εμφανίζει μόνο το εικονίδιο της εφαρμογής) ή "
|
"παραθύρου), 'app-icon-only' (εμφανίζει μόνο το εικονίδιο της εφαρμογής) ή "
|
||||||
"'both' - και τα δύο."
|
"'both' - και τα δύο."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
|
||||||
msgid "Attach modal dialog to the parent window"
|
msgid "Attach modal dialog to the parent window"
|
||||||
msgstr "Προσάρτηση αναγκαστικού διαλόγου στο γονικό παράθυρο"
|
msgstr "Προσάρτηση αναγκαστικού διαλόγου στο γονικό παράθυρο"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
|
||||||
msgid ""
|
msgid ""
|
||||||
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
|
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Αυτό το κλειδί υπερισχύει του κλειδιού στο org.gnome.mutter όταν εκτελείται "
|
"Αυτό το κλειδί υπερισχύει του κλειδιού στο org.gnome.mutter όταν εκτελείται "
|
||||||
"το κέλυφος GNOME."
|
"το κέλυφος GNOME."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
|
||||||
msgid "Arrangement of buttons on the titlebar"
|
msgid "Arrangement of buttons on the titlebar"
|
||||||
msgstr "Διάταξη των κουμπιών στη γραμμή τίτλου"
|
msgstr "Διάταξη των κουμπιών στη γραμμή τίτλου"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
|
||||||
msgid ""
|
msgid ""
|
||||||
"This key overrides the key in org.gnome.desktop.wm.preferences when running "
|
"This key overrides the key in org.gnome.desktop.wm.preferences when running "
|
||||||
"GNOME Shell."
|
"GNOME Shell."
|
||||||
@ -356,17 +370,17 @@ msgstr ""
|
|||||||
"Αυτό το κλειδί υπερισχύει του κλειδιού στο org.gnome.desktop.wm.preferences "
|
"Αυτό το κλειδί υπερισχύει του κλειδιού στο org.gnome.desktop.wm.preferences "
|
||||||
"όταν εκτελείται το κέλυφος GNOME."
|
"όταν εκτελείται το κέλυφος GNOME."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
|
||||||
msgid "Enable edge tiling when dropping windows on screen edges"
|
msgid "Enable edge tiling when dropping windows on screen edges"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ενεργοποίηση της παράθεσης άκρων όταν αποθέτετε παράθυρα στις άκρες της "
|
"Ενεργοποίηση της παράθεσης άκρων όταν αποθέτετε παράθυρα στις άκρες της "
|
||||||
"οθόνης"
|
"οθόνης"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:49
|
||||||
msgid "Workspaces are managed dynamically"
|
msgid "Workspaces are managed dynamically"
|
||||||
msgstr "Οι χώροι εργασίας διαχειρίζονται δυναμικά"
|
msgstr "Οι χώροι εργασίας διαχειρίζονται δυναμικά"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:50
|
||||||
msgid "Workspaces only on primary monitor"
|
msgid "Workspaces only on primary monitor"
|
||||||
msgstr "Χώροι εργασίας μόνο στην κύρια οθόνη"
|
msgstr "Χώροι εργασίας μόνο στην κύρια οθόνη"
|
||||||
|
|
||||||
@ -388,43 +402,42 @@ msgstr ""
|
|||||||
"πολλαπλών επιλογών."
|
"πολλαπλών επιλογών."
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:405
|
#: ../js/gdm/loginDialog.js:405
|
||||||
#| msgid "Session..."
|
|
||||||
msgid "Session…"
|
msgid "Session…"
|
||||||
msgstr "Συνεδρία…"
|
msgstr "Συνεδρία…"
|
||||||
|
|
||||||
#. translators: this message is shown below the user list on the
|
#. translators: this message is shown below the user list on the
|
||||||
#. login screen. It can be activated to reveal an entry for
|
#. login screen. It can be activated to reveal an entry for
|
||||||
#. manually entering the username.
|
#. manually entering the username.
|
||||||
#: ../js/gdm/loginDialog.js:629
|
#: ../js/gdm/loginDialog.js:630
|
||||||
msgid "Not listed?"
|
msgid "Not listed?"
|
||||||
msgstr "Δεν είστε στη λίστα;"
|
msgstr "Δεν είστε στη λίστα;"
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:783 ../js/ui/components/networkAgent.js:137
|
#: ../js/gdm/loginDialog.js:787 ../js/ui/components/networkAgent.js:137
|
||||||
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
|
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:376
|
||||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||||
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
|
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:100
|
||||||
#: ../js/ui/userMenu.js:932
|
#: ../js/ui/userMenu.js:938
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Ακύρωση"
|
msgstr "Ακύρωση"
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:799
|
#: ../js/gdm/loginDialog.js:803
|
||||||
msgctxt "button"
|
msgctxt "button"
|
||||||
msgid "Sign In"
|
msgid "Sign In"
|
||||||
msgstr "Σύνδεση"
|
msgstr "Σύνδεση"
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:799
|
#: ../js/gdm/loginDialog.js:803
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "Επόμενο"
|
msgstr "Επόμενο"
|
||||||
|
|
||||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||||
#. is not visible here since we only care about phase2 authentication
|
#. is not visible here since we only care about phase2 authentication
|
||||||
#. (and don't even care of which one)
|
#. (and don't even care of which one)
|
||||||
#: ../js/gdm/loginDialog.js:904 ../js/ui/components/networkAgent.js:260
|
#: ../js/gdm/loginDialog.js:918 ../js/ui/components/networkAgent.js:260
|
||||||
#: ../js/ui/components/networkAgent.js:278
|
#: ../js/ui/components/networkAgent.js:278
|
||||||
msgid "Username: "
|
msgid "Username: "
|
||||||
msgstr "Όνομα χρήστη: "
|
msgstr "Όνομα χρήστη: "
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:1157
|
#: ../js/gdm/loginDialog.js:1174
|
||||||
msgid "Login Window"
|
msgid "Login Window"
|
||||||
msgstr "Παράθυρο σύνδεσης"
|
msgstr "Παράθυρο σύνδεσης"
|
||||||
|
|
||||||
@ -433,8 +446,8 @@ msgstr "Παράθυρο σύνδεσης"
|
|||||||
msgid "Power"
|
msgid "Power"
|
||||||
msgstr "Ενέργεια"
|
msgstr "Ενέργεια"
|
||||||
|
|
||||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:694 ../js/ui/userMenu.js:698
|
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
|
||||||
#: ../js/ui/userMenu.js:814
|
#: ../js/ui/userMenu.js:816
|
||||||
msgid "Suspend"
|
msgid "Suspend"
|
||||||
msgstr "Αναστολή"
|
msgstr "Αναστολή"
|
||||||
|
|
||||||
@ -442,58 +455,58 @@ msgstr "Αναστολή"
|
|||||||
msgid "Restart"
|
msgid "Restart"
|
||||||
msgstr "Επανεκκίνηση"
|
msgstr "Επανεκκίνηση"
|
||||||
|
|
||||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:696
|
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
|
||||||
#: ../js/ui/userMenu.js:698 ../js/ui/userMenu.js:813 ../js/ui/userMenu.js:936
|
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
|
||||||
msgid "Power Off"
|
msgid "Power Off"
|
||||||
msgstr "Απενεργοποίηση"
|
msgstr "Απενεργοποίηση"
|
||||||
|
|
||||||
#: ../js/gdm/util.js:182
|
#: ../js/gdm/util.js:249
|
||||||
msgid "Authentication error"
|
msgid "Authentication error"
|
||||||
msgstr "Σφάλμα πιστοποίησης"
|
msgstr "Σφάλμα πιστοποίησης"
|
||||||
|
|
||||||
#. Translators: this message is shown below the password entry field
|
#. Translators: this message is shown below the password entry field
|
||||||
#. to indicate the user can swipe their finger instead
|
#. to indicate the user can swipe their finger instead
|
||||||
#: ../js/gdm/util.js:299
|
#: ../js/gdm/util.js:366
|
||||||
msgid "(or swipe finger)"
|
msgid "(or swipe finger)"
|
||||||
msgstr "(ή περάστε το δάκτυλο σας)"
|
msgstr "(ή περάστε το δάκτυλο σας)"
|
||||||
|
|
||||||
#: ../js/gdm/util.js:324
|
#: ../js/gdm/util.js:391
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "(e.g., user or %s)"
|
msgid "(e.g., user or %s)"
|
||||||
msgstr "(π.χ χρήστης ή %s)"
|
msgstr "(π.χ χρήστης ή %s)"
|
||||||
|
|
||||||
#: ../js/misc/util.js:94
|
#: ../js/misc/util.js:97
|
||||||
msgid "Command not found"
|
msgid "Command not found"
|
||||||
msgstr "Δε βρέθηκε η εντολή"
|
msgstr "Δε βρέθηκε η εντολή"
|
||||||
|
|
||||||
#. Replace "Error invoking GLib.shell_parse_argv: " with
|
#. Replace "Error invoking GLib.shell_parse_argv: " with
|
||||||
#. something nicer
|
#. something nicer
|
||||||
#: ../js/misc/util.js:127
|
#: ../js/misc/util.js:130
|
||||||
msgid "Could not parse command:"
|
msgid "Could not parse command:"
|
||||||
msgstr "Δεν ήταν δυνατό να επεξεργαστεί η εντολή:"
|
msgstr "Δεν ήταν δυνατό να επεξεργαστεί η εντολή:"
|
||||||
|
|
||||||
#: ../js/misc/util.js:135
|
#: ../js/misc/util.js:138
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Execution of '%s' failed:"
|
msgid "Execution of '%s' failed:"
|
||||||
msgstr "Η εκτέλεση του '%s' απέτυχε:"
|
msgstr "Η εκτέλεση του '%s' απέτυχε:"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:348
|
#: ../js/ui/appDisplay.js:349
|
||||||
msgid "Frequent"
|
msgid "Frequent"
|
||||||
msgstr "Συχνό"
|
msgstr "Συχνό"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:355
|
#: ../js/ui/appDisplay.js:356
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Όλα"
|
msgstr "Όλα"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:913
|
#: ../js/ui/appDisplay.js:914
|
||||||
msgid "New Window"
|
msgid "New Window"
|
||||||
msgstr "Νέο παράθυρο"
|
msgstr "Νέο παράθυρο"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:916 ../js/ui/dash.js:284
|
#: ../js/ui/appDisplay.js:917 ../js/ui/dash.js:284
|
||||||
msgid "Remove from Favorites"
|
msgid "Remove from Favorites"
|
||||||
msgstr "Αφαίρεση από τα αγαπημένα"
|
msgstr "Αφαίρεση από τα αγαπημένα"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:917
|
#: ../js/ui/appDisplay.js:918
|
||||||
msgid "Add to Favorites"
|
msgid "Add to Favorites"
|
||||||
msgstr "Προσθήκη στα αγαπημένα"
|
msgstr "Προσθήκη στα αγαπημένα"
|
||||||
|
|
||||||
@ -507,7 +520,7 @@ msgstr "Tο %s προστέθηκε στα αγαπημένα σας."
|
|||||||
msgid "%s has been removed from your favorites."
|
msgid "%s has been removed from your favorites."
|
||||||
msgstr "Tο %s αφαιρέθηκε από τα αγαπημένα σας."
|
msgstr "Tο %s αφαιρέθηκε από τα αγαπημένα σας."
|
||||||
|
|
||||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:787
|
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "Ρυθμίσεις"
|
msgstr "Ρυθμίσεις"
|
||||||
|
|
||||||
@ -530,7 +543,7 @@ msgctxt "event list time"
|
|||||||
msgid "%H\\u2236%M"
|
msgid "%H\\u2236%M"
|
||||||
msgstr "%Ω\\u2236%Λ"
|
msgstr "%Ω\\u2236%Λ"
|
||||||
|
|
||||||
#. Transators: Shown in calendar event list, if 12h format,
|
#. Translators: Shown in calendar event list, if 12h format,
|
||||||
#. \u2236 is a ratio character, similar to : and \u2009 is
|
#. \u2236 is a ratio character, similar to : and \u2009 is
|
||||||
#. a thin space
|
#. a thin space
|
||||||
#: ../js/ui/calendar.js:77
|
#: ../js/ui/calendar.js:77
|
||||||
@ -632,35 +645,35 @@ msgid "S"
|
|||||||
msgstr "Σ"
|
msgstr "Σ"
|
||||||
|
|
||||||
#. Translators: Text to show if there are no events
|
#. Translators: Text to show if there are no events
|
||||||
#: ../js/ui/calendar.js:692
|
#: ../js/ui/calendar.js:720
|
||||||
msgid "Nothing Scheduled"
|
msgid "Nothing Scheduled"
|
||||||
msgstr "Τίποτα προγραμματισμένο"
|
msgstr "Τίποτα προγραμματισμένο"
|
||||||
|
|
||||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||||
#: ../js/ui/calendar.js:708
|
#: ../js/ui/calendar.js:736
|
||||||
msgctxt "calendar heading"
|
msgctxt "calendar heading"
|
||||||
msgid "%A, %B %d"
|
msgid "%A, %B %d"
|
||||||
msgstr "%A, %B %d"
|
msgstr "%A, %B %d"
|
||||||
|
|
||||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||||
#: ../js/ui/calendar.js:711
|
#: ../js/ui/calendar.js:739
|
||||||
msgctxt "calendar heading"
|
msgctxt "calendar heading"
|
||||||
msgid "%A, %B %d, %Y"
|
msgid "%A, %B %d, %Y"
|
||||||
msgstr "%A, %B %d, %Y"
|
msgstr "%A, %B %d, %Y"
|
||||||
|
|
||||||
#: ../js/ui/calendar.js:721
|
#: ../js/ui/calendar.js:749
|
||||||
msgid "Today"
|
msgid "Today"
|
||||||
msgstr "Σήμερα"
|
msgstr "Σήμερα"
|
||||||
|
|
||||||
#: ../js/ui/calendar.js:725
|
#: ../js/ui/calendar.js:753
|
||||||
msgid "Tomorrow"
|
msgid "Tomorrow"
|
||||||
msgstr "Αύριο"
|
msgstr "Αύριο"
|
||||||
|
|
||||||
#: ../js/ui/calendar.js:736
|
#: ../js/ui/calendar.js:764
|
||||||
msgid "This week"
|
msgid "This week"
|
||||||
msgstr "Αυτή η εβδομάδα"
|
msgstr "Αυτή η εβδομάδα"
|
||||||
|
|
||||||
#: ../js/ui/calendar.js:744
|
#: ../js/ui/calendar.js:772
|
||||||
msgid "Next week"
|
msgid "Next week"
|
||||||
msgstr "Επόμενη εβδομάδα"
|
msgstr "Επόμενη εβδομάδα"
|
||||||
|
|
||||||
@ -676,12 +689,12 @@ msgstr "Αποσυνδέθηκε εξωτερικός δίσκος"
|
|||||||
msgid "Removable Devices"
|
msgid "Removable Devices"
|
||||||
msgstr "Αφαιρούμενες συσκευές"
|
msgstr "Αφαιρούμενες συσκευές"
|
||||||
|
|
||||||
#: ../js/ui/components/autorunManager.js:593
|
#: ../js/ui/components/autorunManager.js:594
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Open with %s"
|
msgid "Open with %s"
|
||||||
msgstr "Άνοιγμα με %s"
|
msgstr "Άνοιγμα με %s"
|
||||||
|
|
||||||
#: ../js/ui/components/autorunManager.js:619
|
#: ../js/ui/components/autorunManager.js:620
|
||||||
msgid "Eject"
|
msgid "Eject"
|
||||||
msgstr "Εξαγωγή"
|
msgstr "Εξαγωγή"
|
||||||
|
|
||||||
@ -790,7 +803,7 @@ msgid "Sorry, that didn't work. Please try again."
|
|||||||
msgstr "Συγνώμη, αυτό δεν λειτούργησε. Παρακαλώ προσπαθήστε ξανά."
|
msgstr "Συγνώμη, αυτό δεν λειτούργησε. Παρακαλώ προσπαθήστε ξανά."
|
||||||
|
|
||||||
#. Translators: this is a filename used for screencast recording
|
#. Translators: this is a filename used for screencast recording
|
||||||
#: ../js/ui/components/recorder.js:48
|
#: ../js/ui/components/recorder.js:47
|
||||||
#, no-c-format
|
#, no-c-format
|
||||||
msgid "Screencast from %d %t"
|
msgid "Screencast from %d %t"
|
||||||
msgstr "Βίντεο οθόνης από %d %t"
|
msgstr "Βίντεο οθόνης από %d %t"
|
||||||
@ -1037,7 +1050,7 @@ msgstr "Προβολή λογαριασμού"
|
|||||||
msgid "Unknown reason"
|
msgid "Unknown reason"
|
||||||
msgstr "Άγνωστος λόγος"
|
msgstr "Άγνωστος λόγος"
|
||||||
|
|
||||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:97
|
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96
|
||||||
msgid "Windows"
|
msgid "Windows"
|
||||||
msgstr "Παράθυρα"
|
msgstr "Παράθυρα"
|
||||||
|
|
||||||
@ -1066,7 +1079,7 @@ msgstr "Ρυθμίσεις ημερομηνίας & ώρας"
|
|||||||
#. 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").
|
||||||
#.
|
#.
|
||||||
#: ../js/ui/dateMenu.js:205
|
#: ../js/ui/dateMenu.js:215
|
||||||
msgid "%A %B %e, %Y"
|
msgid "%A %B %e, %Y"
|
||||||
msgstr "%A %B %e, %Y"
|
msgstr "%A %B %e, %Y"
|
||||||
|
|
||||||
@ -1235,7 +1248,6 @@ msgid "Remove"
|
|||||||
msgstr "Αφαίρεση"
|
msgstr "Αφαίρεση"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:1501
|
#: ../js/ui/messageTray.js:1501
|
||||||
#| msgid "No Messages"
|
|
||||||
msgid "Clear Messages"
|
msgid "Clear Messages"
|
||||||
msgstr "Καθαρισμός μηνυμάτων"
|
msgstr "Καθαρισμός μηνυμάτων"
|
||||||
|
|
||||||
@ -1243,15 +1255,15 @@ msgstr "Καθαρισμός μηνυμάτων"
|
|||||||
msgid "Notification Settings"
|
msgid "Notification Settings"
|
||||||
msgstr "Ρυθμίσεις ειδοποιήσεων"
|
msgstr "Ρυθμίσεις ειδοποιήσεων"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:1707
|
#: ../js/ui/messageTray.js:1709
|
||||||
msgid "No Messages"
|
msgid "No Messages"
|
||||||
msgstr "Κανένα μήνυμα"
|
msgstr "Κανένα μήνυμα"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:1787
|
#: ../js/ui/messageTray.js:1785
|
||||||
msgid "Message Tray"
|
msgid "Message Tray"
|
||||||
msgstr "Περιοχή ειδοποιήσεων"
|
msgstr "Περιοχή ειδοποιήσεων"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:2864
|
#: ../js/ui/messageTray.js:2813
|
||||||
msgid "System Information"
|
msgid "System Information"
|
||||||
msgstr "Πληροφορίες συστήματος"
|
msgstr "Πληροφορίες συστήματος"
|
||||||
|
|
||||||
@ -1260,14 +1272,14 @@ msgctxt "program"
|
|||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr "Άγνωστο"
|
msgstr "Άγνωστο"
|
||||||
|
|
||||||
#: ../js/ui/overviewControls.js:460 ../js/ui/screenShield.js:153
|
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:149
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d new message"
|
msgid "%d new message"
|
||||||
msgid_plural "%d new messages"
|
msgid_plural "%d new messages"
|
||||||
msgstr[0] "%d νέο μήνυμα"
|
msgstr[0] "%d νέο μήνυμα"
|
||||||
msgstr[1] "%d νέα μηνύματα"
|
msgstr[1] "%d νέα μηνύματα"
|
||||||
|
|
||||||
#: ../js/ui/overview.js:82
|
#: ../js/ui/overview.js:84
|
||||||
msgid "Undo"
|
msgid "Undo"
|
||||||
msgstr "Αναίρεση"
|
msgstr "Αναίρεση"
|
||||||
|
|
||||||
@ -1279,22 +1291,21 @@ msgstr "Επισκόπηση"
|
|||||||
#. in the search entry when no search is
|
#. in the search entry when no search is
|
||||||
#. active; it should not exceed ~30
|
#. active; it should not exceed ~30
|
||||||
#. characters.
|
#. characters.
|
||||||
#: ../js/ui/overview.js:284
|
#: ../js/ui/overview.js:271
|
||||||
#| msgid "Type to search..."
|
|
||||||
msgid "Type to search…"
|
msgid "Type to search…"
|
||||||
msgstr "Πληκτρολογήστε για αναζήτηση…"
|
msgstr "Πληκτρολογήστε για αναζήτηση…"
|
||||||
|
|
||||||
#: ../js/ui/panel.js:613
|
#: ../js/ui/panel.js:633
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
msgstr "Έξοδος"
|
msgstr "Έξοδος"
|
||||||
|
|
||||||
#. Translators: If there is no suitable word for "Activities"
|
#. Translators: If there is no suitable word for "Activities"
|
||||||
#. in your language, you can use the word for "Overview".
|
#. in your language, you can use the word for "Overview".
|
||||||
#: ../js/ui/panel.js:642
|
#: ../js/ui/panel.js:657
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Δραστηριότητες"
|
msgstr "Δραστηριότητες"
|
||||||
|
|
||||||
#: ../js/ui/panel.js:983
|
#: ../js/ui/panel.js:954
|
||||||
msgid "Top Bar"
|
msgid "Top Bar"
|
||||||
msgstr "Πάνω μπάρα"
|
msgstr "Πάνω μπάρα"
|
||||||
|
|
||||||
@ -1307,32 +1318,32 @@ msgstr "Πάνω μπάρα"
|
|||||||
msgid "toggle-switch-us"
|
msgid "toggle-switch-us"
|
||||||
msgstr "toggle-switch-intl"
|
msgstr "toggle-switch-intl"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:205
|
#: ../js/ui/runDialog.js:74
|
||||||
msgid "Enter a Command"
|
msgid "Enter a Command"
|
||||||
msgstr "Εισαγωγή εντολής"
|
msgstr "Εισαγωγή εντολής"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:241
|
#: ../js/ui/runDialog.js:110
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr "Κλείσιμο"
|
msgstr "Κλείσιμο"
|
||||||
|
|
||||||
#. Translators: This is a time format for a date in
|
#. Translators: This is a time format for a date in
|
||||||
#. long format
|
#. long format
|
||||||
#: ../js/ui/screenShield.js:90
|
#: ../js/ui/screenShield.js:86
|
||||||
msgid "%A, %B %d"
|
msgid "%A, %B %d"
|
||||||
msgstr "%A, %B %d"
|
msgstr "%A, %B %d"
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:155
|
#: ../js/ui/screenShield.js:151
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d new notification"
|
msgid "%d new notification"
|
||||||
msgid_plural "%d new notifications"
|
msgid_plural "%d new notifications"
|
||||||
msgstr[0] "%d νέα ειδοποίηση"
|
msgstr[0] "%d νέα ειδοποίηση"
|
||||||
msgstr[1] "%d νέες ειδοποιήσεις"
|
msgstr[1] "%d νέες ειδοποιήσεις"
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:805
|
#: ../js/ui/screenShield.js:438 ../js/ui/userMenu.js:807
|
||||||
msgid "Lock"
|
msgid "Lock"
|
||||||
msgstr "Κλείδωμα"
|
msgstr "Κλείδωμα"
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:639
|
#: ../js/ui/screenShield.js:641
|
||||||
msgid "GNOME needs to lock the screen"
|
msgid "GNOME needs to lock the screen"
|
||||||
msgstr "Το GNOME χρειάζεται να κλειδώσει την οθόνη"
|
msgstr "Το GNOME χρειάζεται να κλειδώσει την οθόνη"
|
||||||
|
|
||||||
@ -1343,21 +1354,19 @@ msgstr "Το GNOME χρειάζεται να κλειδώσει την οθόν
|
|||||||
#.
|
#.
|
||||||
#. XXX: another option is to kick the user into the gdm login
|
#. XXX: another option is to kick the user into the gdm login
|
||||||
#. screen, where we're not affected by grabs
|
#. screen, where we're not affected by grabs
|
||||||
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
|
#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
|
||||||
#| msgid "Unable to connect to %s"
|
|
||||||
msgid "Unable to lock"
|
msgid "Unable to lock"
|
||||||
msgstr "Αδυναμία κλειδώματος"
|
msgstr "Αδυναμία κλειδώματος"
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1170
|
#: ../js/ui/screenShield.js:763 ../js/ui/screenShield.js:1199
|
||||||
msgid "Lock was blocked by an application"
|
msgid "Lock was blocked by an application"
|
||||||
msgstr "Το κλείδωμα εμποδίστηκε από μια εφαρμογή"
|
msgstr "Το κλείδωμα εμποδίστηκε από μια εφαρμογή"
|
||||||
|
|
||||||
#: ../js/ui/searchDisplay.js:431
|
#: ../js/ui/searchDisplay.js:453
|
||||||
#| msgid "Searching..."
|
|
||||||
msgid "Searching…"
|
msgid "Searching…"
|
||||||
msgstr "Αναζήτηση…"
|
msgstr "Αναζήτηση…"
|
||||||
|
|
||||||
#: ../js/ui/searchDisplay.js:475
|
#: ../js/ui/searchDisplay.js:497
|
||||||
msgid "No results."
|
msgid "No results."
|
||||||
msgstr "Δε βρέθηκαν αποτελέσματα."
|
msgstr "Δε βρέθηκαν αποτελέσματα."
|
||||||
|
|
||||||
@ -1369,11 +1378,11 @@ msgstr "Αντιγραφή"
|
|||||||
msgid "Paste"
|
msgid "Paste"
|
||||||
msgstr "Επικόλληση"
|
msgstr "Επικόλληση"
|
||||||
|
|
||||||
#: ../js/ui/shellEntry.js:105
|
#: ../js/ui/shellEntry.js:101
|
||||||
msgid "Show Text"
|
msgid "Show Text"
|
||||||
msgstr "Εμφάνιση κειμένου"
|
msgstr "Εμφάνιση κειμένου"
|
||||||
|
|
||||||
#: ../js/ui/shellEntry.js:107
|
#: ../js/ui/shellEntry.js:103
|
||||||
msgid "Hide Text"
|
msgid "Hide Text"
|
||||||
msgstr "Απόκρυψη κειμένου"
|
msgstr "Απόκρυψη κειμένου"
|
||||||
|
|
||||||
@ -1385,7 +1394,7 @@ msgstr "Κωδικός"
|
|||||||
msgid "Remember Password"
|
msgid "Remember Password"
|
||||||
msgstr "Απομνημόνευση κωδικού"
|
msgstr "Απομνημόνευση κωδικού"
|
||||||
|
|
||||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:140
|
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:114
|
||||||
msgid "Unlock"
|
msgid "Unlock"
|
||||||
msgstr "Ξεκλείδωμα"
|
msgstr "Ξεκλείδωμα"
|
||||||
|
|
||||||
@ -1449,12 +1458,10 @@ msgid "Visibility"
|
|||||||
msgstr "Ορατότητα"
|
msgstr "Ορατότητα"
|
||||||
|
|
||||||
#: ../js/ui/status/bluetooth.js:59
|
#: ../js/ui/status/bluetooth.js:59
|
||||||
#| msgid "Send Files to Device..."
|
|
||||||
msgid "Send Files to Device…"
|
msgid "Send Files to Device…"
|
||||||
msgstr "Αποστολή αρχείων σε συσκευή…"
|
msgstr "Αποστολή αρχείων σε συσκευή…"
|
||||||
|
|
||||||
#: ../js/ui/status/bluetooth.js:60
|
#: ../js/ui/status/bluetooth.js:60
|
||||||
#| msgid "Set Up a New Device..."
|
|
||||||
msgid "Set Up a New Device…"
|
msgid "Set Up a New Device…"
|
||||||
msgstr "Ρύθμιση νέας συσκευής…"
|
msgstr "Ρύθμιση νέας συσκευής…"
|
||||||
|
|
||||||
@ -1481,7 +1488,6 @@ msgid "connecting..."
|
|||||||
msgstr "σύνδεση..."
|
msgstr "σύνδεση..."
|
||||||
|
|
||||||
#: ../js/ui/status/bluetooth.js:239
|
#: ../js/ui/status/bluetooth.js:239
|
||||||
#| msgid "Send Files..."
|
|
||||||
msgid "Send Files…"
|
msgid "Send Files…"
|
||||||
msgstr "Αποστολή αρχείων…"
|
msgstr "Αποστολή αρχείων…"
|
||||||
|
|
||||||
@ -1694,7 +1700,6 @@ msgstr "Ρυθμίσεις τροφοδοσίας"
|
|||||||
#. 0 is reported when UPower does not have enough data
|
#. 0 is reported when UPower does not have enough data
|
||||||
#. to estimate battery life
|
#. to estimate battery life
|
||||||
#: ../js/ui/status/power.js:99
|
#: ../js/ui/status/power.js:99
|
||||||
#| msgid "Estimating..."
|
|
||||||
msgid "Estimating…"
|
msgid "Estimating…"
|
||||||
msgstr "Υπολογισμός…"
|
msgstr "Υπολογισμός…"
|
||||||
|
|
||||||
@ -1794,59 +1799,59 @@ msgstr "Ένταση ήχου"
|
|||||||
msgid "Microphone"
|
msgid "Microphone"
|
||||||
msgstr "Μικρόφωνο"
|
msgstr "Μικρόφωνο"
|
||||||
|
|
||||||
#: ../js/ui/unlockDialog.js:151
|
#: ../js/ui/unlockDialog.js:125
|
||||||
msgid "Log in as another user"
|
msgid "Log in as another user"
|
||||||
msgstr "Είσοδος ως άλλος χρήστης"
|
msgstr "Είσοδος ως άλλος χρήστης"
|
||||||
|
|
||||||
#: ../js/ui/unlockDialog.js:177
|
#: ../js/ui/unlockDialog.js:146
|
||||||
msgid "Unlock Window"
|
msgid "Unlock Window"
|
||||||
msgstr "Ξεκλείδωμα παραθύρου"
|
msgstr "Ξεκλείδωμα παραθύρου"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:192
|
#: ../js/ui/userMenu.js:193
|
||||||
msgid "Available"
|
msgid "Available"
|
||||||
msgstr "Διαθέσιμος-η"
|
msgstr "Διαθέσιμος-η"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:195
|
#: ../js/ui/userMenu.js:196
|
||||||
msgid "Busy"
|
msgid "Busy"
|
||||||
msgstr "Απασχολημένος-η"
|
msgstr "Απασχολημένος-η"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:198
|
#: ../js/ui/userMenu.js:199
|
||||||
msgid "Invisible"
|
msgid "Invisible"
|
||||||
msgstr "Αόρατος-η"
|
msgstr "Αόρατος-η"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:201
|
#: ../js/ui/userMenu.js:202
|
||||||
msgid "Away"
|
msgid "Away"
|
||||||
msgstr "Απουσιάζει"
|
msgstr "Απουσιάζει"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:204
|
#: ../js/ui/userMenu.js:205
|
||||||
msgid "Idle"
|
msgid "Idle"
|
||||||
msgstr "Αδρανής"
|
msgstr "Αδρανής"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:207
|
#: ../js/ui/userMenu.js:208
|
||||||
msgid "Offline"
|
msgid "Offline"
|
||||||
msgstr "Εκτός σύνδεσης"
|
msgstr "Εκτός σύνδεσης"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:779
|
#: ../js/ui/userMenu.js:781
|
||||||
msgid "Notifications"
|
msgid "Notifications"
|
||||||
msgstr "Ειδοποιήσεις"
|
msgstr "Ειδοποιήσεις"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:795
|
#: ../js/ui/userMenu.js:797
|
||||||
msgid "Switch User"
|
msgid "Switch User"
|
||||||
msgstr "Αλλαγή χρήστη"
|
msgstr "Αλλαγή χρήστη"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:800
|
#: ../js/ui/userMenu.js:802
|
||||||
msgid "Log Out"
|
msgid "Log Out"
|
||||||
msgstr "Αποσύνδεση"
|
msgstr "Αποσύνδεση"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:820
|
#: ../js/ui/userMenu.js:822
|
||||||
msgid "Install Updates & Restart"
|
msgid "Install Updates & Restart"
|
||||||
msgstr "Εγκατάσταση ενημερώσεων & επανεκκίνηση"
|
msgstr "Εγκατάσταση ενημερώσεων & επανεκκίνηση"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:838
|
#: ../js/ui/userMenu.js:840
|
||||||
msgid "Your chat status will be set to busy"
|
msgid "Your chat status will be set to busy"
|
||||||
msgstr "Η κατάσταση συνομιλίας σας θα ορισθεί σε απασχολημένος"
|
msgstr "Η κατάσταση συνομιλίας σας θα ορισθεί σε απασχολημένος"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:839
|
#: ../js/ui/userMenu.js:841
|
||||||
msgid ""
|
msgid ""
|
||||||
"Notifications are now disabled, including chat messages. Your online status "
|
"Notifications are now disabled, including chat messages. Your online status "
|
||||||
"has been adjusted to let others know that you might not see their messages."
|
"has been adjusted to let others know that you might not see their messages."
|
||||||
@ -1855,34 +1860,36 @@ msgstr ""
|
|||||||
"κατάσταση σας έχει ορισθεί έτσι ώστε να γίνεται γνωστό ότι πιθανόν να μην "
|
"κατάσταση σας έχει ορισθεί έτσι ώστε να γίνεται γνωστό ότι πιθανόν να μην "
|
||||||
"δείτε τα μηνύματα τους."
|
"δείτε τα μηνύματα τους."
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:885
|
#: ../js/ui/userMenu.js:888
|
||||||
msgid "Other users are logged in."
|
msgid "Other users are logged in."
|
||||||
msgstr "Και άλλοι χρήστες είναι συνδεμένοι."
|
msgstr "Και άλλοι χρήστες είναι συνδεμένοι."
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:890
|
#: ../js/ui/userMenu.js:893
|
||||||
msgid "Shutting down might cause them to lose unsaved work."
|
msgid "Shutting down might cause them to lose unsaved work."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Κλείνοντας μπορεί να τους προκαλέσετε την απώλεια αναποθήκευτης εργασίας."
|
"Κλείνοντας μπορεί να τους προκαλέσετε την απώλεια αναποθήκευτης εργασίας."
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:916
|
#. Translators: Remote here refers to a remote session, like a ssh login
|
||||||
|
#: ../js/ui/userMenu.js:921
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s (remote)"
|
msgid "%s (remote)"
|
||||||
msgstr "%s (απομακρυσμένο)"
|
msgstr "%s (απομακρυσμένο)"
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:918
|
#. Translators: Console here refers to a tty like a VT console
|
||||||
|
#: ../js/ui/userMenu.js:924
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s (console)"
|
msgid "%s (console)"
|
||||||
msgstr "%s (κονσόλα)"
|
msgstr "%s (κονσόλα)"
|
||||||
|
|
||||||
#: ../js/ui/viewSelector.js:101
|
#: ../js/ui/viewSelector.js:100
|
||||||
msgid "Applications"
|
msgid "Applications"
|
||||||
msgstr "Εφαρμογές"
|
msgstr "Εφαρμογές"
|
||||||
|
|
||||||
#: ../js/ui/viewSelector.js:105
|
#: ../js/ui/viewSelector.js:104
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Αναζήτηση"
|
msgstr "Αναζήτηση"
|
||||||
|
|
||||||
#: ../js/ui/wanda.js:92
|
#: ../js/ui/wanda.js:77
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sorry, no wisdom for you today:\n"
|
"Sorry, no wisdom for you today:\n"
|
||||||
@ -1891,7 +1898,7 @@ msgstr ""
|
|||||||
"Συγγνώμη, κανένα απόφθεγμα για εσάς σήμερα:\n"
|
"Συγγνώμη, κανένα απόφθεγμα για εσάς σήμερα:\n"
|
||||||
"%s"
|
"%s"
|
||||||
|
|
||||||
#: ../js/ui/wanda.js:96
|
#: ../js/ui/wanda.js:81
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s the Oracle says"
|
msgid "%s the Oracle says"
|
||||||
msgstr "%s ο Προφήτης λέει"
|
msgstr "%s ο Προφήτης λέει"
|
||||||
|
6
po/lt.po
6
po/lt.po
@ -461,7 +461,7 @@ msgstr "Nepavyko įvykdyti „%s“:"
|
|||||||
|
|
||||||
#: ../js/ui/appDisplay.js:349
|
#: ../js/ui/appDisplay.js:349
|
||||||
msgid "Frequent"
|
msgid "Frequent"
|
||||||
msgstr "Dažniausios"
|
msgstr "Dažnai naudojamos"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:356
|
#: ../js/ui/appDisplay.js:356
|
||||||
msgid "All"
|
msgid "All"
|
||||||
@ -1262,7 +1262,7 @@ msgstr "Apžvalga"
|
|||||||
#. characters.
|
#. characters.
|
||||||
#: ../js/ui/overview.js:271
|
#: ../js/ui/overview.js:271
|
||||||
msgid "Type to search…"
|
msgid "Type to search…"
|
||||||
msgstr "Rašykite ko ieškote…"
|
msgstr "Rašykite, ko ieškote…"
|
||||||
|
|
||||||
#: ../js/ui/panel.js:612
|
#: ../js/ui/panel.js:612
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
@ -1299,7 +1299,7 @@ msgstr "Užverti"
|
|||||||
#. long format
|
#. long format
|
||||||
#: ../js/ui/screenShield.js:86
|
#: ../js/ui/screenShield.js:86
|
||||||
msgid "%A, %B %d"
|
msgid "%A, %B %d"
|
||||||
msgstr "%A, %B %d"
|
msgstr "%A, %B %d d."
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:151
|
#: ../js/ui/screenShield.js:151
|
||||||
#, c-format
|
#, c-format
|
||||||
|
320
po/ml.po
320
po/ml.po
@ -10,9 +10,9 @@ msgstr ""
|
|||||||
"Project-Id-Version: gnome-shell master\n"
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"shell&keywords=I18N+L10N&component=general\n"
|
"shell&keywords=I18N+L10N&component=general\n"
|
||||||
"POT-Creation-Date: 2013-03-23 11:49+0000\n"
|
"POT-Creation-Date: 2013-04-24 18:23+0000\n"
|
||||||
"PO-Revision-Date: 2013-03-25 14:57+0530\n"
|
"PO-Revision-Date: 2013-04-25 00:32+0530\n"
|
||||||
"Last-Translator: Ani Peter <peter.ani@gmail.com>\n"
|
"Last-Translator: Balasankar C <c.balasankar@gmail.com>\n"
|
||||||
"Language-Team: American English <kde-i18n-doc@kde.org>\n"
|
"Language-Team: American English <kde-i18n-doc@kde.org>\n"
|
||||||
"Language: ml\n"
|
"Language: ml\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -44,10 +44,14 @@ msgid "Focus the active notification"
|
|||||||
msgstr "സജീവമായ അറിയിപ്പിനെ കേന്ദ്രീകരിക്കുക"
|
msgstr "സജീവമായ അറിയിപ്പിനെ കേന്ദ്രീകരിക്കുക"
|
||||||
|
|
||||||
#: ../data/50-gnome-shell-system.xml.in.h:4
|
#: ../data/50-gnome-shell-system.xml.in.h:4
|
||||||
|
msgid "Show the overview"
|
||||||
|
msgstr "പൊതുവായ അവലോകനം കാണിക്കുക"
|
||||||
|
|
||||||
|
#: ../data/50-gnome-shell-system.xml.in.h:5
|
||||||
msgid "Show all applications"
|
msgid "Show all applications"
|
||||||
msgstr "എല്ലാ പ്രയോഗങ്ങളും കാണിയ്ക്കുക"
|
msgstr "എല്ലാ പ്രയോഗങ്ങളും കാണിയ്ക്കുക"
|
||||||
|
|
||||||
#: ../data/50-gnome-shell-system.xml.in.h:5
|
#: ../data/50-gnome-shell-system.xml.in.h:6
|
||||||
msgid "Open the application menu"
|
msgid "Open the application menu"
|
||||||
msgstr "പ്രയോഗത്തിന്റെ മെനു തുറക്കുക"
|
msgstr "പ്രയോഗത്തിന്റെ മെനു തുറക്കുക"
|
||||||
|
|
||||||
@ -71,8 +75,7 @@ msgstr "ഗ്നോം ഷെല് എക്സ്റ്റെന്ഷ
|
|||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
|
||||||
msgid "Enable internal tools useful for developers and testers from Alt-F2"
|
msgid "Enable internal tools useful for developers and testers from Alt-F2"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Alt-F2-ല് ഡവലപ്പര്മാര്ക്കും ടെസ്റ്റേര്സിനും പ്രയോജനകരമായ ആന്തരിക "
|
"Alt-F2-ല് ഡവലപ്പര്മാര്ക്കും ടെസ്റ്റേര്സിനും പ്രയോജനകരമായ ആന്തരിക പ്രയോഗങ്ങള് പ്രവര്ത്തന "
|
||||||
"പ്രയോഗങ്ങള് പ്രവര്ത്തന "
|
|
||||||
"സജ്ജമാക്കുന്നു"
|
"സജ്ജമാക്കുന്നു"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
|
||||||
@ -80,8 +83,7 @@ msgid ""
|
|||||||
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
|
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
|
||||||
"dialog."
|
"dialog."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Alt-F2 ഡയലോഗ് ഉപയോഗിച്ചു് ആന്തരിക ഡീബഗ്ഗിലേക്കും നീരീക്ഷണ പ്രയോഗങ്ങളിലേക്കും "
|
"Alt-F2 ഡയലോഗ് ഉപയോഗിച്ചു് ആന്തരിക ഡീബഗ്ഗിലേക്കും നീരീക്ഷണ പ്രയോഗങ്ങളിലേക്കും പ്രവേശനം "
|
||||||
"പ്രവേശനം "
|
|
||||||
"അനുവദിയ്ക്കുക."
|
"അനുവദിയ്ക്കുക."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
|
||||||
@ -95,19 +97,14 @@ msgid ""
|
|||||||
"list. You can also manipulate this list with the EnableExtension and "
|
"list. You can also manipulate this list with the EnableExtension and "
|
||||||
"DisableExtension DBus methods on org.gnome.Shell."
|
"DisableExtension DBus methods on org.gnome.Shell."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഗ്നോം ഷെല് എക്സ്റ്റെന്ഷനുകള്ക്കു് ഒരു യുയുഐഡി വിശേഷതയുണ്ടു്; ലഭ്യമാക്കേണ്ട "
|
"ഗ്നോം ഷെല് എക്സ്റ്റെന്ഷനുകള്ക്കു് ഒരു യുയുഐഡി വിശേഷതയുണ്ടു്; ലഭ്യമാക്കേണ്ട എക്സ്റ്റെന്ഷനുകള് ഈ കീ പട്ടിക "
|
||||||
"എക്സ്റ്റെന്ഷനുകള് ഈ കീ പട്ടിക "
|
"ലഭ്യമാക്കുന്നു. ലഭ്യമാക്കേണ്ട ഏതു് എക്സ്റ്റെന്ഷനും ഈ പട്ടികയിലുണ്ടാവണം. org.gnome.Shell-ല് "
|
||||||
"ലഭ്യമാക്കുന്നു. ലഭ്യമാക്കേണ്ട ഏതു് എക്സ്റ്റെന്ഷനും ഈ പട്ടികയിലുണ്ടാവണം. "
|
"നിങ്ങള്ക്കു് EnableExtension, DisableExtension എന്നീ ഡീബസ് രീതികളിലൂടെ ഈ പട്ടിക "
|
||||||
"org.gnome.Shell-ല് "
|
|
||||||
"നിങ്ങള്ക്കു് EnableExtension, DisableExtension എന്നീ ഡീബസ് രീതികളിലൂടെ ഈ "
|
|
||||||
"പട്ടിക "
|
|
||||||
"കൈകാര്യം ചെയ്യുവാനും സാധിയ്ക്കുന്നു."
|
"കൈകാര്യം ചെയ്യുവാനും സാധിയ്ക്കുന്നു."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
|
||||||
msgid "Whether to collect stats about applications usage"
|
msgid "Whether to collect stats about applications usage"
|
||||||
msgstr ""
|
msgstr "പ്രയോഗങ്ങളുടെ ഉപയോഗത്തെപ്പറ്റിയുള്ള സ്ഥിതിവിവരക്കണക്കുകള് ശേഖരിയ്ക്കണമോ എന്നു്"
|
||||||
"പ്രയോഗങ്ങളുടെ ഉപയോഗത്തെപ്പറ്റിയുള്ള സ്ഥിതിവിവരക്കണക്കുകള് ശേഖരിയ്ക്കണമോ "
|
|
||||||
"എന്നു്"
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -116,12 +113,9 @@ msgid ""
|
|||||||
"want to disable this for privacy reasons. Please note that doing so won't "
|
"want to disable this for privacy reasons. Please note that doing so won't "
|
||||||
"remove already saved data."
|
"remove already saved data."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഏറ്റവും കൂടുതല് തവണ ഉപയോഗിയ്ക്കുന്ന പ്രയോഗങ്ങള് ലഭ്യമാക്കുന്നതിനായി ഷെല് "
|
"ഏറ്റവും കൂടുതല് തവണ ഉപയോഗിയ്ക്കുന്ന പ്രയോഗങ്ങള് ലഭ്യമാക്കുന്നതിനായി ഷെല് സാധാരണയായി സജീവമായ "
|
||||||
"സാധാരണയായി സജീവമായ "
|
"പ്രയോഗങ്ങളെ നിരീക്ഷിയ്ക്കുന്നു. (ഉദാഹരണത്തിനു്, ലോഞ്ചേര്സ്). ഈ ഡേറ്റാ സ്വകാര്യമായി "
|
||||||
"പ്രയോഗങ്ങളെ നിരീക്ഷിയ്ക്കുന്നു. (ഉദാഹരണത്തിനു്, ലോഞ്ചേര്സ്). ഈ ഡേറ്റാ "
|
"സൂക്ഷിയ്ക്കുന്നെങ്കിലും, ചില കാരണങ്ങളാല് ഇതു് പ്രവര്ത്തന രഹിതമാക്കേണ്ടതുണ്ടു്. ഇങ്ങനെ ചെയ്യുന്നതു് "
|
||||||
"സ്വകാര്യമായി "
|
|
||||||
"സൂക്ഷിയ്ക്കുന്നെങ്കിലും, ചില കാരണങ്ങളാല് ഇതു് പ്രവര്ത്തന "
|
|
||||||
"രഹിതമാക്കേണ്ടതുണ്ടു്. ഇങ്ങനെ ചെയ്യുന്നതു് "
|
|
||||||
"നിങ്ങള് സൂക്ഷിച്ച ഡേറ്റയെ ബാധിയ്ക്കുന്നതല്ല."
|
"നിങ്ങള് സൂക്ഷിച്ച ഡേറ്റയെ ബാധിയ്ക്കുന്നതല്ല."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
|
||||||
@ -132,8 +126,7 @@ msgstr "ഇഷ്ടമുള്ള പ്രയോഗങ്ങള്ക്
|
|||||||
msgid ""
|
msgid ""
|
||||||
"The applications corresponding to these identifiers will be displayed in the "
|
"The applications corresponding to these identifiers will be displayed in the "
|
||||||
"favorites area."
|
"favorites area."
|
||||||
msgstr ""
|
msgstr "ഈ ഐഡന്റിഫയറുകള്ക്കുള്ള പ്രയോഗങ്ങള് ഉചിതമായ സ്ഥലങ്ങളില് കാണിയ്ക്കുന്നു."
|
||||||
"ഈ ഐഡന്റിഫയറുകള്ക്കുള്ള പ്രയോഗങ്ങള് ഉചിതമായ സ്ഥലങ്ങളില് കാണിയ്ക്കുന്നു."
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
|
||||||
msgid "List of categories that should be displayed as folders"
|
msgid "List of categories that should be displayed as folders"
|
||||||
@ -144,8 +137,8 @@ msgid ""
|
|||||||
"Each category name in this list will be represented as folder in the "
|
"Each category name in this list will be represented as folder in the "
|
||||||
"application view, rather than being displayed inline in the main view."
|
"application view, rather than being displayed inline in the main view."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഈ പട്ടികയിലുള്ള ഓരോ വിഭാഗത്തിന്റെ പേരും, പ്രധാന കാഴ്ചയില് ഓരോ വരിയായി "
|
"ഈ പട്ടികയിലുള്ള ഓരോ വിഭാഗത്തിന്റെ പേരും, പ്രധാന കാഴ്ചയില് ഓരോ വരിയായി കാണിയ്ക്കുന്നതിനു് "
|
||||||
"കാണിയ്ക്കുന്നതിനു് പകരം പ്രയോഗങ്ങളുടെ കാഴ്ചയില് ഫോള്ഡറായി കാണിയ്ക്കുന്നു. "
|
"പകരം പ്രയോഗങ്ങളുടെ കാഴ്ചയില് ഫോള്ഡറായി കാണിയ്ക്കുന്നു. "
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
|
||||||
msgid "History for command (Alt-F2) dialog"
|
msgid "History for command (Alt-F2) dialog"
|
||||||
@ -160,8 +153,7 @@ msgid ""
|
|||||||
"Internally used to store the last IM presence explicitly set by the user. "
|
"Internally used to store the last IM presence explicitly set by the user. "
|
||||||
"The value here is from the TpConnectionPresenceType enumeration."
|
"The value here is from the TpConnectionPresenceType enumeration."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഉപയോക്താവു് സജ്ജമാക്കിയ അവസാന ഐഎം ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് "
|
"ഉപയോക്താവു് സജ്ജമാക്കിയ അവസാന ഐഎം ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
|
||||||
"ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
|
|
||||||
"TpConnectionPresenceType തരത്തിലുള്ളതാകുന്നു."
|
"TpConnectionPresenceType തരത്തിലുള്ളതാകുന്നു."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
|
||||||
@ -169,8 +161,7 @@ msgid ""
|
|||||||
"Internally used to store the last session presence status for the user. The "
|
"Internally used to store the last session presence status for the user. The "
|
||||||
"value here is from the GsmPresenceStatus enumeration."
|
"value here is from the GsmPresenceStatus enumeration."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഉപയോക്താവിനുള്ള അവസാന സെഷന് അവസ്ഥ ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് "
|
"ഉപയോക്താവിനുള്ള അവസാന സെഷന് അവസ്ഥ ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
|
||||||
"ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
|
|
||||||
"GsmPresenceStatus തരത്തിലുള്ളതാകുന്നു."
|
"GsmPresenceStatus തരത്തിലുള്ളതാകുന്നു."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
|
||||||
@ -182,15 +173,13 @@ msgid ""
|
|||||||
"This key overrides the automatic hiding of the 'Log out' menuitem in single-"
|
"This key overrides the automatic hiding of the 'Log out' menuitem in single-"
|
||||||
"user, single-session situations."
|
"user, single-session situations."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"സിംഗിള് യൂസര്, സിംഗിള് സെഷനില് 'ലോഗൌട്ട്' മെനുവസ്തു അദൃശ്യമാക്കുന്നതിനായി "
|
"സിംഗിള് യൂസര്, സിംഗിള് സെഷനില് 'ലോഗൌട്ട്' മെനുവസ്തു അദൃശ്യമാക്കുന്നതിനായി ഈ കീ ഉപയോഗിയ്ക്കുന്നു"
|
||||||
"ഈ കീ ഉപയോഗിയ്ക്കുന്നു"
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
|
||||||
msgid ""
|
msgid ""
|
||||||
"Whether to remember password for mounting encrypted or remote filesystems"
|
"Whether to remember password for mounting encrypted or remote filesystems"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"എന്ക്രിപ്റ്റ് ചെയ്തതോ വിദൂരമോ ആയ ഫയല്സിസ്റ്റങ്ങള് മൌണ്ട് ചെയ്യുമ്പോഴുള്ള "
|
"എന്ക്രിപ്റ്റ് ചെയ്തതോ വിദൂരമോ ആയ ഫയല്സിസ്റ്റങ്ങള് മൌണ്ട് ചെയ്യുമ്പോഴുള്ള രഹസ്യവാക്ക് ഓര്മ്മിക്കണോ എന്ന്"
|
||||||
"രഹസ്യവാക്ക് ഓര്മ്മിക്കണോ എന്ന്"
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -199,10 +188,9 @@ msgid ""
|
|||||||
"'Remember Password' checkbox will be present. This key sets the default "
|
"'Remember Password' checkbox will be present. This key sets the default "
|
||||||
"state of the checkbox."
|
"state of the checkbox."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഒരു എന്ക്രിപ്റ്റ് ചെയ്ത ഡിവൈസ് അല്ലെങ്കില് വിദൂര ഫയല്സിസ്റ്റം മൌണ്ട് "
|
"ഒരു എന്ക്രിപ്റ്റ് ചെയ്ത ഡിവൈസ് അല്ലെങ്കില് വിദൂര ഫയല്സിസ്റ്റം മൌണ്ട് ചെയ്യുമ്പോള് ഷെല് ഒരു രഹസ്യവാക്ക് "
|
||||||
"ചെയ്യുമ്പോള് ഷെല് ഒരു രഹസ്യവാക്ക് ആവശ്യപ്പെടുന്നു. രഹസ്യവാക്ക് "
|
"ആവശ്യപ്പെടുന്നു. രഹസ്യവാക്ക് സൂക്ഷിയ്ക്കുവാന് സാധ്യമെങ്കില്, 'രഹസ്യവാക്ക് ഓര്ത്തു്വയ്ക്കുക' ചെക്ക്ബോക്സ് "
|
||||||
"സൂക്ഷിയ്ക്കുവാന് സാധ്യമെങ്കില്, 'രഹസ്യവാക്ക് ഓര്ത്തു്വയ്ക്കുക' "
|
"കാണാം. ഈ കീ ചെക്ക്ബോക്സിന്റെ സ്വതവേയുള്ള അവസ്ഥ സജ്ജമാക്കുന്നു."
|
||||||
"ചെക്ക്ബോക്സ് കാണാം. ഈ കീ ചെക്ക്ബോക്സിന്റെ സ്വതവേയുള്ള അവസ്ഥ സജ്ജമാക്കുന്നു."
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
|
||||||
msgid "Show the week date in the calendar"
|
msgid "Show the week date in the calendar"
|
||||||
@ -228,61 +216,65 @@ msgstr "\"പ്രയോഗങ്ങള് കാണിയ്ക്കുക
|
|||||||
msgid ""
|
msgid ""
|
||||||
"Keybinding to open the \"Show Applications\" view of the Activities Overview."
|
"Keybinding to open the \"Show Applications\" view of the Activities Overview."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"പ്രവര്ത്തികളുടെ അവലോകനത്തിന്റെ \"പ്രയോഗങ്ങള് കാണിയ്ക്കുക\" എന്ന കാഴ്ച "
|
"പ്രവര്ത്തികളുടെ അവലോകനത്തിന്റെ \"പ്രയോഗങ്ങള് കാണിയ്ക്കുക\" എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്ഡിങ്"
|
||||||
"തുറക്കുന്നതിനുള്ള കീബൈന്ഡിങ്"
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
|
||||||
|
msgid "Keybinding to open the overview"
|
||||||
|
msgstr "പൊതുവായ അവലോകനം തുറക്കുന്നതിനുള്ള കീബൈന്ഡിങ്"
|
||||||
|
|
||||||
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
|
||||||
|
msgid "Keybinding to open the Activities Overview."
|
||||||
|
msgstr "പ്രയോഗങ്ങളുടെ പൊതുവായ അവലോകനം എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്ഡിങ്"
|
||||||
|
|
||||||
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
|
||||||
msgid "Keybinding to toggle the visibility of the message tray"
|
msgid "Keybinding to toggle the visibility of the message tray"
|
||||||
msgstr "സന്ദേശ ട്രേയുടെ ദൃശ്യത ടൊഗ്ഗിള് ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
|
msgstr "സന്ദേശ ട്രേയുടെ ദൃശ്യത ടൊഗ്ഗിള് ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
|
||||||
msgid "Keybinding to toggle the visibility of the message tray."
|
msgid "Keybinding to toggle the visibility of the message tray."
|
||||||
msgstr "സന്ദേശ ട്രേയുടെ ദൃശ്യത ടൊഗ്ഗിള് ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം."
|
msgstr "സന്ദേശ ട്രേയുടെ ദൃശ്യത ടൊഗ്ഗിള് ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
|
||||||
msgid "Keybinding to focus the active notification"
|
msgid "Keybinding to focus the active notification"
|
||||||
msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്ഡിങ്"
|
msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്ഡിങ്"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
|
||||||
msgid "Keybinding to focus the active notification."
|
msgid "Keybinding to focus the active notification."
|
||||||
msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്ഡിങ്."
|
msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്ഡിങ്."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
|
||||||
msgid "Keybinding to toggle the screen recorder"
|
msgid "Keybinding to toggle the screen recorder"
|
||||||
msgstr "സ്ക്രീന് റിക്കോര്ഡര് ടൊഗ്ഗിള് ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
|
msgstr "സ്ക്രീന് റിക്കോര്ഡര് ടൊഗ്ഗിള് ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
|
||||||
msgid "Keybinding to start/stop the builtin screen recorder."
|
msgid "Keybinding to start/stop the builtin screen recorder."
|
||||||
msgstr ""
|
msgstr "ബിള്ട്ടിന് സ്ക്രീന് റിക്കോര്ഡര് തുടങ്ങുവാന്/നിര്ത്തുന്നതിനുള്ള കീക്കൂട്ടം."
|
||||||
"ബിള്ട്ടിന് സ്ക്രീന് റിക്കോര്ഡര് തുടങ്ങുവാന്/നിര്ത്തുന്നതിനുള്ള "
|
|
||||||
"കീക്കൂട്ടം."
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
|
||||||
msgid "Which keyboard to use"
|
msgid "Which keyboard to use"
|
||||||
msgstr "ഏതു് കീബോര്ഡ് ഉപയോഗിയ്ക്കണം"
|
msgstr "ഏതു് കീബോര്ഡ് ഉപയോഗിയ്ക്കണം"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
|
||||||
msgid "The type of keyboard to use."
|
msgid "The type of keyboard to use."
|
||||||
msgstr "ഏതു് തരം കീബോര്ഡ് ഉപയോഗിയ്ക്കണം."
|
msgstr "ഏതു് തരം കീബോര്ഡ് ഉപയോഗിയ്ക്കണം."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
|
||||||
msgid "Framerate used for recording screencasts."
|
msgid "Framerate used for recording screencasts."
|
||||||
msgstr "സ്ക്രീന്കാസ്റ്റുകള് റിക്കോര്ഡ് ചെയ്യുന്നതിനുള്ള ഫ്രെയിം റേറ്റ്."
|
msgstr "സ്ക്രീന്കാസ്റ്റുകള് റിക്കോര്ഡ് ചെയ്യുന്നതിനുള്ള ഫ്രെയിം റേറ്റ്."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
|
||||||
msgid ""
|
msgid ""
|
||||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||||
"screencast recorder in frames-per-second."
|
"screencast recorder in frames-per-second."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഗ്നോം ഷെല്ലിന്റെ സ്ക്രീന്കാസ്റ്റ് റിക്കോര്ഡര് റീക്കോര്ഡ് ചെയ്തിട്ടുള്ള "
|
"ഗ്നോം ഷെല്ലിന്റെ സ്ക്രീന്കാസ്റ്റ് റിക്കോര്ഡര് റീക്കോര്ഡ് ചെയ്തിട്ടുള്ള സ്ക്രീന്കാസ്റ്റിന്റെ "
|
||||||
"സ്ക്രീന്കാസ്റ്റിന്റെ "
|
|
||||||
"ഫ്രെയിംറേറ്റ്, ഒരു സെക്കന്ഡില് ഒരു ഫ്രെയിം."
|
"ഫ്രെയിംറേറ്റ്, ഒരു സെക്കന്ഡില് ഒരു ഫ്രെയിം."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
|
||||||
msgid "The gstreamer pipeline used to encode the screencast"
|
msgid "The gstreamer pipeline used to encode the screencast"
|
||||||
msgstr "സ്ക്രീന്കാസ്റ്റ് എന്കോഡ് ചെയ്യുന്നതിനുള്ള gstreamer പൈപ്പ്ലൈന്"
|
msgstr "സ്ക്രീന്കാസ്റ്റ് എന്കോഡ് ചെയ്യുന്നതിനുള്ള gstreamer പൈപ്പ്ലൈന്"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
|
||||||
#, no-c-format
|
#, no-c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||||
@ -296,78 +288,68 @@ msgid ""
|
|||||||
"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
|
"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
|
||||||
"used as a placeholder for a guess at the optimal thread count on the system."
|
"used as a placeholder for a guess at the optimal thread count on the system."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"റിക്കോര്ഡിങുകള് എന്കോഡ് ചെയ്യുന്നതിനായി GStreamer പൈപ്പ് ലൈന് "
|
"റിക്കോര്ഡിങുകള് എന്കോഡ് ചെയ്യുന്നതിനായി GStreamer പൈപ്പ് ലൈന് ഉപയോഗിയ്ക്കുന്നു. gst-launch-"
|
||||||
"ഉപയോഗിയ്ക്കുന്നു. gst-launch-"
|
"നുള്ള സിന്റാക്സ് ഉപയോഗിയ്ക്കുന്നു. കാലിയായി സജ്ജമാക്കുമ്പോള് കാലിയാകുന്നു.ഇതു് നിലവില് 'vp8enc "
|
||||||
"നുള്ള സിന്റാക്സ് ഉപയോഗിയ്ക്കുന്നു. കാലിയായി സജ്ജമാക്കുമ്പോള് "
|
|
||||||
"കാലിയാകുന്നു.ഇതു് നിലവില് 'vp8enc "
|
|
||||||
"min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! "
|
"min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! "
|
||||||
"queue ! webmmux' ആകുന്നുസ WEBM VP8 കോഡ് ഉപയോഗിച്ചു് റിക്കോര്ഡ് ചെയ്യുന്നു."
|
"queue ! webmmux' ആകുന്നുസ WEBM VP8 കോഡ് ഉപയോഗിച്ചു് റിക്കോര്ഡ് ചെയ്യുന്നു."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
|
||||||
msgid "File extension used for storing the screencast"
|
msgid "File extension used for storing the screencast"
|
||||||
msgstr "സ്ക്രീന്കാസ്റ്റ് സൂക്ഷിയ്ക്കുന്നതിനുള്ള ഫയല് എക്സ്റ്റെന്ഷന്"
|
msgstr "സ്ക്രീന്കാസ്റ്റ് സൂക്ഷിയ്ക്കുന്നതിനുള്ള ഫയല് എക്സ്റ്റെന്ഷന്"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
|
||||||
msgid ""
|
msgid ""
|
||||||
"The filename for recorded screencasts will be a unique filename based on the "
|
"The filename for recorded screencasts will be a unique filename based on the "
|
||||||
"current date, and use this extension. It should be changed when recording to "
|
"current date, and use this extension. It should be changed when recording to "
|
||||||
"a different container format."
|
"a different container format."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"റിക്കോര്ഡ് ചെയ്ത സ്ക്രീന്കാസ്റ്റുകള്ക്കുള്ള ഫയല്നാമം നിലവിലുള്ള തീയതി, "
|
"റിക്കോര്ഡ് ചെയ്ത സ്ക്രീന്കാസ്റ്റുകള്ക്കുള്ള ഫയല്നാമം നിലവിലുള്ള തീയതി, എക്സ്റ്റെന്ഷന് എന്നിവ "
|
||||||
"എക്സ്റ്റെന്ഷന് എന്നിവ "
|
"അനുസരിച്ചാകുന്നു. മറ്റൊരു ശൈലിയിലേക്കു് റിക്കോര്ഡ് ചെയ്യുമ്പോള് ഇതു് മാറ്റണം."
|
||||||
"അനുസരിച്ചാകുന്നു. മറ്റൊരു ശൈലിയിലേക്കു് റിക്കോര്ഡ് ചെയ്യുമ്പോള് ഇതു് "
|
|
||||||
"മാറ്റണം."
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
|
||||||
msgid "The application icon mode."
|
msgid "The application icon mode."
|
||||||
msgstr "പ്രയോഗത്തിന്റെ ഐക്കണ് മോഡ്."
|
msgstr "പ്രയോഗത്തിന്റെ ഐക്കണ് മോഡ്."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
|
||||||
msgid ""
|
msgid ""
|
||||||
"Configures how the windows are shown in the switcher. Valid possibilities "
|
"Configures how the windows are shown in the switcher. Valid possibilities "
|
||||||
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
||||||
"only' (shows only the application icon) or 'both'."
|
"only' (shows only the application icon) or 'both'."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"സ്വിച്ചറില് ജാലകങ്ങള് എങ്ങനെ കാണിയ്ക്കുന്നു എന്നു് ക്രമീകരിയ്ക്കുന്നു. "
|
"സ്വിച്ചറില് ജാലകങ്ങള് എങ്ങനെ കാണിയ്ക്കുന്നു എന്നു് ക്രമീകരിയ്ക്കുന്നു. ശരിയായ സാധ്യതകള്: "
|
||||||
"ശരിയായ "
|
"'thumbnail-only' (ജാലകത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു), 'app-icon-"
|
||||||
"സാധ്യതകള്: 'thumbnail-only' (ജാലകത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു), "
|
|
||||||
"'app-icon-"
|
|
||||||
"only' (പ്രയോഗത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു) അല്ലെങ്കില് 'both'."
|
"only' (പ്രയോഗത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു) അല്ലെങ്കില് 'both'."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
|
||||||
msgid "Attach modal dialog to the parent window"
|
msgid "Attach modal dialog to the parent window"
|
||||||
msgstr "പേരന്റ് ജാലകത്തിലേക്കു് ഡയലോഗ് ചേര്ക്കുക"
|
msgstr "പേരന്റ് ജാലകത്തിലേക്കു് ഡയലോഗ് ചേര്ക്കുക"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
|
||||||
msgid ""
|
msgid ""
|
||||||
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
|
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
|
||||||
msgstr ""
|
msgstr "ഗ്നോം ഷെല് പ്രവര്ത്തിയ്ക്കുമ്പോള് org.gnome.mutter-ലുള്ള കീ ഈ കീ തിരുത്തിയെഴുതുന്നു."
|
||||||
"ഗ്നോം ഷെല് പ്രവര്ത്തിയ്ക്കുമ്പോള് org.gnome.mutter-ലുള്ള കീ ഈ കീ "
|
|
||||||
"തിരുത്തിയെഴുതുന്നു."
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
|
||||||
msgid "Arrangement of buttons on the titlebar"
|
msgid "Arrangement of buttons on the titlebar"
|
||||||
msgstr "തലക്കെട്ടിനുള്ള ബാറില് ബട്ടണുകളുടെ ക്രമീകരണം"
|
msgstr "തലക്കെട്ടിനുള്ള ബാറില് ബട്ടണുകളുടെ ക്രമീകരണം"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
|
||||||
msgid ""
|
msgid ""
|
||||||
"This key overrides the key in org.gnome.desktop.wm.preferences when running "
|
"This key overrides the key in org.gnome.desktop.wm.preferences when running "
|
||||||
"GNOME Shell."
|
"GNOME Shell."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഗ്നോം ഷെല് പ്രവര്ത്തിയ്ക്കുമ്പോള് org.gnome.desktop.wm.preferences-ലുള്ള "
|
"ഗ്നോം ഷെല് പ്രവര്ത്തിയ്ക്കുമ്പോള് org.gnome.desktop.wm.preferences-ലുള്ള കീ ഈ കീ "
|
||||||
"കീ ഈ കീ തിരുത്തിയെഴുതുന്നു."
|
"തിരുത്തിയെഴുതുന്നു."
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
|
||||||
msgid "Enable edge tiling when dropping windows on screen edges"
|
msgid "Enable edge tiling when dropping windows on screen edges"
|
||||||
msgstr ""
|
msgstr "സ്ക്രീന് കോണുകളില് ജാലകങ്ങള് എത്തിയ്ക്കുമ്പോള് കോണ് ചരിയ്ക്കുന്നതിനായി പ്രവര്ത്തന സജ്ജമാക്കുക"
|
||||||
"സ്ക്രീന് കോണുകളില് ജാലകങ്ങള് എത്തിയ്ക്കുമ്പോള് കോണ് ചരിയ്ക്കുന്നതിനായി "
|
|
||||||
"പ്രവര്ത്തന സജ്ജമാക്കുക"
|
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:49
|
||||||
msgid "Workspaces are managed dynamically"
|
msgid "Workspaces are managed dynamically"
|
||||||
msgstr "പണിയിടങ്ങള് ഡയനാമിക്കായി കൈകാര്യം ചെയ്തിരിക്കുന്നു"
|
msgstr "പണിയിടങ്ങള് ഡയനാമിക്കായി കൈകാര്യം ചെയ്തിരിക്കുന്നു"
|
||||||
|
|
||||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
|
#: ../data/org.gnome.shell.gschema.xml.in.in.h:50
|
||||||
msgid "Workspaces only on primary monitor"
|
msgid "Workspaces only on primary monitor"
|
||||||
msgstr "പ്രധാന മോണിറ്ററില് മാത്രം പണിയിടങ്ങള്"
|
msgstr "പ്രധാന മോണിറ്ററില് മാത്രം പണിയിടങ്ങള്"
|
||||||
|
|
||||||
@ -382,12 +364,9 @@ msgstr "എക്സ്റ്റെന്ഷന്"
|
|||||||
|
|
||||||
#: ../js/extensionPrefs/main.js:189
|
#: ../js/extensionPrefs/main.js:189
|
||||||
msgid "Select an extension to configure using the combobox above."
|
msgid "Select an extension to configure using the combobox above."
|
||||||
msgstr ""
|
msgstr "മുകളിലുള്ള കോമ്പോ ബോക്സ് ഉപയോഗിച്ചു് ക്രമീകരിയ്ക്കുന്നതിനുള്ളൊരു എക്സ്റ്റെന്ഷന് തെരഞ്ഞെടുക്കുക."
|
||||||
"മുകളിലുള്ള കോമ്പോ ബോക്സ് ഉപയോഗിച്ചു് ക്രമീകരിയ്ക്കുന്നതിനുള്ളൊരു "
|
|
||||||
"എക്സ്റ്റെന്ഷന് തെരഞ്ഞെടുക്കുക."
|
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:405
|
#: ../js/gdm/loginDialog.js:405
|
||||||
#| msgid "Session..."
|
|
||||||
msgid "Session…"
|
msgid "Session…"
|
||||||
msgstr "പ്രവര്ത്തനവേള..."
|
msgstr "പ്രവര്ത്തനവേള..."
|
||||||
|
|
||||||
@ -398,32 +377,32 @@ msgstr "പ്രവര്ത്തനവേള..."
|
|||||||
msgid "Not listed?"
|
msgid "Not listed?"
|
||||||
msgstr "ലഭ്യമല്ലേ?"
|
msgstr "ലഭ്യമല്ലേ?"
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:786 ../js/ui/components/networkAgent.js:137
|
#: ../js/gdm/loginDialog.js:787 ../js/ui/components/networkAgent.js:137
|
||||||
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
|
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:376
|
||||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||||
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:99
|
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:100
|
||||||
#: ../js/ui/userMenu.js:938
|
#: ../js/ui/userMenu.js:938
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "വേണ്ട"
|
msgstr "വേണ്ട"
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:802
|
#: ../js/gdm/loginDialog.js:803
|
||||||
msgctxt "button"
|
msgctxt "button"
|
||||||
msgid "Sign In"
|
msgid "Sign In"
|
||||||
msgstr "അകത്തുകയറുക"
|
msgstr "അകത്തുകയറുക"
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:802
|
#: ../js/gdm/loginDialog.js:803
|
||||||
msgid "Next"
|
msgid "Next"
|
||||||
msgstr "അടുത്തത്"
|
msgstr "അടുത്തത്"
|
||||||
|
|
||||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||||
#. is not visible here since we only care about phase2 authentication
|
#. is not visible here since we only care about phase2 authentication
|
||||||
#. (and don't even care of which one)
|
#. (and don't even care of which one)
|
||||||
#: ../js/gdm/loginDialog.js:917 ../js/ui/components/networkAgent.js:260
|
#: ../js/gdm/loginDialog.js:918 ../js/ui/components/networkAgent.js:260
|
||||||
#: ../js/ui/components/networkAgent.js:278
|
#: ../js/ui/components/networkAgent.js:278
|
||||||
msgid "Username: "
|
msgid "Username: "
|
||||||
msgstr "ഉപയോക്തൃ നാമം: "
|
msgstr "ഉപയോക്തൃ നാമം: "
|
||||||
|
|
||||||
#: ../js/gdm/loginDialog.js:1173
|
#: ../js/gdm/loginDialog.js:1174
|
||||||
msgid "Login Window"
|
msgid "Login Window"
|
||||||
msgstr "പ്രവേശന ജാലകം"
|
msgstr "പ്രവേശന ജാലകം"
|
||||||
|
|
||||||
@ -476,23 +455,23 @@ msgstr "ആജ്ഞ പ്രാവര്ത്തികമാക്കാ
|
|||||||
msgid "Execution of '%s' failed:"
|
msgid "Execution of '%s' failed:"
|
||||||
msgstr "'%s' നടപ്പിലാക്കുന്നതില് പരാജയപ്പെട്ടു:"
|
msgstr "'%s' നടപ്പിലാക്കുന്നതില് പരാജയപ്പെട്ടു:"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:349
|
#: ../js/ui/appDisplay.js:351
|
||||||
msgid "Frequent"
|
msgid "Frequent"
|
||||||
msgstr "ഇടയ്ക്കിടെ"
|
msgstr "ഇടയ്ക്കിടെ"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:356
|
#: ../js/ui/appDisplay.js:358
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "എല്ലാം"
|
msgstr "എല്ലാം"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:914
|
#: ../js/ui/appDisplay.js:916
|
||||||
msgid "New Window"
|
msgid "New Window"
|
||||||
msgstr "പുതിയ വിന്ഡോ"
|
msgstr "പുതിയ വിന്ഡോ"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:917 ../js/ui/dash.js:284
|
#: ../js/ui/appDisplay.js:919 ../js/ui/dash.js:284
|
||||||
msgid "Remove from Favorites"
|
msgid "Remove from Favorites"
|
||||||
msgstr "ഇഷ്ടപ്പെട്ടവയില് നിന്നും നീക്കം ചെയ്യുക"
|
msgstr "ഇഷ്ടപ്പെട്ടവയില് നിന്നും നീക്കം ചെയ്യുക"
|
||||||
|
|
||||||
#: ../js/ui/appDisplay.js:918
|
#: ../js/ui/appDisplay.js:920
|
||||||
msgid "Add to Favorites"
|
msgid "Add to Favorites"
|
||||||
msgstr "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്ക്കുക"
|
msgstr "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്ക്കുക"
|
||||||
|
|
||||||
@ -529,7 +508,7 @@ msgctxt "event list time"
|
|||||||
msgid "%H\\u2236%M"
|
msgid "%H\\u2236%M"
|
||||||
msgstr "%H\\u2236%M"
|
msgstr "%H\\u2236%M"
|
||||||
|
|
||||||
#. Transators: Shown in calendar event list, if 12h format,
|
#. Translators: Shown in calendar event list, if 12h format,
|
||||||
#. \u2236 is a ratio character, similar to : and \u2009 is
|
#. \u2236 is a ratio character, similar to : and \u2009 is
|
||||||
#. a thin space
|
#. a thin space
|
||||||
#: ../js/ui/calendar.js:77
|
#: ../js/ui/calendar.js:77
|
||||||
@ -732,8 +711,7 @@ msgid ""
|
|||||||
"Passwords or encryption keys are required to access the wireless network "
|
"Passwords or encryption keys are required to access the wireless network "
|
||||||
"'%s'."
|
"'%s'."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"വയര്ലെസ് നെറ്റ്വര്ക്ക് '%s'-ലേക്ക് പ്രവേശിക്കുന്നതിനായി രഹസ്യവാക്കുകള് "
|
"വയര്ലെസ് നെറ്റ്വര്ക്ക് '%s'-ലേക്ക് പ്രവേശിക്കുന്നതിനായി രഹസ്യവാക്കുകള് അല്ലെങ്കില് എന്ക്രിപ്ഷന് കീകള് "
|
||||||
"അല്ലെങ്കില് എന്ക്രിപ്ഷന് കീകള് "
|
|
||||||
"ആവശ്യമുണ്ടു്."
|
"ആവശ്യമുണ്ടു്."
|
||||||
|
|
||||||
#: ../js/ui/components/networkAgent.js:314
|
#: ../js/ui/components/networkAgent.js:314
|
||||||
@ -790,7 +768,7 @@ msgid "Sorry, that didn't work. Please try again."
|
|||||||
msgstr "ക്ഷമിക്കണം, അതു ശരിയല്ല. ദയവായി വീണ്ടും ശ്രമിക്കുക."
|
msgstr "ക്ഷമിക്കണം, അതു ശരിയല്ല. ദയവായി വീണ്ടും ശ്രമിക്കുക."
|
||||||
|
|
||||||
#. Translators: this is a filename used for screencast recording
|
#. Translators: this is a filename used for screencast recording
|
||||||
#: ../js/ui/components/recorder.js:48
|
#: ../js/ui/components/recorder.js:47
|
||||||
#, no-c-format
|
#, no-c-format
|
||||||
msgid "Screencast from %d %t"
|
msgid "Screencast from %d %t"
|
||||||
msgstr "%d-ല് നിന്നുള്ള സ്ക്രീന്കാസ്റ്റ്, %t-ല്"
|
msgstr "%d-ല് നിന്നുള്ള സ്ക്രീന്കാസ്റ്റ്, %t-ല്"
|
||||||
@ -913,8 +891,7 @@ msgstr "%s നിങ്ങള്ക്കു് %s അയച്ചിരി
|
|||||||
#: ../js/ui/components/telepathyClient.js:1206
|
#: ../js/ui/components/telepathyClient.js:1206
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s would like permission to see when you are online"
|
msgid "%s would like permission to see when you are online"
|
||||||
msgstr ""
|
msgstr "നിങ്ങള് ഓണ്ലൈന് ആകുമ്പോള് കാണുന്നതിനുള്ള അനുമതി %s-നു് ആവശ്യമുണ്ടു്"
|
||||||
"നിങ്ങള് ഓണ്ലൈന് ആകുമ്പോള് കാണുന്നതിനുള്ള അനുമതി %s-നു് ആവശ്യമുണ്ടു്"
|
|
||||||
|
|
||||||
#: ../js/ui/components/telepathyClient.js:1298
|
#: ../js/ui/components/telepathyClient.js:1298
|
||||||
msgid "Network error"
|
msgid "Network error"
|
||||||
@ -987,9 +964,7 @@ msgstr "ഈ അക്കൌണ്ട് നിലവില് സര്വ
|
|||||||
#: ../js/ui/components/telepathyClient.js:1332
|
#: ../js/ui/components/telepathyClient.js:1332
|
||||||
msgid ""
|
msgid ""
|
||||||
"Connection has been replaced by a new connection using the same resource"
|
"Connection has been replaced by a new connection using the same resource"
|
||||||
msgstr ""
|
msgstr "അതേ ശ്രോതസ്സ് ഉപയോഗിച്ചു് ഒരു പുതിയ കണക്ഷന് ഉപയോഗിച്ചു് ഈ കണക്ഷന് മാറ്റിസ്ഥാപിയ്ക്കുന്നു"
|
||||||
"അതേ ശ്രോതസ്സ് ഉപയോഗിച്ചു് ഒരു പുതിയ കണക്ഷന് ഉപയോഗിച്ചു് ഈ കണക്ഷന് "
|
|
||||||
"മാറ്റിസ്ഥാപിയ്ക്കുന്നു"
|
|
||||||
|
|
||||||
#: ../js/ui/components/telepathyClient.js:1334
|
#: ../js/ui/components/telepathyClient.js:1334
|
||||||
msgid "The account already exists on the server"
|
msgid "The account already exists on the server"
|
||||||
@ -1006,17 +981,14 @@ msgstr "സമ്മതപത്രം വീണ്ടും ആവശ്യപ
|
|||||||
#: ../js/ui/components/telepathyClient.js:1340
|
#: ../js/ui/components/telepathyClient.js:1340
|
||||||
msgid ""
|
msgid ""
|
||||||
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
|
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
|
||||||
msgstr ""
|
msgstr "സമ്മതപത്രം സുരക്ഷിതമല്ലാത്തൊരു സിഫര് ആല്ഗോരിഥം ഉപയോഗിയ്ക്കുന്നു അല്ലെങ്കില് ഉചിതമല്ല"
|
||||||
"സമ്മതപത്രം സുരക്ഷിതമല്ലാത്തൊരു സിഫര് ആല്ഗോരിഥം ഉപയോഗിയ്ക്കുന്നു "
|
|
||||||
"അല്ലെങ്കില് ഉചിതമല്ല"
|
|
||||||
|
|
||||||
#: ../js/ui/components/telepathyClient.js:1342
|
#: ../js/ui/components/telepathyClient.js:1342
|
||||||
msgid ""
|
msgid ""
|
||||||
"The length of the server certificate, or the depth of the server certificate "
|
"The length of the server certificate, or the depth of the server certificate "
|
||||||
"chain, exceed the limits imposed by the cryptography library"
|
"chain, exceed the limits imposed by the cryptography library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"സര്വറിന്റെ സമ്മതപത്രത്തിന്റെ വ്യാപ്തി, അല്ലെങ്കില് സര്വര് സമ്മതപത്ര "
|
"സര്വറിന്റെ സമ്മതപത്രത്തിന്റെ വ്യാപ്തി, അല്ലെങ്കില് സര്വര് സമ്മതപത്ര ചെയിന്റെ വ്യാപ്തി, എന്നിവ "
|
||||||
"ചെയിന്റെ വ്യാപ്തി, എന്നിവ "
|
|
||||||
"പരിധിയില് കൂടുന്നു"
|
"പരിധിയില് കൂടുന്നു"
|
||||||
|
|
||||||
#: ../js/ui/components/telepathyClient.js:1344
|
#: ../js/ui/components/telepathyClient.js:1344
|
||||||
@ -1085,8 +1057,7 @@ msgstr "പുറത്ത് കടക്കുക"
|
|||||||
#: ../js/ui/endSessionDialog.js:65
|
#: ../js/ui/endSessionDialog.js:65
|
||||||
msgid "Click Log Out to quit these applications and log out of the system."
|
msgid "Click Log Out to quit these applications and log out of the system."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഈ പ്രയോഗങ്ങളില് നിന്നും പുറത്തു് കടക്കുന്നതിനായി പുറത്തു കടക്കുക ക്ലിക്ക് "
|
"ഈ പ്രയോഗങ്ങളില് നിന്നും പുറത്തു് കടക്കുന്നതിനായി പുറത്തു കടക്കുക ക്ലിക്ക് ചെയ്തു് സിസ്റ്റത്തില് നിന്നും "
|
||||||
"ചെയ്തു് സിസ്റ്റത്തില് നിന്നും "
|
|
||||||
"പുറത്തു് കടക്കുക."
|
"പുറത്തു് കടക്കുക."
|
||||||
|
|
||||||
#: ../js/ui/endSessionDialog.js:67
|
#: ../js/ui/endSessionDialog.js:67
|
||||||
@ -1120,8 +1091,7 @@ msgstr "നിര്ത്തുക"
|
|||||||
#: ../js/ui/endSessionDialog.js:84
|
#: ../js/ui/endSessionDialog.js:84
|
||||||
msgid "Click Power Off to quit these applications and power off the system."
|
msgid "Click Power Off to quit these applications and power off the system."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ഈ പ്രയോഗങ്ങളില് നിന്നും പുറത്തു് കടക്കുന്നതിനായി പവര് ഓഫ് ചെയ്യുക ക്ലിക്ക് "
|
"ഈ പ്രയോഗങ്ങളില് നിന്നും പുറത്തു് കടക്കുന്നതിനായി പവര് ഓഫ് ചെയ്യുക ക്ലിക്ക് ചെയ്തു സിസ്റ്റിന്റെ പവര് "
|
||||||
"ചെയ്തു സിസ്റ്റിന്റെ പവര് "
|
|
||||||
"ഓഫ് ചെയ്യുക."
|
"ഓഫ് ചെയ്യുക."
|
||||||
|
|
||||||
#: ../js/ui/endSessionDialog.js:86
|
#: ../js/ui/endSessionDialog.js:86
|
||||||
@ -1152,8 +1122,7 @@ msgstr "പുനരാരംഭിക്കുക"
|
|||||||
|
|
||||||
#: ../js/ui/endSessionDialog.js:101
|
#: ../js/ui/endSessionDialog.js:101
|
||||||
msgid "Click Restart to quit these applications and restart the system."
|
msgid "Click Restart to quit these applications and restart the system."
|
||||||
msgstr ""
|
msgstr "ഈ പ്രയോഗങ്ങള് നിറുത്തി സിസ്റ്റം പുനരാരംഭിക്കുവാന് പുനരാരംഭിക്കൂ അമര്ത്തുക"
|
||||||
"ഈ പ്രയോഗങ്ങള് നിറുത്തി സിസ്റ്റം പുനരാരംഭിക്കുവാന് പുനരാരംഭിക്കൂ അമര്ത്തുക"
|
|
||||||
|
|
||||||
#: ../js/ui/endSessionDialog.js:103
|
#: ../js/ui/endSessionDialog.js:103
|
||||||
#, c-format
|
#, c-format
|
||||||
@ -1173,9 +1142,7 @@ msgstr "ഇന്സ്റ്റോള്"
|
|||||||
#: ../js/ui/extensionDownloader.js:204
|
#: ../js/ui/extensionDownloader.js:204
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Download and install '%s' from extensions.gnome.org?"
|
msgid "Download and install '%s' from extensions.gnome.org?"
|
||||||
msgstr ""
|
msgstr "extensions.gnome.org ഇല് നിന്നും '%s' ഡൗണ്ലോട് ചെയ്ത് ഇന്സ്റ്റോള് ചെയ്യേണമോ?"
|
||||||
"extensions.gnome.org ഇല് നിന്നും '%s' ഡൗണ്ലോട് ചെയ്ത് ഇന്സ്റ്റോള് "
|
|
||||||
"ചെയ്യേണമോ?"
|
|
||||||
|
|
||||||
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
|
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
|
||||||
#: ../js/ui/status/power.js:211
|
#: ../js/ui/status/power.js:211
|
||||||
@ -1204,7 +1171,9 @@ msgstr "പിശകുകള് കാണിക്കുക"
|
|||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "പ്രവര്ത്തനക്ഷമമാക്കി"
|
msgstr "പ്രവര്ത്തനക്ഷമമാക്കി"
|
||||||
|
|
||||||
#: ../js/ui/lookingGlass.js:769
|
#. translators:
|
||||||
|
#. * The device has been disabled
|
||||||
|
#: ../js/ui/lookingGlass.js:769 ../src/gvc/gvc-mixer-control.c:1830
|
||||||
msgid "Disabled"
|
msgid "Disabled"
|
||||||
msgstr "പ്രവര്ത്തനരഹിതമാക്കി"
|
msgstr "പ്രവര്ത്തനരഹിതമാക്കി"
|
||||||
|
|
||||||
@ -1237,7 +1206,6 @@ msgid "Remove"
|
|||||||
msgstr "നീക്കം ചെയ്യുക"
|
msgstr "നീക്കം ചെയ്യുക"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:1501
|
#: ../js/ui/messageTray.js:1501
|
||||||
#| msgid "No Messages"
|
|
||||||
msgid "Clear Messages"
|
msgid "Clear Messages"
|
||||||
msgstr "സന്ദേശങ്ങള് വെടിപ്പാക്കുക"
|
msgstr "സന്ദേശങ്ങള് വെടിപ്പാക്കുക"
|
||||||
|
|
||||||
@ -1245,35 +1213,35 @@ msgstr "സന്ദേശങ്ങള് വെടിപ്പാക്ക
|
|||||||
msgid "Notification Settings"
|
msgid "Notification Settings"
|
||||||
msgstr "അറിയിപ്പു് ക്രമീകരണങ്ങള്"
|
msgstr "അറിയിപ്പു് ക്രമീകരണങ്ങള്"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:1709
|
#: ../js/ui/messageTray.js:1710
|
||||||
msgid "No Messages"
|
msgid "No Messages"
|
||||||
msgstr "സന്ദേശങ്ങളില്ല"
|
msgstr "സന്ദേശങ്ങളില്ല"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:1782
|
#: ../js/ui/messageTray.js:1783
|
||||||
msgid "Message Tray"
|
msgid "Message Tray"
|
||||||
msgstr "സന്ദേശത്തിന്റെ ട്രേ"
|
msgstr "സന്ദേശത്തിന്റെ ട്രേ"
|
||||||
|
|
||||||
#: ../js/ui/messageTray.js:2810
|
#: ../js/ui/messageTray.js:2801
|
||||||
msgid "System Information"
|
msgid "System Information"
|
||||||
msgstr "സിസ്റ്റത്തെക്കുറിച്ചുള്ള വിവരം"
|
msgstr "സിസ്റ്റത്തെക്കുറിച്ചുള്ള വിവരം"
|
||||||
|
|
||||||
#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:374
|
#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:378
|
||||||
msgctxt "program"
|
msgctxt "program"
|
||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr "അജ്ഞാതം"
|
msgstr "അജ്ഞാതം"
|
||||||
|
|
||||||
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:149
|
#: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:149
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d new message"
|
msgid "%d new message"
|
||||||
msgid_plural "%d new messages"
|
msgid_plural "%d new messages"
|
||||||
msgstr[0] "%d പുതിയ സന്ദേശം"
|
msgstr[0] "%d പുതിയ സന്ദേശം"
|
||||||
msgstr[1] "%d പുതിയ സന്ദേശങ്ങള്"
|
msgstr[1] "%d പുതിയ സന്ദേശങ്ങള്"
|
||||||
|
|
||||||
#: ../js/ui/overview.js:84
|
#: ../js/ui/overview.js:82
|
||||||
msgid "Undo"
|
msgid "Undo"
|
||||||
msgstr "വേണ്ട"
|
msgstr "വേണ്ട"
|
||||||
|
|
||||||
#: ../js/ui/overview.js:129
|
#: ../js/ui/overview.js:127
|
||||||
msgid "Overview"
|
msgid "Overview"
|
||||||
msgstr "അവലോകനം"
|
msgstr "അവലോകനം"
|
||||||
|
|
||||||
@ -1281,22 +1249,21 @@ msgstr "അവലോകനം"
|
|||||||
#. in the search entry when no search is
|
#. in the search entry when no search is
|
||||||
#. active; it should not exceed ~30
|
#. active; it should not exceed ~30
|
||||||
#. characters.
|
#. characters.
|
||||||
#: ../js/ui/overview.js:271
|
#: ../js/ui/overview.js:260
|
||||||
#| msgid "Type to search..."
|
|
||||||
msgid "Type to search…"
|
msgid "Type to search…"
|
||||||
msgstr "തെരയുന്നതിനായി ടൈപ്പ് ചെയ്യുക..."
|
msgstr "തെരയുന്നതിനായി ടൈപ്പ് ചെയ്യുക..."
|
||||||
|
|
||||||
#: ../js/ui/panel.js:612
|
#: ../js/ui/panel.js:641
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
msgstr "നിര്ത്തുക"
|
msgstr "നിര്ത്തുക"
|
||||||
|
|
||||||
#. Translators: If there is no suitable word for "Activities"
|
#. Translators: If there is no suitable word for "Activities"
|
||||||
#. in your language, you can use the word for "Overview".
|
#. in your language, you can use the word for "Overview".
|
||||||
#: ../js/ui/panel.js:636
|
#: ../js/ui/panel.js:692
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "പ്രവര്ത്തനങ്ങള്"
|
msgstr "പ്രവര്ത്തനങ്ങള്"
|
||||||
|
|
||||||
#: ../js/ui/panel.js:933
|
#: ../js/ui/panel.js:989
|
||||||
msgid "Top Bar"
|
msgid "Top Bar"
|
||||||
msgstr "മുകളിലുള്ള ബാര്"
|
msgstr "മുകളിലുള്ള ബാര്"
|
||||||
|
|
||||||
@ -1305,15 +1272,15 @@ msgstr "മുകളിലുള്ള ബാര്"
|
|||||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||||
#. switches containing "◯" and "|"). Other values will
|
#. switches containing "◯" and "|"). Other values will
|
||||||
#. simply result in invisible toggle switches.
|
#. simply result in invisible toggle switches.
|
||||||
#: ../js/ui/popupMenu.js:727
|
#: ../js/ui/popupMenu.js:718
|
||||||
msgid "toggle-switch-us"
|
msgid "toggle-switch-us"
|
||||||
msgstr "toggle-switch-us"
|
msgstr "toggle-switch-us"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:73
|
#: ../js/ui/runDialog.js:74
|
||||||
msgid "Enter a Command"
|
msgid "Enter a Command"
|
||||||
msgstr "ഒരു കമാന്ഡ് നല്കുക"
|
msgstr "ഒരു കമാന്ഡ് നല്കുക"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:109
|
#: ../js/ui/runDialog.js:110
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr "അടക്കുക"
|
msgstr "അടക്കുക"
|
||||||
|
|
||||||
@ -1334,7 +1301,7 @@ msgstr[1] "%d പുതിയ അറിയിപ്പുകള്"
|
|||||||
msgid "Lock"
|
msgid "Lock"
|
||||||
msgstr "പൂട്ടുക"
|
msgstr "പൂട്ടുക"
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:637
|
#: ../js/ui/screenShield.js:641
|
||||||
msgid "GNOME needs to lock the screen"
|
msgid "GNOME needs to lock the screen"
|
||||||
msgstr "ഗ്നോമിന് സ്ക്രീന് പൂട്ടണം"
|
msgstr "ഗ്നോമിന് സ്ക്രീന് പൂട്ടണം"
|
||||||
|
|
||||||
@ -1345,17 +1312,15 @@ msgstr "ഗ്നോമിന് സ്ക്രീന് പൂട്ടണ
|
|||||||
#.
|
#.
|
||||||
#. XXX: another option is to kick the user into the gdm login
|
#. XXX: another option is to kick the user into the gdm login
|
||||||
#. screen, where we're not affected by grabs
|
#. screen, where we're not affected by grabs
|
||||||
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1194
|
#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
|
||||||
#| msgid "Unable to connect to %s"
|
|
||||||
msgid "Unable to lock"
|
msgid "Unable to lock"
|
||||||
msgstr "പൂട്ടുവാന് സാധ്യമല്ല"
|
msgstr "പൂട്ടുവാന് സാധ്യമല്ല"
|
||||||
|
|
||||||
#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1195
|
#: ../js/ui/screenShield.js:763 ../js/ui/screenShield.js:1199
|
||||||
msgid "Lock was blocked by an application"
|
msgid "Lock was blocked by an application"
|
||||||
msgstr "പൂട്ടുന്ന സംവിധാനം ഒരു പ്രയോഗം തടസ്സപ്പെടുത്തിയിരിയ്ക്കുന്നു"
|
msgstr "പൂട്ടുന്ന സംവിധാനം ഒരു പ്രയോഗം തടസ്സപ്പെടുത്തിയിരിയ്ക്കുന്നു"
|
||||||
|
|
||||||
#: ../js/ui/searchDisplay.js:453
|
#: ../js/ui/searchDisplay.js:453
|
||||||
#| msgid "Searching..."
|
|
||||||
msgid "Searching…"
|
msgid "Searching…"
|
||||||
msgstr "തെരയുന്നു..."
|
msgstr "തെരയുന്നു..."
|
||||||
|
|
||||||
@ -1371,11 +1336,11 @@ msgstr "പകര്ത്തുക"
|
|||||||
msgid "Paste"
|
msgid "Paste"
|
||||||
msgstr "ഒട്ടിയ്ക്കുക"
|
msgstr "ഒട്ടിയ്ക്കുക"
|
||||||
|
|
||||||
#: ../js/ui/shellEntry.js:106
|
#: ../js/ui/shellEntry.js:101
|
||||||
msgid "Show Text"
|
msgid "Show Text"
|
||||||
msgstr "പദാവലി കാണിക്കുക"
|
msgstr "പദാവലി കാണിക്കുക"
|
||||||
|
|
||||||
#: ../js/ui/shellEntry.js:108
|
#: ../js/ui/shellEntry.js:103
|
||||||
msgid "Hide Text"
|
msgid "Hide Text"
|
||||||
msgstr "പദാവലി മറക്കുക"
|
msgstr "പദാവലി മറക്കുക"
|
||||||
|
|
||||||
@ -1387,7 +1352,7 @@ msgstr "രഹസ്യവാക്ക്"
|
|||||||
msgid "Remember Password"
|
msgid "Remember Password"
|
||||||
msgstr "രഹസ്യവാക്ക് ഓര്ത്തു് വയ്ക്കുക"
|
msgstr "രഹസ്യവാക്ക് ഓര്ത്തു് വയ്ക്കുക"
|
||||||
|
|
||||||
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:113
|
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:114
|
||||||
msgid "Unlock"
|
msgid "Unlock"
|
||||||
msgstr "പൂട്ട് തുറക്കുക"
|
msgstr "പൂട്ട് തുറക്കുക"
|
||||||
|
|
||||||
@ -1451,12 +1416,10 @@ msgid "Visibility"
|
|||||||
msgstr "കാഴ്ച"
|
msgstr "കാഴ്ച"
|
||||||
|
|
||||||
#: ../js/ui/status/bluetooth.js:59
|
#: ../js/ui/status/bluetooth.js:59
|
||||||
#| msgid "Send Files to Device..."
|
|
||||||
msgid "Send Files to Device…"
|
msgid "Send Files to Device…"
|
||||||
msgstr "ഡിവൈസിലേക്കു് ഫയലുകള് അയയ്ക്കുക..."
|
msgstr "ഡിവൈസിലേക്കു് ഫയലുകള് അയയ്ക്കുക..."
|
||||||
|
|
||||||
#: ../js/ui/status/bluetooth.js:60
|
#: ../js/ui/status/bluetooth.js:60
|
||||||
#| msgid "Set Up a New Device..."
|
|
||||||
msgid "Set Up a New Device…"
|
msgid "Set Up a New Device…"
|
||||||
msgstr "പുതിയൊരു ഡിവൈസ് സജ്ജമാക്കുക..."
|
msgstr "പുതിയൊരു ഡിവൈസ് സജ്ജമാക്കുക..."
|
||||||
|
|
||||||
@ -1483,7 +1446,6 @@ msgid "connecting..."
|
|||||||
msgstr "ബന്ധിപ്പിയ്ക്കുന്നു...."
|
msgstr "ബന്ധിപ്പിയ്ക്കുന്നു...."
|
||||||
|
|
||||||
#: ../js/ui/status/bluetooth.js:239
|
#: ../js/ui/status/bluetooth.js:239
|
||||||
#| msgid "Send Files..."
|
|
||||||
msgid "Send Files…"
|
msgid "Send Files…"
|
||||||
msgstr "ഫയലുകള് അയയ്ക്കുക..."
|
msgstr "ഫയലുകള് അയയ്ക്കുക..."
|
||||||
|
|
||||||
@ -1696,7 +1658,6 @@ msgstr "ഊര്ജ്ജ ക്രമീകരണങ്ങള്"
|
|||||||
#. 0 is reported when UPower does not have enough data
|
#. 0 is reported when UPower does not have enough data
|
||||||
#. to estimate battery life
|
#. to estimate battery life
|
||||||
#: ../js/ui/status/power.js:99
|
#: ../js/ui/status/power.js:99
|
||||||
#| msgid "Estimating..."
|
|
||||||
msgid "Estimating…"
|
msgid "Estimating…"
|
||||||
msgstr "കണക്കുകൂട്ടുന്നു..."
|
msgstr "കണക്കുകൂട്ടുന്നു..."
|
||||||
|
|
||||||
@ -1796,11 +1757,11 @@ msgstr "ഒച്ച"
|
|||||||
msgid "Microphone"
|
msgid "Microphone"
|
||||||
msgstr "മൈക്രോഫോണ്"
|
msgstr "മൈക്രോഫോണ്"
|
||||||
|
|
||||||
#: ../js/ui/unlockDialog.js:124
|
#: ../js/ui/unlockDialog.js:125
|
||||||
msgid "Log in as another user"
|
msgid "Log in as another user"
|
||||||
msgstr "മറ്റൊരു ഉപയോക്താവായി പ്രവേശിയ്ക്കുക"
|
msgstr "മറ്റൊരു ഉപയോക്താവായി പ്രവേശിയ്ക്കുക"
|
||||||
|
|
||||||
#: ../js/ui/unlockDialog.js:145
|
#: ../js/ui/unlockDialog.js:146
|
||||||
msgid "Unlock Window"
|
msgid "Unlock Window"
|
||||||
msgstr "ജാലകത്തിന്റെ പൂട്ടു തുറക്കുക"
|
msgstr "ജാലകത്തിന്റെ പൂട്ടു തുറക്കുക"
|
||||||
|
|
||||||
@ -1853,10 +1814,8 @@ msgid ""
|
|||||||
"Notifications are now disabled, including chat messages. Your online status "
|
"Notifications are now disabled, including chat messages. Your online status "
|
||||||
"has been adjusted to let others know that you might not see their messages."
|
"has been adjusted to let others know that you might not see their messages."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"ചാറ്റ് സന്ദേശങ്ങള് എന്ന പോലെ അറിയിപ്പുകള് പ്രവര്ത്തന രഹിതമാക്കുന്നു. "
|
"ചാറ്റ് സന്ദേശങ്ങള് എന്ന പോലെ അറിയിപ്പുകള് പ്രവര്ത്തന രഹിതമാക്കുന്നു. മറ്റുള്ളവരുടെ ചാറ്റ് സന്ദേശങ്ങള് "
|
||||||
"മറ്റുള്ളവരുടെ ചാറ്റ് സന്ദേശങ്ങള് "
|
"നിങ്ങള്ക്കു് കാണുവാന് സാധ്യമല്ല എന്നു് നിങ്ങളുടെ ഓണ്ലൈന് അവസ്ഥയില് വ്യക്തമാക്കുന്നു."
|
||||||
"നിങ്ങള്ക്കു് കാണുവാന് സാധ്യമല്ല എന്നു് നിങ്ങളുടെ ഓണ്ലൈന് അവസ്ഥയില് "
|
|
||||||
"വ്യക്തമാക്കുന്നു."
|
|
||||||
|
|
||||||
#: ../js/ui/userMenu.js:888
|
#: ../js/ui/userMenu.js:888
|
||||||
msgid "Other users are logged in."
|
msgid "Other users are logged in."
|
||||||
@ -1909,6 +1868,28 @@ msgstr "'%s' തയ്യാറാണ്"
|
|||||||
msgid "Evolution Calendar"
|
msgid "Evolution Calendar"
|
||||||
msgstr "ഇവല്യൂഷന് കലണ്ടര്"
|
msgstr "ഇവല്യൂഷന് കലണ്ടര്"
|
||||||
|
|
||||||
|
#. translators:
|
||||||
|
#. * The number of sound outputs on a particular device
|
||||||
|
#: ../src/gvc/gvc-mixer-control.c:1837
|
||||||
|
#, c-format
|
||||||
|
msgid "%u Output"
|
||||||
|
msgid_plural "%u Outputs"
|
||||||
|
msgstr[0] "%u ഔട്ട്പുട്ട്"
|
||||||
|
msgstr[1] "%u ഔട്ട്പുട്ടുകള്"
|
||||||
|
|
||||||
|
#. translators:
|
||||||
|
#. * The number of sound inputs on a particular device
|
||||||
|
#: ../src/gvc/gvc-mixer-control.c:1847
|
||||||
|
#, c-format
|
||||||
|
msgid "%u Input"
|
||||||
|
msgid_plural "%u Inputs"
|
||||||
|
msgstr[0] "%u ഇന്പുട്ട്"
|
||||||
|
msgstr[1] "%u ഇന്പുട്ടുകള്"
|
||||||
|
|
||||||
|
#: ../src/gvc/gvc-mixer-control.c:2371
|
||||||
|
msgid "System Sounds"
|
||||||
|
msgstr "സിസ്റ്റം ശബ്ദങ്ങള്"
|
||||||
|
|
||||||
#: ../src/main.c:347
|
#: ../src/main.c:347
|
||||||
msgid "Print version"
|
msgid "Print version"
|
||||||
msgstr "പ്രിന്റ് ചെയ്യുവാന് സാധിയ്ക്കുന്ന പതിപ്പു്"
|
msgstr "പ്രിന്റ് ചെയ്യുവാന് സാധിയ്ക്കുന്ന പതിപ്പു്"
|
||||||
@ -1925,7 +1906,7 @@ msgstr "ഒരു പ്രത്യേക മോഡ് ഉപയോഗിയ്
|
|||||||
msgid "List possible modes"
|
msgid "List possible modes"
|
||||||
msgstr "സാധ്യമായ മോഡുകള് ലഭ്യമാക്കുക"
|
msgstr "സാധ്യമായ മോഡുകള് ലഭ്യമാക്കുക"
|
||||||
|
|
||||||
#: ../src/shell-app.c:622
|
#: ../src/shell-app.c:626
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Failed to launch '%s'"
|
msgid "Failed to launch '%s'"
|
||||||
msgstr "'%s' ലഭ്യമാക്കുന്നതില് പരാജയം"
|
msgstr "'%s' ലഭ്യമാക്കുന്നതില് പരാജയം"
|
||||||
@ -1955,19 +1936,6 @@ msgstr "ഉപയോക്താവു് ആധികാരികത ഉറപ
|
|||||||
#~ msgid "More..."
|
#~ msgid "More..."
|
||||||
#~ msgstr "കൂടുതല്..."
|
#~ msgstr "കൂടുതല്..."
|
||||||
|
|
||||||
#~ msgid "%u Output"
|
|
||||||
#~ msgid_plural "%u Outputs"
|
|
||||||
#~ msgstr[0] "%u ഔട്ട്പുട്ട്"
|
|
||||||
#~ msgstr[1] "%u ഔട്ട്പുട്ടുകള്"
|
|
||||||
|
|
||||||
#~ msgid "%u Input"
|
|
||||||
#~ msgid_plural "%u Inputs"
|
|
||||||
#~ msgstr[0] "%u ഇന്പുട്ട്"
|
|
||||||
#~ msgstr[1] "%u ഇന്പുട്ടുകള്"
|
|
||||||
|
|
||||||
#~ msgid "System Sounds"
|
|
||||||
#~ msgstr "സിസ്റ്റം ശബ്ദങ്ങള്"
|
|
||||||
|
|
||||||
#~ msgctxt "event list time"
|
#~ msgctxt "event list time"
|
||||||
#~ msgid "%H:%M"
|
#~ msgid "%H:%M"
|
||||||
#~ msgstr "%H:%M"
|
#~ msgstr "%H:%M"
|
||||||
|
1136
po/zh_CN.po
1136
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user