Compare commits
535 Commits
Author | SHA1 | Date | |
---|---|---|---|
a1aa58bb64 | |||
82ffe233dc | |||
9e16bb85e3 | |||
887b41bce3 | |||
63eef286c9 | |||
14e8cba2b1 | |||
3418e6e85e | |||
8f0c980d3c | |||
c86f3b8d48 | |||
8cf6b4c728 | |||
612b9e9faf | |||
c2c4c26f72 | |||
be4d504e27 | |||
9ed0bbb3a9 | |||
e5bc3a2ba8 | |||
1dee10c575 | |||
352fb7b833 | |||
81cee34c17 | |||
fa786fd3ef | |||
0751a90bd9 | |||
709193d680 | |||
d0d82cdf7e | |||
5d2b7e2c9e | |||
07660f7fcf | |||
5d138e1b79 | |||
0133c6e174 | |||
2054f77e2b | |||
e4911e2f7a | |||
eabc2e6781 | |||
71cf87cbb1 | |||
155997b5fa | |||
82ce8fe3ff | |||
9f1da20161 | |||
d4239d570d | |||
1ecbabc69a | |||
7f767c49d8 | |||
aabe56ba79 | |||
d227ddfc88 | |||
021d3dadbb | |||
4902a600d5 | |||
db39ba3b9f | |||
239a9e4816 | |||
35e99266ba | |||
67ae8ed8e9 | |||
356e4c0967 | |||
80a9d2e7c9 | |||
5088f22388 | |||
4156a4c2d0 | |||
b6c2399a17 | |||
5be9326192 | |||
388cfa3695 | |||
e8914c6699 | |||
13bf64a53d | |||
f96b2ee858 | |||
b12967b930 | |||
d896248ff8 | |||
2ebdc81c8f | |||
2af5e851b3 | |||
bd9455ec8e | |||
fefee3b49e | |||
071c49b7c6 | |||
4e9e6e75d3 | |||
a13af7fbcc | |||
4fa8e2b59d | |||
90783c7cdf | |||
f99b4da4ec | |||
a64e0e1f49 | |||
270e82e3db | |||
8f4a4d93f2 | |||
83265bb12a | |||
5be8d5f9cf | |||
08126e5a38 | |||
b76efe17d6 | |||
ca2678446d | |||
06d906b962 | |||
023f5149e5 | |||
2e45508529 | |||
6241a8269f | |||
465d03ab2c | |||
a56cd3c3d6 | |||
d8a98e5467 | |||
2d813cbdd8 | |||
6d3434f3a5 | |||
fa0268f35a | |||
712ea9b9b6 | |||
b7fd78b254 | |||
a2e6b3167b | |||
72037af241 | |||
62048abbf5 | |||
7b61aca956 | |||
6709e5e458 | |||
11c8405879 | |||
6028628261 | |||
3d8a1537d2 | |||
c5b4decdae | |||
c7237be3e3 | |||
aa39166b74 | |||
e7b9933036 | |||
54d51c713c | |||
72e0c2fe11 | |||
7458d3ef39 | |||
52c5f9b144 | |||
8c7085acd4 | |||
083ca7d39b | |||
68e716ae27 | |||
ef8772916d | |||
b54e374bc5 | |||
b3228258ee | |||
e5036a458e | |||
c714a66ba3 | |||
d80b7be6ca | |||
77de611ec7 | |||
60b54c0052 | |||
6a3130e25f | |||
91cba1f8f4 | |||
22b2661df9 | |||
f8b397a5dc | |||
44b475e746 | |||
d25610903a | |||
2efcbaf206 | |||
7f1d2825fd | |||
b9edb1dc01 | |||
b0cc778c49 | |||
ff840db708 | |||
11f30e2e09 | |||
4886275df4 | |||
c5de239e25 | |||
10dcc100e9 | |||
8ada9b43ae | |||
fd77225c3e | |||
7ed3facf8f | |||
6c97e2a5ab | |||
f19e8b1e78 | |||
08e669adde | |||
bbd2c02b7f | |||
0d9da86b7e | |||
5810fcb14d | |||
2466eb3132 | |||
fc59e222d2 | |||
ff983432d9 | |||
67b4f9b3a9 | |||
f279b6bf7e | |||
daec53f4fe | |||
cb1966612e | |||
cda279f5c2 | |||
31915cdc93 | |||
329f803004 | |||
7780c99de6 | |||
d3ad857ba4 | |||
aa1405e4ea | |||
aa0a7f7816 | |||
3850edced5 | |||
ddd59f2e76 | |||
10a0f2b614 | |||
8bc85d4a79 | |||
3dc07d48c5 | |||
c1acf992fa | |||
99149f9c41 | |||
bcd307066a | |||
446910cb10 | |||
fde200d084 | |||
a376cd1610 | |||
dbeab0ef87 | |||
7765d6a08f | |||
aed50e2a39 | |||
b262a42458 | |||
3fd90dfcb1 | |||
dec55a3291 | |||
f6f3ded842 | |||
004c5cf287 | |||
aba5f2f7b8 | |||
2a96964204 | |||
a9fe2a1493 | |||
8f3bdd4f1a | |||
4322a20cbe | |||
2403fd0680 | |||
e01baf2a25 | |||
d27b37fefe | |||
acedb60abe | |||
77556d181e | |||
60612cace9 | |||
f057502834 | |||
21a1149532 | |||
6724ca4f63 | |||
7a6c25b3fb | |||
0366e320af | |||
f03793b825 | |||
0907f030b4 | |||
7542e68b6f | |||
9003a34285 | |||
3cc27eaabe | |||
601b081bf3 | |||
4405d993c9 | |||
361308eb1b | |||
d894dedaf6 | |||
d12dd1491f | |||
1625591598 | |||
a50c30a4fd | |||
02bfc74c1e | |||
a012dd838d | |||
e2b634e59a | |||
80b98d8787 | |||
bf2ba83cd5 | |||
727c43dbab | |||
203dedfb3a | |||
8d5d4159a3 | |||
cecba0269f | |||
3c878793b5 | |||
0549f42030 | |||
50f248ec5b | |||
544bdb4fb1 | |||
88b295ff9f | |||
602326bb57 | |||
31857cf05f | |||
896d8e830c | |||
5f86e29830 | |||
579ae59eca | |||
90db743cc9 | |||
fe82897064 | |||
0f49f36519 | |||
a92b7342ba | |||
86818e9c52 | |||
7a8a00c705 | |||
c8d5e0a51c | |||
524a7ff6fb | |||
5f6ac33d59 | |||
b4f5e4206d | |||
5133c3e010 | |||
0b77ea422a | |||
5c1dd4ea18 | |||
e1c687184e | |||
297d1356a2 | |||
98327b0c13 | |||
6786aee5ed | |||
0b9c726b4e | |||
436dd6ee8c | |||
acc053302d | |||
534b371d42 | |||
6004e3d2e1 | |||
c632074ba7 | |||
664245d2a6 | |||
fda4fc674d | |||
bf28c24c82 | |||
32df0e80ca | |||
297eab738f | |||
5819dd3a5a | |||
a007b1bb2d | |||
5cb43b6bae | |||
f524138a64 | |||
0aa626b2fb | |||
3ae1050f67 | |||
67736642f3 | |||
09f3c87d20 | |||
9deca75de8 | |||
5bc1dede81 | |||
6d53d43766 | |||
325462d9bf | |||
ee4ae62946 | |||
153768ef7f | |||
7249a218ba | |||
de348a4c49 | |||
fda8ffd9ef | |||
6cb707cc4f | |||
998c5f17fc | |||
c34b357051 | |||
40f4e92461 | |||
c727da823b | |||
8d1b7962d8 | |||
a6dfe20348 | |||
ed46390bbc | |||
80eb5bf535 | |||
7596fdb460 | |||
8db1ff8aef | |||
1dfffdbc4e | |||
64b2b4a7d4 | |||
ae35d0e43c | |||
b22c5eb167 | |||
6d5e414863 | |||
3e74dfb66d | |||
1245628521 | |||
c567690004 | |||
2feff4207b | |||
8c40b6f9a7 | |||
fc759bff77 | |||
3581c1d5b4 | |||
64e7459d1c | |||
f71afa9248 | |||
1ebca2e6d5 | |||
6222796b6a | |||
dcecf41d18 | |||
9d1fbffe75 | |||
6a6ba94bb9 | |||
ef5de3a5c0 | |||
f042e43990 | |||
620330db8f | |||
3765acc0a5 | |||
2e8654b96c | |||
cc5c5e7e8a | |||
9c849b0290 | |||
ad314b362e | |||
737a4f2816 | |||
96e4128e3e | |||
6f515f2327 | |||
cbb9a7ab96 | |||
e2e3b29ed1 | |||
82a8ac1976 | |||
bfec396ec2 | |||
d0e7f880d7 | |||
ff81659b9e | |||
6b2b3475c8 | |||
ed8acefc00 | |||
737f395d6c | |||
ab0a5d4a53 | |||
9412ac2027 | |||
c8670819dc | |||
4132ccae33 | |||
c450120409 | |||
6ce07abf50 | |||
e39e539ee9 | |||
9563a8f8a0 | |||
d5f37fa280 | |||
5c6e5ef6d0 | |||
42adc38609 | |||
e5fadb3b42 | |||
4c5f3aa971 | |||
6fc49b79a4 | |||
9e804b082f | |||
1fb220beb7 | |||
1fc1282fad | |||
8834a7df10 | |||
a34071e1c3 | |||
52a342300a | |||
b9456caeb0 | |||
26aa4333a5 | |||
04d2b0d282 | |||
7def1a4aa5 | |||
001b9868fb | |||
59a3e393f9 | |||
b846354787 | |||
2674d96e54 | |||
0c98fe8546 | |||
f18ed3c6ae | |||
644acf7018 | |||
96dca48b3b | |||
1309b64c33 | |||
1301dee744 | |||
efc194e36c | |||
60f41a109f | |||
890efa787a | |||
60c88612f7 | |||
82648bc86c | |||
ea1e5a5210 | |||
aa03734d39 | |||
bb0f76f562 | |||
8c2a290d09 | |||
8e661c3780 | |||
a03b6c419a | |||
fb55dd677f | |||
408790a630 | |||
08371f073d | |||
e7289378b7 | |||
31bde574de | |||
5aacfe4e6e | |||
5cf7bdabfd | |||
2021edd1fb | |||
45c1a9eafb | |||
0ae1556b94 | |||
c0739bd1e3 | |||
af51e8df7b | |||
22ef63cc44 | |||
03a5133e8c | |||
7f2456c00d | |||
aa4dbee362 | |||
7b65735cc9 | |||
7ef35fbec7 | |||
8b10d85fee | |||
d99b1e6c09 | |||
57ab7aec5b | |||
29a221bf62 | |||
671c569a9e | |||
492c033760 | |||
33a3b8046d | |||
88df183450 | |||
6a9080c3d6 | |||
019670b5ab | |||
4cab0c95d3 | |||
d51e79d483 | |||
69a27911a7 | |||
dd48514b24 | |||
eb54662098 | |||
545f0432c8 | |||
fdefb317cb | |||
4e0c8bfe67 | |||
dbd629d5d2 | |||
619a44a499 | |||
c5ca4e3ff0 | |||
55771b437b | |||
8727680983 | |||
ab9f21351f | |||
0055cabc62 | |||
fc70c2246b | |||
f7b6acaff8 | |||
91caa8e59f | |||
4c44bb7f52 | |||
df8a735aa9 | |||
82aea3e8d6 | |||
a8baf4a2a2 | |||
bfd344cdec | |||
a0fd4e195d | |||
4b3fbc4c9b | |||
63b1699a35 | |||
48acc41698 | |||
17672accfe | |||
986d72d9de | |||
ffd461cdb4 | |||
71dfab9711 | |||
c4f5274d74 | |||
4bfc3bafcb | |||
898b2b903d | |||
7921954a31 | |||
0e42de9149 | |||
8e4a5f1ac5 | |||
61577e176e | |||
4b008b1ada | |||
bee37b5bc4 | |||
b5ab8b6ed5 | |||
4d6bd91d16 | |||
5428db5385 | |||
6e6b1e6052 | |||
bc2b47974d | |||
2244b6ff1b | |||
fb384fc291 | |||
73cae8ce9a | |||
dcd07eb23f | |||
fa24448489 | |||
c975740f92 | |||
1e0187fa57 | |||
cbdf060bca | |||
19a8dff975 | |||
72f9f482d6 | |||
88de26138a | |||
57bd964cb3 | |||
74a39ae57c | |||
5090a4ccce | |||
ae0652d13f | |||
101a07a3d7 | |||
b012e93121 | |||
c31109800b | |||
c0dc363a3d | |||
739bb28220 | |||
aab2794e05 | |||
75c8c1bfb6 | |||
6f8bd96195 | |||
25f0d098bd | |||
56d584b7c6 | |||
20bf53add1 | |||
0d440bb0a2 | |||
8ec62ce46b | |||
79927faaec | |||
e4c7f1f3c4 | |||
68710c4647 | |||
7d7cbde1f3 | |||
bafd9c777a | |||
42a5531f15 | |||
227da25776 | |||
2028f33e38 | |||
145bf19636 | |||
d1675c44e2 | |||
6934e4db26 | |||
cae3414854 | |||
90d061edaf | |||
2e02918323 | |||
e77a1fd33b | |||
dd01c24c34 | |||
f88fbee80d | |||
fe08edbe2b | |||
d2a16bca10 | |||
a87f51487e | |||
76fce94b66 | |||
d104f9210a | |||
d0780d1622 | |||
9d5906dae3 | |||
07a0960265 | |||
72bee6d7ca | |||
249f26d23c | |||
7813c5b93f | |||
e38d83fd44 | |||
d97657b151 | |||
7e857dede3 | |||
c3218f6b03 | |||
1060d0db60 | |||
b8925a091c | |||
79cca07a41 | |||
c1d189c9ad | |||
b1a973ee5a | |||
93ef560779 | |||
c28d35ad86 | |||
9c654a6ab5 | |||
64a54e379c | |||
8f4ec8583b | |||
f4852d7264 | |||
092e1a691d | |||
16ac42421d | |||
1d2eadb9c0 | |||
35c85d9fac | |||
f0622c1896 | |||
6d11247417 | |||
59c3e3a179 | |||
42e26a8682 | |||
db6caac9cc | |||
ba4a57ba0b | |||
018e3bc35f | |||
a56bc9d933 | |||
5b93525ce8 | |||
6abb86dff6 | |||
6a27d5ed80 | |||
8232684672 | |||
2d855ce5cf | |||
b2b685e46d | |||
ef552846d1 | |||
c7dfd0894e | |||
5b1a76aeff | |||
529b6ca935 | |||
625a4c0766 | |||
3c3ea2f575 | |||
57a332bb08 | |||
73ac98b193 | |||
59e3cbb36b | |||
fb019a7cbf | |||
09607f6aa7 | |||
1c4a33eb78 | |||
b7513097ea | |||
a35677a9bf | |||
89de3a81c6 | |||
b9828bf5de |
8
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
*.o
|
*.o
|
||||||
.deps
|
.deps
|
||||||
.libs
|
.libs
|
||||||
|
ABOUT-NLS
|
||||||
ChangeLog
|
ChangeLog
|
||||||
INSTALL
|
INSTALL
|
||||||
Makefile
|
Makefile
|
||||||
@ -29,8 +30,13 @@ m4/
|
|||||||
omf.make
|
omf.make
|
||||||
po/*.gmo
|
po/*.gmo
|
||||||
po/gnome-shell.pot
|
po/gnome-shell.pot
|
||||||
|
po/*.header
|
||||||
|
po/*.sed
|
||||||
|
po/*.sin
|
||||||
po/Makefile.in.in
|
po/Makefile.in.in
|
||||||
|
po/Makevars.template
|
||||||
po/POTFILES
|
po/POTFILES
|
||||||
|
po/Rules-quot
|
||||||
po/stamp-it
|
po/stamp-it
|
||||||
scripts/launcher.pyc
|
scripts/launcher.pyc
|
||||||
src/*.gir
|
src/*.gir
|
||||||
@ -43,9 +49,11 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
|
|||||||
src/gnome-shell
|
src/gnome-shell
|
||||||
src/gnome-shell-calendar-server
|
src/gnome-shell-calendar-server
|
||||||
src/gnome-shell-extension-tool
|
src/gnome-shell-extension-tool
|
||||||
|
src/gnome-shell-hotplug-sniffer
|
||||||
src/gnome-shell-jhbuild
|
src/gnome-shell-jhbuild
|
||||||
src/gnome-shell-perf-helper
|
src/gnome-shell-perf-helper
|
||||||
src/gnome-shell-real
|
src/gnome-shell-real
|
||||||
|
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||||
src/run-js-test
|
src/run-js-test
|
||||||
src/test-recorder
|
src/test-recorder
|
||||||
src/test-recorder.ogg
|
src/test-recorder.ogg
|
||||||
|
@ -3,5 +3,5 @@ E-mail: otaylor@redhat.com
|
|||||||
Userid: otaylor
|
Userid: otaylor
|
||||||
|
|
||||||
Colin Walters
|
Colin Walters
|
||||||
E-mail: walters@redhat.com
|
E-mail: walters@verbum.org
|
||||||
Userid: walters
|
Userid: walters
|
||||||
|
335
NEWS
@ -1,3 +1,338 @@
|
|||||||
|
3.1.90.1
|
||||||
|
========
|
||||||
|
|
||||||
|
* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
|
||||||
|
* Fix build with new gobject-introspection [Dan]
|
||||||
|
* Use a better icon for removable devices [Cosimo; #657757]
|
||||||
|
* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
|
||||||
|
* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
|
||||||
|
* Misc build fixes [Adel; #657697]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
|
||||||
|
Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
|
||||||
|
|
||||||
|
3.1.90
|
||||||
|
======
|
||||||
|
* Add an on-screen keyboard that uses Caribou as a backend
|
||||||
|
[Nohemi, Dan; #612662]
|
||||||
|
* Allow searching for people in the overview using libfolks
|
||||||
|
as the backend [Morten; #643018]
|
||||||
|
* Add a "Login Screen" mode to be used when GDM is running; this
|
||||||
|
mode has a stripped down user interface, and also contains the
|
||||||
|
code to display the user list and authentication. [Ray; #657082]
|
||||||
|
* Rework user menu to separate out "Do Not Disturb" from the IM
|
||||||
|
status and to visually match GNOME Contacts. [Florian; #652837]
|
||||||
|
* Implement displaying images such as cover-art in notifications
|
||||||
|
[Neha, Marina; #621009]
|
||||||
|
* Support default actions for notifications [Florian; #655818]
|
||||||
|
* Networking
|
||||||
|
- Stop using nm-applet for dialogs; do them as proper system modal
|
||||||
|
dialogs in the shell code. [Giovanni; #650244]
|
||||||
|
- Fix handling of hidden access points [Giovanni; #646454]
|
||||||
|
* Telepathy integration
|
||||||
|
- Support subscription requests [Guillaume, Xavier; #653941]
|
||||||
|
- Notify on account connection errors [Alban, Jasper, Xavier; #654159]
|
||||||
|
- Allow approving file transfers [Guillaume; #653940]
|
||||||
|
- Improve styling of messages [Jasper; #640271]
|
||||||
|
* Extension system [Jasper; #654770]
|
||||||
|
- Support live enabling and disabling of extensions
|
||||||
|
- Add the ability to install extensions from HTTP
|
||||||
|
- Enhance D-Bus interface for controlling extensions
|
||||||
|
- Collect errors separately for each extension
|
||||||
|
* Add Main.panel.addToStatusArea for extension convenience
|
||||||
|
[Giovanni, Jasper, Marc-Antoine; #653205]
|
||||||
|
* Port to the new gnome-menus API. Clean up and speed up
|
||||||
|
application information loading [Colin; #648149, #656546]
|
||||||
|
* Use the accountsservice library rather than cut-and-pasted GDM code
|
||||||
|
[Florian; #650893]
|
||||||
|
* Add a D-Bus interface to take a screenshot; this will avoid various race
|
||||||
|
conditions with the current gnome-screenshot approach [Adel; #652952]
|
||||||
|
* Show numeric indicators to distinguish duplicate keyboard names
|
||||||
|
[Giovanni; #650128]
|
||||||
|
* Add GNOME Documents to the favorites list [Adel; #657520]
|
||||||
|
* Update the clock immediately on resume from suspend [Colin; #656403]
|
||||||
|
* Remove animation support from StAdjustment [Ray; #657082]
|
||||||
|
* Support configuration of calendar applications via gsettings
|
||||||
|
[Tassilo; #651190]
|
||||||
|
* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
|
||||||
|
* Darken workspace background on all workspaces [Rui; #656433]
|
||||||
|
* Improve detection of the starting day of the week [Florian; #649078]
|
||||||
|
* Add StButtonAccessible [Alejandro]
|
||||||
|
* Visual tweaks to match mockups
|
||||||
|
[Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
|
||||||
|
* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
|
||||||
|
#645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
|
||||||
|
#656335, #657111]
|
||||||
|
* Code cleanups [Colin, Dan, Guillaume, Ray;
|
||||||
|
#652718, #654639, #648651, #655813, #657082]
|
||||||
|
* String tweaks [Jasper, Jeremy; #652984, #640271]
|
||||||
|
* Build fixes [Jasper, Nohemi; #644275, #655812]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
|
||||||
|
Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
|
||||||
|
Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
|
||||||
|
Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
|
||||||
|
Marina Zhurakhinskaya
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
|
||||||
|
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
|
||||||
|
Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
|
||||||
|
Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
|
||||||
|
A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||||
|
|
||||||
|
3.1.4
|
||||||
|
=====
|
||||||
|
* Take over inserted media handling and autorun from gnome-session [Cosimo]
|
||||||
|
* Message Tray
|
||||||
|
- Display a count of unread notifications on icons
|
||||||
|
[Jasper, Guillaume; #649356, #654139]
|
||||||
|
- Only remove icons when the sender quits from D-Bus, not when it
|
||||||
|
closes its last window [Neha, Marina; #645764]
|
||||||
|
- Solve problems switching chats between shell and Empathy
|
||||||
|
[Guillaume; #654237]
|
||||||
|
- Fix handling of bad GMarkup in messages [Dan; #650298]
|
||||||
|
- Never show notifications when the screensaver is active [Dan; #654550]
|
||||||
|
* Telepathy integrationpp
|
||||||
|
- Implement Telepathy Debug interface to enable empathy-debugger
|
||||||
|
[Guillaume; #652816]
|
||||||
|
- Allow approving room invitations, and audio/video calls
|
||||||
|
[Guillaume; #653740 #653939]
|
||||||
|
- Send typing notifications [Jonny; #650196]
|
||||||
|
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
|
||||||
|
* Make control-Return in the overview open a new window [Maxim]
|
||||||
|
* Delay showing the alt-Tab switcher to reduce visual noise when
|
||||||
|
flipping betweeen windows [Dan; #652346]
|
||||||
|
* When we have vertically stacked monitors, put the message tray
|
||||||
|
on the bottom one [Dan; #636963]
|
||||||
|
* Fix various problems with keynav and the Activities button
|
||||||
|
[Dan; #641253 #645759]
|
||||||
|
* Ensure screensaver is locked when switching users [Colin; #654565]
|
||||||
|
* Improve extension creation tool [Jasper; #653206]
|
||||||
|
* Fix compatibility with latest GJS [Giovanni; #654349]
|
||||||
|
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
|
||||||
|
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
|
||||||
|
#647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
|
||||||
|
#654269 #654527 #655446]
|
||||||
|
* Build fixes [Florian, Siegfried; #654300]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
|
||||||
|
Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
|
||||||
|
Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
|
||||||
|
Dan Winship, Marina Zhurakhinskaya
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
|
||||||
|
Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||||
|
Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
|
||||||
|
Michal Štrba, Matej Urbančič [sl]
|
||||||
|
|
||||||
|
3.1.3
|
||||||
|
=====
|
||||||
|
* Fix problem with "user theme extension" breaking the CSS for other
|
||||||
|
extensions [Giovanni; #650971]
|
||||||
|
* Telepathy IM framework integration
|
||||||
|
- Switch to using telepathy-glib rather than talking to
|
||||||
|
Telepathy via D-Bus [Guillaume, Jasper; #645585, #649633, #651138, #651227]
|
||||||
|
- Acknowledge messages when the user clicks on them [Guillaume, #647893]
|
||||||
|
- Fix problem with telepathy icon blinking for incoming messages
|
||||||
|
even though the user has been notified of them [Guillaume; #643594]
|
||||||
|
* Networking
|
||||||
|
- keep wirelesss networks in predictable order [Giovanni; #646580, #652313]
|
||||||
|
- Show unmanaged devices in the menu [Giovanni; #646946]
|
||||||
|
- Fix overflow when too many VPN connections [Giovanni; #651602]
|
||||||
|
* Bluetooth
|
||||||
|
- Show "hardware disabled" when disabled by rfkill [Giovanni; #648048]
|
||||||
|
- Fix bug updating status of devices [Giovanni; #647565]
|
||||||
|
* LookingGlass console:
|
||||||
|
- Add a "Memory" tab [Colin; #650692]
|
||||||
|
- Make escape work from any page [Dan Winship; #647303]
|
||||||
|
- Provide a way to refer to panel items as, e.g.,
|
||||||
|
Main.panel._activities [Dan Winship; #646915]
|
||||||
|
* User menu
|
||||||
|
- Fix problem with suspend menu option locking the screen even when the user
|
||||||
|
disabled that. [Florian; #652327]
|
||||||
|
- Hide "power off..." option if shutdown is disabled via PolicyKit
|
||||||
|
[Florian; #652038]
|
||||||
|
* Track changes to WM_CLASS (fixes problems with LibreOffice tracking)
|
||||||
|
[Colin; #649315]
|
||||||
|
* Remove app tracking workarounds for Firefox and LibreOffice [Colin; #651015]
|
||||||
|
* Use upstream gettext autoconfigury rather than glib version [Javier; #631576]
|
||||||
|
* Show messages in the message tray when an application is fullscreen
|
||||||
|
[Dan Winship; #608667]
|
||||||
|
* Don't autohide the workspace pager if there is more than one workspace
|
||||||
|
[Florian; #652714, #653078, #653142]
|
||||||
|
* Don't always slide out the workspace pager at drag begin [Florian; #652730]
|
||||||
|
* Only offer to remove a favorite app when dragging it's icon [Owen; #642895]
|
||||||
|
* Allow dropping an icon anywhere on a workspace [Adel; #652079]
|
||||||
|
* st-scroll-view: Make the fade effect and offset themable [Jasper; #651813]
|
||||||
|
* Obey the user's preference when running an application in a terminal
|
||||||
|
from the run dialog [Florian; #648422]
|
||||||
|
* Consistently exit overview when launching external applications
|
||||||
|
[Colin; #653095]
|
||||||
|
* Adapt to changes in GJS for how GObject APIs are bound
|
||||||
|
[Alex, Colin, Florian, Jasper, Marc-Antoine; #649981, #652597]
|
||||||
|
* Fix problems with scrolling in overflow for alt-Tab switcher
|
||||||
|
[Dan Winship, Adel; #647807]
|
||||||
|
* Mark relationships between labels and actors for accessibility [Alejandro]
|
||||||
|
* Add org.gnome.shell.enabled-extensions complementing disabled-extensions
|
||||||
|
GSetting [Tassilo; #651088]
|
||||||
|
* Visual tweaks [Jakub, Jasper; #646261, #652715]
|
||||||
|
* Switch to building against clutter-1.7 with independent Cogl [Adel; #653397]
|
||||||
|
* Code cleanups [Colin, Dan Winship, Florian; #633620, #645031, #648755, #648758,
|
||||||
|
#648760, #649203, #649517, #650317, #652730]
|
||||||
|
* Memory leak fixes [Colin, Maxim; #649508, #650934]
|
||||||
|
* Build Fixes [Colin, Dan Winship, Florian, Ionut, Morten, Owen, Sean; #647395,
|
||||||
|
#648006, #650869, #653199, #653275
|
||||||
|
* Miscellaneous bug fixes [Adam, Adel, Dan Williams, Dan Winship, Florian,
|
||||||
|
Ionut, Jasper, Maxim, Ray; #620105, #639459, #641570, #642793, #643513,
|
||||||
|
#645848, #646919, #647186, #648305, #648410, #648562, #648894, #649001,
|
||||||
|
#645990, #647893, #647907, #651012, #651086, #651606, #651569, #651866,
|
||||||
|
#652388, #653511]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Ionut Biru, Giovanni Campagna, Guillaume Desmottes, Adam Dingle,
|
||||||
|
Maxim Ermilov, Adel Gadllah, Tassilo Horn, Javier Jardón, Jonny Lamb,
|
||||||
|
Alexander Larsson, Rui Matos, Morten Mjelva, Florian Müllner,
|
||||||
|
Marc-Antoine Perennou, Alejandro Piñeiro, Jasper St. Pierre, Jakub Steiner,
|
||||||
|
Ray Strode, Owen Taylor, Colin Walters, Dan Williams, Sean Wilson, Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Daniel Martinez Cucalon [ar], Ihar Hrachyshka [be], Carles Ferrando,
|
||||||
|
Gil Forcada, Sílvia Miranda [ca], Kristjan Schmidt [eo], Jorge González,
|
||||||
|
Daniel Mustieles [es], Seán de Búrca [ga], Fran Diéguez [gl],
|
||||||
|
Yaron Shahrabani [he], Kjartan Maraas [nb], Misha Shnurapet,
|
||||||
|
Yuri Myasoedov [ru], Daniel Nylander [se], Peter Mráz [sk],
|
||||||
|
Matej Urbančič [sl], Krishnababu Krothapalli [te], Daniel Korostil [uk],
|
||||||
|
Aron Xu [zh_CN]
|
||||||
|
|
||||||
|
3.0.2
|
||||||
|
=====
|
||||||
|
* Network Menu [Dan Williams]
|
||||||
|
- Fix connecting to WPA2 Enterprise access points
|
||||||
|
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=648171
|
||||||
|
- Show the mobile broadband wizard when selecting 3G network
|
||||||
|
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=649318
|
||||||
|
- Miscellaneous bug fixes
|
||||||
|
648648, 650124
|
||||||
|
* Fix duplicate icons in the application browser [Owen]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=648739
|
||||||
|
* Make clicking anywhere on the volume icon slider work [Giovanni]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646660
|
||||||
|
* Fix a case where activating and clicking the hot corner
|
||||||
|
at the same time could result in immediately leaving the
|
||||||
|
overview [Rui]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=649427
|
||||||
|
* Fix a case where applications became misordered in Alt-Tab [Jasper]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=643302
|
||||||
|
* Fix a bug where messages you send could show up in
|
||||||
|
notifications as if someone else sent them [Jonny]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=650219
|
||||||
|
* Memory leak fixes [Colin, Maxim]
|
||||||
|
642652, 649508, 649497
|
||||||
|
* Miscellaneous minor bug fixes [Adel, Christopher, Jasper]
|
||||||
|
649596, 648765, 648983, 649632
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Christopher Aillon, Giovanni Campagna, Maxim Ermilov,
|
||||||
|
Adel Gadllah, Jonny Lamb, Rui Matos, Jasper St. Pierre,
|
||||||
|
Owen Taylor, Colin Walters, Dan Williams
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Arash Mousavi [fa], Seán de Búrca [ga], Timo Jyrinki [fi],
|
||||||
|
Sigurd Gartmann [nb], Daniel Nylander [se], Peter Mráz [sl],
|
||||||
|
Abduxukur Abdurixit [ug], Nguyễn Thái Ngọc Duy [vi]
|
||||||
|
|
||||||
|
3.0.1
|
||||||
|
=====
|
||||||
|
|
||||||
|
* Network menu
|
||||||
|
- Fix problems updating the menu for mobile broadband devices [Giovanni]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646395
|
||||||
|
- Fix missing device descriptions with multiple devices of the
|
||||||
|
same type [Giovanni]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646074
|
||||||
|
- Label ad-hoc neworks with an appropriate icon [Dan]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646141
|
||||||
|
- Fix displaying some devices states as "invalid" [Dan]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646946
|
||||||
|
- Fix problems with access points that don't report a SSID [Giovanni]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=647040
|
||||||
|
- Miscellaneous minor bug fixes [Dan, Giovanni, Owen]
|
||||||
|
645981, 646558, 646443, 646708, 646968
|
||||||
|
* Application menu and icon
|
||||||
|
- Fix bug where application menu icon was missing at GNOME Shell
|
||||||
|
startup. [Florian]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=644122
|
||||||
|
- Fix missing application menu for dialog windows [Colin]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=647082
|
||||||
|
- When launching an application through an alternate launcher
|
||||||
|
(like for a System Settings pane), association the windows with
|
||||||
|
the application, not the launcher. [Colin]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646689
|
||||||
|
* Activities overview
|
||||||
|
- Load the applications view incrementally to avoid potentially freezing
|
||||||
|
for multiple seconds [Colin]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=647778
|
||||||
|
- Fix bug where package installation while the overview
|
||||||
|
was up could result in a corrupted application display. [Giovanni]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=645801
|
||||||
|
- Fix dragging from the search results to launch apps and docs [Florian]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=645990
|
||||||
|
- Fix flickering of selection when searching in the overview [Florian]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646019
|
||||||
|
- Fix bug when typing into the search box when text was already
|
||||||
|
selected [Nohemi]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=636341
|
||||||
|
* Fix layout of notifications for right-to-left languages [Florian]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646921
|
||||||
|
* Remove a confusing special case where Alt-Tab sometimes switched
|
||||||
|
to a different window of the same application rather than to
|
||||||
|
a different application. [Rui]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=648132
|
||||||
|
* Fix a crash that could happen when a window was opened on a
|
||||||
|
workspace that was immediately removed [Dan]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=648132
|
||||||
|
* Fix keyboard navigation in logout/reboot dialogs [Dan]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646740
|
||||||
|
* Fix missing inspector icon in Looking Glass console [Dan]
|
||||||
|
* Miscellaneous minor bug fixes [Adel, Colin, Dan, Florian, Nohemi]
|
||||||
|
645648, 646205, 646257, 646855, 647098, 646730
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Nohemi Fernandez, Adel Gadllah, Rui Matos, Florian Müllner,
|
||||||
|
Owen Taylor, Colin Walters, Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Hendrik Richter [de], Jorge González [es], Arash Mousavi [fa],
|
||||||
|
Fran Diéguez [gl], Jiro Matsuzawa [ja], Piotr Drąg [pl], Daniel Nylander [sv],
|
||||||
|
Sira Nokyoongtong [th], Muhammet Kara [tr], Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||||
|
|
||||||
|
3.0.0.2
|
||||||
|
=======
|
||||||
|
|
||||||
|
* Fix missing import that was preventing extensions from loading.
|
||||||
|
[Maxim Ermilov]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646333
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Timo Jyrinki [fi]
|
||||||
|
|
||||||
|
3.0.0.1
|
||||||
|
=======
|
||||||
|
|
||||||
|
* Fix problem with stuck event handling if network menu pops down while
|
||||||
|
user is using the scrollbar. [Owen Taylor]
|
||||||
|
https://bugzilla.gnome.org/show_bug.cgi?id=646825
|
||||||
|
|
||||||
Contributors to GNOME Shell 3.0
|
Contributors to GNOME Shell 3.0
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
|
72
configure.ac
@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
AC_INIT([gnome-shell],[3.0.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
AC_INIT([gnome-shell],[3.1.90.1],[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])
|
||||||
@ -23,12 +23,16 @@ AM_PROG_CC_C_O
|
|||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
LT_INIT([disable-static])
|
LT_INIT([disable-static])
|
||||||
|
|
||||||
|
# i18n
|
||||||
|
IT_PROG_INTLTOOL([0.40])
|
||||||
|
|
||||||
|
AM_GNU_GETTEXT([external])
|
||||||
|
AM_GNU_GETTEXT_VERSION([0.17])
|
||||||
|
|
||||||
GETTEXT_PACKAGE=gnome-shell
|
GETTEXT_PACKAGE=gnome-shell
|
||||||
AC_SUBST(GETTEXT_PACKAGE)
|
AC_SUBST(GETTEXT_PACKAGE)
|
||||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||||
[The prefix for our gettext translation domains.])
|
[The prefix for our gettext translation domains.])
|
||||||
IT_PROG_INTLTOOL(0.26)
|
|
||||||
AM_GLIB_GNU_GETTEXT
|
|
||||||
|
|
||||||
PKG_PROG_PKG_CONFIG([0.22])
|
PKG_PROG_PKG_CONFIG([0.22])
|
||||||
|
|
||||||
@ -60,73 +64,70 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||||
|
|
||||||
CLUTTER_MIN_VERSION=1.5.15
|
CLUTTER_MIN_VERSION=1.7.5
|
||||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||||
GJS_MIN_VERSION=0.7.11
|
GJS_MIN_VERSION=1.29.15
|
||||||
MUTTER_MIN_VERSION=3.0.0
|
MUTTER_MIN_VERSION=3.0.0
|
||||||
|
FOLKS_MIN_VERSION=0.5.2
|
||||||
GTK_MIN_VERSION=3.0.0
|
GTK_MIN_VERSION=3.0.0
|
||||||
GIO_MIN_VERSION=2.25.9
|
GIO_MIN_VERSION=2.29.10
|
||||||
LIBECAL_MIN_VERSION=2.32.0
|
LIBECAL_MIN_VERSION=2.32.0
|
||||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||||
LIBEDATASERVERUI2_MIN_VERSION=1.2.0
|
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
||||||
LIBEDATASERVERUI3_MIN_VERSION=2.91.6
|
TELEPATHY_GLIB_MIN_VERSION=0.15.5
|
||||||
TELEPATHY_GLIB_MIN_VERSION=0.13.12
|
|
||||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||||
POLKIT_MIN_VERSION=0.100
|
POLKIT_MIN_VERSION=0.100
|
||||||
|
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||||
|
|
||||||
# Collect more than 20 libraries for a prize!
|
# Collect more than 20 libraries for a prize!
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
|
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
|
||||||
gio-unix-2.0 dbus-glib-1 libxml-2.0
|
gio-unix-2.0 dbus-glib-1 libxml-2.0
|
||||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||||
|
folks >= $FOLKS_MIN_VERSION
|
||||||
libmutter >= $MUTTER_MIN_VERSION
|
libmutter >= $MUTTER_MIN_VERSION
|
||||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||||
libgnome-menu $recorder_modules gconf-2.0
|
libgnome-menu-3.0 $recorder_modules gconf-2.0
|
||||||
gdk-x11-3.0
|
gdk-x11-3.0 libsoup-2.4
|
||||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
libstartup-notification-1.0
|
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||||
libcanberra
|
libcanberra
|
||||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
|
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||||
|
libnm-glib libnm-util gnome-keyring-1)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||||
|
|
||||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
||||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
||||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
||||||
|
|
||||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
||||||
# NM is the only typelib we use that we don't jhbuild
|
|
||||||
PKG_CHECK_EXISTS([libnm-glib >= 0.8.995],
|
|
||||||
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
|
|
||||||
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
|
|
||||||
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
|
|
||||||
fi])
|
|
||||||
AC_SUBST(JHBUILD_TYPELIBDIR)
|
AC_SUBST(JHBUILD_TYPELIBDIR)
|
||||||
|
|
||||||
saved_CFLAGS=$CFLAGS
|
saved_CFLAGS=$CFLAGS
|
||||||
saved_LIBS=$LIBS
|
saved_LIBS=$LIBS
|
||||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
CFLAGS=$GNOME_SHELL_CFLAGS
|
||||||
LIBS=$GNOME_SHELL_LIBS
|
LIBS=$GNOME_SHELL_LIBS
|
||||||
# sn_startup_sequence_get_application_id, we can replace with a version check later
|
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
||||||
AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id XFixesCreatePointerBarrier)
|
|
||||||
CFLAGS=$saved_CFLAGS
|
CFLAGS=$saved_CFLAGS
|
||||||
LIBS=$saved_LIBS
|
LIBS=$saved_LIBS
|
||||||
|
|
||||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.90.0 x11)
|
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 gnome-desktop-3.0 >= 2.90.0 x11)
|
||||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
|
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
|
||||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||||
PKG_CHECK_MODULES(JS_TEST, clutter-x11-1.0 gjs-1.0 gobject-introspection-1.0 gtk+-3.0)
|
|
||||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
||||||
|
|
||||||
AC_MSG_CHECKING([for bluetooth support])
|
AC_MSG_CHECKING([for bluetooth support])
|
||||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
|
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||||
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0`/gnome-bluetooth
|
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
|
||||||
BLUETOOTH_LIBS="-L'$BLUETOOTH_DIR' -lgnome-bluetooth-applet"
|
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
|
||||||
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
||||||
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
|
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
|
||||||
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
|
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
|
||||||
@ -136,13 +137,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
|
|||||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
AC_SUBST([HAVE_BLUETOOTH],[0])
|
||||||
AC_MSG_RESULT([no])])
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
# Default to libedataserverui-3.0, but allow falling back to 1.2
|
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
||||||
PKG_CHECK_EXISTS(libedataserverui-3.0,
|
|
||||||
[EDS_API=3.0
|
|
||||||
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI3_MIN_VERSION],
|
|
||||||
[EDS_API=1.2
|
|
||||||
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI2_MIN_VERSION])
|
|
||||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-$EDS_API >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
|
||||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||||
|
|
||||||
@ -158,6 +153,17 @@ AC_CHECK_FUNCS(fdwalk)
|
|||||||
AC_CHECK_FUNCS(mallinfo)
|
AC_CHECK_FUNCS(mallinfo)
|
||||||
AC_CHECK_HEADERS([sys/resource.h])
|
AC_CHECK_HEADERS([sys/resource.h])
|
||||||
|
|
||||||
|
# _NL_TIME_FIRST_WEEKDAY is an enum and not a define
|
||||||
|
AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY])
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <langinfo.h>]],
|
||||||
|
[[nl_langinfo(_NL_TIME_FIRST_WEEKDAY);]])],
|
||||||
|
[langinfo_ok=yes], [langinfo_ok=no])
|
||||||
|
AC_MSG_RESULT($langinfo_ok)
|
||||||
|
if test "$langinfo_ok" = "yes"; then
|
||||||
|
AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1],
|
||||||
|
[Define if _NL_TIME_FIRST_WEEKDAY is available])
|
||||||
|
fi
|
||||||
|
|
||||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||||
AM_PATH_GLIB_2_0()
|
AM_PATH_GLIB_2_0()
|
||||||
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
|
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
|
||||||
|
@ -29,27 +29,16 @@ dist_theme_DATA = \
|
|||||||
theme/dash-placeholder.svg \
|
theme/dash-placeholder.svg \
|
||||||
theme/filter-selected-ltr.svg \
|
theme/filter-selected-ltr.svg \
|
||||||
theme/filter-selected-rtl.svg \
|
theme/filter-selected-rtl.svg \
|
||||||
|
theme/gdm.css \
|
||||||
theme/gnome-shell.css \
|
theme/gnome-shell.css \
|
||||||
theme/mosaic-view-active.svg \
|
|
||||||
theme/mosaic-view.svg \
|
|
||||||
theme/move-window-on-new.svg \
|
|
||||||
theme/panel-border.svg \
|
theme/panel-border.svg \
|
||||||
theme/panel-button-border.svg \
|
theme/panel-button-border.svg \
|
||||||
theme/panel-button-highlight-narrow.svg \
|
theme/panel-button-highlight-narrow.svg \
|
||||||
theme/panel-button-highlight-wide.svg \
|
theme/panel-button-highlight-wide.svg \
|
||||||
theme/process-working.svg \
|
theme/process-working.svg \
|
||||||
theme/running-indicator.svg \
|
theme/running-indicator.svg \
|
||||||
theme/scroll-button-down-hover.png \
|
|
||||||
theme/scroll-button-down.png \
|
|
||||||
theme/scroll-button-up-hover.png \
|
|
||||||
theme/scroll-button-up.png \
|
|
||||||
theme/scroll-hhandle.svg \
|
theme/scroll-hhandle.svg \
|
||||||
theme/scroll-vhandle.svg \
|
theme/scroll-vhandle.svg \
|
||||||
theme/section-more.svg \
|
|
||||||
theme/section-more-open.svg \
|
|
||||||
theme/separator-white.png \
|
|
||||||
theme/single-view-active.svg \
|
|
||||||
theme/single-view.svg \
|
|
||||||
theme/source-button-border.svg \
|
theme/source-button-border.svg \
|
||||||
theme/toggle-off-us.svg \
|
theme/toggle-off-us.svg \
|
||||||
theme/toggle-off-intl.svg \
|
theme/toggle-off-intl.svg \
|
||||||
|
@ -11,12 +11,13 @@
|
|||||||
using the Alt-F2 dialog.
|
using the Alt-F2 dialog.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="disabled-extensions" type="as">
|
<key name="enabled-extensions" type="as">
|
||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
<_summary>Uuids of extensions to disable</_summary>
|
<_summary>Uuids of extensions to enable</_summary>
|
||||||
<_description>
|
<_description>
|
||||||
GNOME Shell extensions have a uuid property;
|
GNOME Shell extensions have a uuid property; this key lists extensions
|
||||||
this key lists extensions which should not be loaded.
|
which should be loaded. disabled-extensions overrides this setting for
|
||||||
|
extensions that appear in both lists.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="enable-app-monitoring" type="b">
|
<key name="enable-app-monitoring" type="b">
|
||||||
@ -30,7 +31,7 @@
|
|||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="favorite-apps" type="as">
|
<key name="favorite-apps" type="as">
|
||||||
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
|
<default>[ 'firefox.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>
|
||||||
<_description>
|
<_description>
|
||||||
The applications corresponding to these identifiers
|
The applications corresponding to these identifiers
|
||||||
@ -52,6 +53,7 @@
|
|||||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
<child name="clock" schema="org.gnome.shell.clock"/>
|
||||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||||
|
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
|
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
|
||||||
@ -65,6 +67,24 @@
|
|||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
|
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
||||||
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
|
<key name="show-keyboard" type="b">
|
||||||
|
<default>false</default>
|
||||||
|
<_summary>Show the onscreen keyboard</_summary>
|
||||||
|
<_description>
|
||||||
|
If true, display onscreen keyboard.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
<key name="keyboard-type" type="s">
|
||||||
|
<default>'touch'</default>
|
||||||
|
<_summary>Which keyboard to use</_summary>
|
||||||
|
<_description>
|
||||||
|
The type of keyboard to use.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
|
||||||
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
||||||
gettext-domain="@GETTEXT_PACKAGE@">
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
<key name="show-seconds" type="b">
|
<key name="show-seconds" type="b">
|
||||||
|
161
data/theme/gdm.css
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/* Copyright 2011, Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU Lesser General Public License,
|
||||||
|
* version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Login Dialog */
|
||||||
|
|
||||||
|
.login-dialog-title {
|
||||||
|
font-size: 14pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #666666;
|
||||||
|
padding-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog {
|
||||||
|
border-radius: 16px;
|
||||||
|
min-height: 150px;
|
||||||
|
max-height: 700px;
|
||||||
|
min-width: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-view {
|
||||||
|
-st-vfade-offset: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list {
|
||||||
|
spacing: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item:ltr {
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item:rtl {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
||||||
|
font-size: 20pt;
|
||||||
|
padding-left: 1em;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
|
||||||
|
color: white;
|
||||||
|
text-shadow: black 0px 2px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item-vertical-layout {
|
||||||
|
spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
|
||||||
|
background-color: rgba(0,0,0,0.0);
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
|
||||||
|
background-color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-user-list-item-icon {
|
||||||
|
border: 2px solid #8b8b8b;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-not-listed-button {
|
||||||
|
padding-top: 2em;
|
||||||
|
}
|
||||||
|
.login-dialog-not-listed-label {
|
||||||
|
font-size: 14pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-prompt-layout {
|
||||||
|
padding-bottom: 64px;
|
||||||
|
}
|
||||||
|
.login-dialog-prompt-label {
|
||||||
|
color: white;
|
||||||
|
font-size: 20pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-prompt-entry {
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 2px solid #5656cc;
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
|
caret-color: black;
|
||||||
|
caret-size: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 10.5pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-button {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-button:focus {
|
||||||
|
background-color: #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-button:active {
|
||||||
|
background-color: #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-button:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-scroll-view {
|
||||||
|
background-gradient-start: rgba(80,80,80,0.3);
|
||||||
|
background-gradient-end: rgba(80,80,80,0.7);
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(80,80,80,1.0);
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-item:focus {
|
||||||
|
background-color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-triangle {
|
||||||
|
padding-right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-item-box {
|
||||||
|
spacing: .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-dialog-session-list-item-dot {
|
||||||
|
width: .75em;
|
||||||
|
height: .75em;
|
||||||
|
}
|
@ -39,6 +39,11 @@ StScrollBar
|
|||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StScrollView.vfade
|
||||||
|
{
|
||||||
|
-st-vfade-offset: 68px;
|
||||||
|
}
|
||||||
|
|
||||||
StScrollView StScrollBar
|
StScrollView StScrollBar
|
||||||
{
|
{
|
||||||
min-width: 16px;
|
min-width: 16px;
|
||||||
@ -88,12 +93,12 @@ StTooltip StLabel {
|
|||||||
/* PopupMenu */
|
/* PopupMenu */
|
||||||
|
|
||||||
.popup-menu-boxpointer {
|
.popup-menu-boxpointer {
|
||||||
-arrow-border-radius: 9px;
|
-arrow-border-radius: 8px;
|
||||||
-arrow-background-color: rgba(0,0,0,0.9);
|
-arrow-background-color: rgba(0,0,0,0.9);
|
||||||
-arrow-border-width: 2px;
|
-arrow-border-width: 2px;
|
||||||
-arrow-border-color: #5f5f5f;
|
-arrow-border-color: #a5a5a5;
|
||||||
-arrow-base: 30px;
|
-arrow-base: 24px;
|
||||||
-arrow-rise: 15px;
|
-arrow-rise: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-menu {
|
.popup-menu {
|
||||||
@ -134,6 +139,15 @@ StTooltip StLabel {
|
|||||||
border-width: 0px;
|
border-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-combo-menu {
|
||||||
|
background-color: rgba(0,0,0,0.9);
|
||||||
|
padding: 1em 0em;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 10.5pt;
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
@ -153,6 +167,10 @@ StTooltip StLabel {
|
|||||||
.popup-image-menu-item {
|
.popup-image-menu-item {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-combobox-item {
|
||||||
|
spacing: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.popup-separator-menu-item {
|
.popup-separator-menu-item {
|
||||||
-gradient-height: 2px;
|
-gradient-height: 2px;
|
||||||
-gradient-start: rgba(8,8,8,0);
|
-gradient-start: rgba(8,8,8,0);
|
||||||
@ -182,6 +200,7 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popup-inactive-menu-item {
|
.popup-inactive-menu-item {
|
||||||
|
font-weight: normal;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +236,39 @@ StTooltip StLabel {
|
|||||||
spacing: .5em;
|
spacing: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shared button properties */
|
||||||
|
|
||||||
|
.dash-search-button, .notification-button, .notification-icon-button,
|
||||||
|
.hotplug-notification-item, .hotplug-resident-eject-button,
|
||||||
|
.modal-dialog-button {
|
||||||
|
color: white;
|
||||||
|
border: 1px solid #8b8b8b;
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: rgba(255, 255, 255, 0.2);
|
||||||
|
background-gradient-end: rgba(255, 255, 255, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-button:hover, .notification-button:hover,
|
||||||
|
.notification-icon-button:hover, .hotplug-notification-item:hover,
|
||||||
|
.hotplug-resident-eject-button:hover, .modal-dialog-button:hover {
|
||||||
|
background-gradient-start: rgba(255, 255, 255, 0.3);
|
||||||
|
background-gradient-end: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-button:selected, .notification-button:focus,
|
||||||
|
.notification-icon-button:focus, .hotplug-notification-item:focus,
|
||||||
|
.modal-dialog-button:focus {
|
||||||
|
border: 2px solid #8b8b8b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-button:active, .dash-search-button:pressed,
|
||||||
|
.notification-button:active, .notification-icon-button:active,
|
||||||
|
.hotplug-notification-item:active, .hotplug-resident-eject-button:active,
|
||||||
|
.modal-dialog-button:active, .modal-dialog-button:pressed {
|
||||||
|
background-gradient-start: rgba(255, 255, 255, 0);
|
||||||
|
background-gradient-end: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
/* Panel */
|
/* Panel */
|
||||||
|
|
||||||
#panel {
|
#panel {
|
||||||
@ -258,7 +310,7 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel-corner:active,
|
.panel-corner:active,
|
||||||
.panel-corner:checked,
|
.panel-corner:overview,
|
||||||
.panel-corner:focus {
|
.panel-corner:focus {
|
||||||
-panel-corner-inner-border-color: rgba(255,255,255,0.8);
|
-panel-corner-inner-border-color: rgba(255,255,255,0.8);
|
||||||
}
|
}
|
||||||
@ -295,7 +347,7 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel-button:active,
|
.panel-button:active,
|
||||||
.panel-button:checked,
|
.panel-button:overview,
|
||||||
.panel-button:focus {
|
.panel-button:focus {
|
||||||
border-image: url("panel-button-border.svg") 10 10 0 2;
|
border-image: url("panel-button-border.svg") 10 10 0 2;
|
||||||
background-image: url("panel-button-highlight-wide.svg");
|
background-image: url("panel-button-highlight-wide.svg");
|
||||||
@ -315,6 +367,10 @@ StTooltip StLabel {
|
|||||||
icon-shadow: black 0px 2px 2px;
|
icon-shadow: black 0px 2px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-menu {
|
||||||
|
-boxpointer-gap: 4px
|
||||||
|
}
|
||||||
|
|
||||||
/* The rounded panel corners we draw don't
|
/* The rounded panel corners we draw don't
|
||||||
* support transitions, so disable transitions
|
* support transitions, so disable transitions
|
||||||
* for the buttons at the left/right edges
|
* for the buttons at the left/right edges
|
||||||
@ -331,6 +387,49 @@ StTooltip StLabel {
|
|||||||
spacing: 4px;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chooser-user-icon:hover {
|
||||||
|
border: 2px solid #bbbbbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chooser-user-name {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chooser-combo {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chooser-combo.popup-combo-menu {
|
||||||
|
background-color: rgba(0,0,0,0.7);
|
||||||
|
padding: .4em 0em;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #5f5f5f;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 10.5pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-chooser-status-item,
|
||||||
|
.status-chooser-combo > .popup-combobox-item {
|
||||||
|
spacing: .4em;
|
||||||
|
}
|
||||||
|
|
||||||
#legacyTray {
|
#legacyTray {
|
||||||
spacing: 14px;
|
spacing: 14px;
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
@ -353,7 +452,6 @@ StTooltip StLabel {
|
|||||||
|
|
||||||
#overview {
|
#overview {
|
||||||
spacing: 12px;
|
spacing: 12px;
|
||||||
background-color: rgba(0,0,0,0.6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.window-caption {
|
.window-caption {
|
||||||
@ -458,6 +556,7 @@ StTooltip StLabel {
|
|||||||
background-gradient-start: rgba(5,5,6,0.1);
|
background-gradient-start: rgba(5,5,6,0.1);
|
||||||
background-gradient-end: rgba(254,254,254,0.1);
|
background-gradient-end: rgba(254,254,254,0.1);
|
||||||
background-gradient-direction: vertical;
|
background-gradient-direction: vertical;
|
||||||
|
selected-color: black;
|
||||||
caret-color: rgb(128, 128, 128);
|
caret-color: rgb(128, 128, 128);
|
||||||
caret-size: 1px;
|
caret-size: 1px;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
@ -545,27 +644,26 @@ StTooltip StLabel {
|
|||||||
spacing: 12px;
|
spacing: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Text labels are an odd number of pixels tall. The uneven top and bottom
|
||||||
|
* padding compensates for this and ensures that the label is vertically
|
||||||
|
* centered */
|
||||||
.dash-search-button {
|
.dash-search-button {
|
||||||
background-gradient-direction: vertical;
|
|
||||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
|
||||||
background-gradient-end: rgba(255, 255, 255, 0);
|
|
||||||
border: 1px solid #808080;
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
height: 32px;
|
padding-top: 4px;
|
||||||
|
padding-bottom: 5px;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dash-search-button:selected,
|
.dash-search-button:selected {
|
||||||
.dash-search-button:hover {
|
padding-top: 3px;
|
||||||
background-gradient-direction: vertical;
|
padding-bottom: 4px;
|
||||||
background-gradient-start: rgba(255, 255, 255, 0.4);
|
width: 298px;
|
||||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dash-search-button-label {
|
.dash-search-button-label {
|
||||||
color: #cccccc;
|
color: white;
|
||||||
font-size: 12pt;
|
font-size: 11pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apps */
|
/* Apps */
|
||||||
@ -575,6 +673,11 @@ StTooltip StLabel {
|
|||||||
-shell-grid-item-size: 118px;
|
-shell-grid-item-size: 118px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact-grid {
|
||||||
|
spacing: 36px;
|
||||||
|
-shell-grid-item-size: 272px; /* 2 * -shell-grid-item-size + spacing */
|
||||||
|
}
|
||||||
|
|
||||||
.icon-grid .overview-icon {
|
.icon-grid .overview-icon {
|
||||||
icon-size: 96px;
|
icon-size: 96px;
|
||||||
}
|
}
|
||||||
@ -641,11 +744,57 @@ StTooltip StLabel {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
width: 272px; /* Same width as two normal results + spacing */
|
||||||
|
height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 3px;
|
||||||
|
border: 1px rgba(0,0,0,0);
|
||||||
|
transition-duration: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-content {
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 8px;
|
||||||
|
width: 232px;
|
||||||
|
height: 84px;
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-icon {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-details {
|
||||||
|
padding: 6px 8px 11px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-details-alias {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-bottom: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-details-status {
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-details-status-icon {
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
transition-duration: 100;
|
||||||
|
}
|
||||||
|
|
||||||
.app-well-app.running > .overview-icon {
|
.app-well-app.running > .overview-icon {
|
||||||
text-shadow: black 0px 2px 2px;
|
text-shadow: black 0px 2px 2px;
|
||||||
background-image: url("running-indicator.svg");
|
background-image: url("running-indicator.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact:selected,
|
||||||
.app-well-app:selected > .overview-icon,
|
.app-well-app:selected > .overview-icon,
|
||||||
.search-result-content:selected > .overview-icon {
|
.search-result-content:selected > .overview-icon {
|
||||||
background-color: rgba(255,255,255,0.33);
|
background-color: rgba(255,255,255,0.33);
|
||||||
@ -659,6 +808,7 @@ StTooltip StLabel {
|
|||||||
transition-duration: 100;
|
transition-duration: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact:focus,
|
||||||
.app-well-app:focus > .overview-icon,
|
.app-well-app:focus > .overview-icon,
|
||||||
.search-result-content:focus > .overview-icon {
|
.search-result-content:focus > .overview-icon {
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
@ -718,6 +868,8 @@ StTooltip StLabel {
|
|||||||
.lg-dialog StEntry
|
.lg-dialog StEntry
|
||||||
{
|
{
|
||||||
color: #88ff66;
|
color: #88ff66;
|
||||||
|
selection-background-color: #88ff66;
|
||||||
|
selected-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lg-obj-inspector-title
|
.lg-obj-inspector-title
|
||||||
@ -791,9 +943,9 @@ StTooltip StLabel {
|
|||||||
|
|
||||||
/* Calendar popup */
|
/* Calendar popup */
|
||||||
|
|
||||||
#calendarArea {
|
#calendarEventsArea {
|
||||||
/* this is the width of the entire popup */
|
/* this is the width of the second column of the popup */
|
||||||
min-width: 600px;
|
min-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-vertical-separator {
|
.calendar-vertical-separator {
|
||||||
@ -1012,26 +1164,35 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#notification {
|
#notification {
|
||||||
font-size: 12pt;
|
font-size: 11pt;
|
||||||
border-radius: 5px 5px 0px 0px;
|
border-radius: 10px 10px 0px 0px;
|
||||||
background: rgba(0,0,0,0.9);
|
background: rgba(0,0,0,0.8);
|
||||||
padding: 8px 8px 4px 8px;
|
padding: 8px 8px 4px 8px;
|
||||||
spacing-rows: 10px;
|
spacing-rows: 10px;
|
||||||
spacing-columns: 10px;
|
spacing-columns: 10px;
|
||||||
width: 34em;
|
width: 34em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multi-line-notification {
|
#notification.multi-line-notification {
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
|
||||||
|
taken into account during allocation, so its height ends up being limited by the height
|
||||||
|
of the content in the other rows. To avoid showing a stretched image, we set the minimum
|
||||||
|
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
|
||||||
|
.notification-with-image {
|
||||||
|
min-height: 159px;
|
||||||
|
}
|
||||||
|
|
||||||
.summary-boxpointer {
|
.summary-boxpointer {
|
||||||
-arrow-border-radius: 9px;
|
-arrow-border-radius: 8px;
|
||||||
-arrow-background-color: rgba(0,0,0,0.9);
|
-arrow-background-color: rgba(0,0,0,0.9);
|
||||||
-arrow-border-width: 2px;
|
-arrow-border-width: 2px;
|
||||||
-arrow-border-color: #5f5f5f;
|
-arrow-border-color: #a5a5a5;
|
||||||
-arrow-base: 30px;
|
-arrow-base: 24px;
|
||||||
-arrow-rise: 15px;
|
-arrow-rise: 11px;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-boxpointer #notification {
|
.summary-boxpointer #notification {
|
||||||
@ -1085,60 +1246,100 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#notification-actions {
|
#notification-actions {
|
||||||
spacing: 5px;
|
spacing: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-button {
|
.notification-button {
|
||||||
background-color: #3c3c3c;
|
border-radius: 18px;
|
||||||
padding: 2px 14px;
|
font-size: 11pt;
|
||||||
border-radius: 12px;
|
padding: 4px 42px 5px;
|
||||||
border: 1px solid #181818;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-button:hover {
|
|
||||||
border: 1px solid #a1a1a1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-button:focus {
|
.notification-button:focus {
|
||||||
background-color: #666666;
|
padding: 3px 41px 4px;
|
||||||
}
|
|
||||||
|
|
||||||
.notification-button:active {
|
|
||||||
border: 1px solid #a1a1a1;
|
|
||||||
background-color: #2b2b2b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-icon-button {
|
.notification-icon-button {
|
||||||
border: 2px rgba(0,0,0,0.0);
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-icon-button:hover {
|
|
||||||
border: 2px rgba(161,161,161,0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-icon-button:focus {
|
.notification-icon-button:focus {
|
||||||
background: rgba(192,192,192,0.7);
|
padding: 4px;
|
||||||
}
|
|
||||||
|
|
||||||
.notification-icon-button:active {
|
|
||||||
background: rgba(128,128,128,0.7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-icon-button > StIcon {
|
.notification-icon-button > StIcon {
|
||||||
icon-size: 36px;
|
icon-size: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hotplug-transient-box {
|
||||||
|
spacing: 6px;
|
||||||
|
padding: 2px 72px 2px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-notification-item {
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 18px;
|
||||||
|
font-size: 10.5pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-notification-item:focus {
|
||||||
|
padding: 1px 71px 1px 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-notification-item-icon {
|
||||||
|
icon-size: 24px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-box {
|
||||||
|
spacing: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-mount {
|
||||||
|
spacing: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-mount:hover {
|
||||||
|
background-gradient-direction: horizontal;
|
||||||
|
background-gradient-start: rgba(255, 255, 255, 0.1);
|
||||||
|
background-gradient-end: rgba(255, 255, 255, 0);
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-mount-label {
|
||||||
|
color: inherit;
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-mount-icon {
|
||||||
|
icon-size: 24px;
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-eject-icon {
|
||||||
|
icon-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotplug-resident-eject-button {
|
||||||
|
padding: 7px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-log-message {
|
.chat-log-message {
|
||||||
color: #888888;
|
color: #888888;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-received {
|
.chat-group-sent, .chat-group-meta {
|
||||||
background-gradient-direction: horizontal;
|
padding: 8px 0;
|
||||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
}
|
||||||
background-gradient-end: rgba(255, 255, 255, 0);
|
|
||||||
|
|
||||||
|
.chat-sent {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
@ -1149,23 +1350,20 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-sent {
|
.chat-sent {
|
||||||
background-gradient-direction: horizontal;
|
padding-left: 18pt;
|
||||||
background-gradient-start: rgba(255, 255, 255, 0);
|
|
||||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
|
||||||
|
|
||||||
padding-left: 4px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
color: #7E7E7E;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-sent:rtl {
|
.chat-sent:rtl {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-right: 4px;
|
padding-right: 18pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-meta-message {
|
.chat-meta-message {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 10.5pt;
|
font-size: 9pt;
|
||||||
color: #bbbbbb;
|
color: #bbbbbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1174,22 +1372,35 @@ StTooltip StLabel {
|
|||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subscription-message {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
#notification StEntry {
|
#notification StEntry {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #565656;
|
|
||||||
color: #a8a8a8;
|
color: #a8a8a8;
|
||||||
background-color: #404040;
|
selected-color: black;
|
||||||
caret-color: #ffffff;
|
border: 1px solid rgba(245,245,245,0.2);
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: rgb(200,200,200);
|
||||||
|
background-gradient-end: white;
|
||||||
|
transition-duration: 300;
|
||||||
|
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||||
|
|
||||||
|
caret-color: #a8a8a8;
|
||||||
caret-size: 1px;
|
caret-size: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#notification StEntry:focus {
|
#notification StEntry:focus {
|
||||||
border: 1px solid #3a3a3a;
|
border: 1px solid #8b8b8b;
|
||||||
color: #545454;
|
color: #333333;
|
||||||
background-color: #e8e8e8;
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: rgb(200,200,200);
|
||||||
|
background-gradient-end: white;
|
||||||
|
|
||||||
caret-color: #545454;
|
caret-color: #545454;
|
||||||
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
|
selection-background-color: #808080;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The spacing and padding on the summary is tricky; we want to keep
|
/* The spacing and padding on the summary is tricky; we want to keep
|
||||||
@ -1255,6 +1466,16 @@ StTooltip StLabel {
|
|||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.summary-source-counter {
|
||||||
|
color: white;
|
||||||
|
background-color: #3465A4;
|
||||||
|
text-shadow: black 1px 1px 0;
|
||||||
|
font-size: 9pt;
|
||||||
|
border-radius: 1em;
|
||||||
|
min-height: 1em;
|
||||||
|
min-width: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.source-title {
|
.source-title {
|
||||||
font-size: 9pt;
|
font-size: 9pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -1403,7 +1624,7 @@ StTooltip StLabel {
|
|||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
background-color: rgba(0.0, 0.0, 0.0, 0.9);
|
background-color: rgba(0.0, 0.0, 0.0, 0.9);
|
||||||
border: 2px solid #868686;
|
border: 2px solid #868686;
|
||||||
color: #ffffff;
|
color: #babdb6;
|
||||||
|
|
||||||
padding-right: 42px;
|
padding-right: 42px;
|
||||||
padding-left: 42px;
|
padding-left: 42px;
|
||||||
@ -1416,37 +1637,21 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog-button {
|
.modal-dialog-button {
|
||||||
border: 1px solid #8b8b8b;
|
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
font-size: 10.5pt;
|
font-size: 11pt;
|
||||||
|
color: white;
|
||||||
|
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
padding: 4px 32px 5px;
|
||||||
padding-left: 32px;
|
|
||||||
padding-right: 32px;
|
|
||||||
padding-top: 8px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
|
|
||||||
background-gradient-direction: vertical;
|
|
||||||
background-gradient-start: #29323b;
|
|
||||||
background-gradient-end: #121a24;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog-button:active,
|
.modal-dialog-button:disabled {
|
||||||
.modal-dialog-button:pressed {
|
color: rgb(60, 60, 60);
|
||||||
border-color: #a5a5a5;
|
|
||||||
background-gradient-start: #121a24;
|
|
||||||
background-gradient-end: #29323b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog-button:focus {
|
.modal-dialog-button:focus {
|
||||||
border: 2px solid #a5a5a5;
|
padding: 3px 31px 4px;
|
||||||
|
|
||||||
padding-left: 31px;
|
|
||||||
padding-right: 31px;
|
|
||||||
padding-top: 7px;
|
|
||||||
padding-bottom: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run Dialog */
|
/* Run Dialog */
|
||||||
@ -1470,6 +1675,8 @@ StTooltip StLabel {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
width: 23em;
|
width: 23em;
|
||||||
color: white;
|
color: white;
|
||||||
|
selection-background-color: white;
|
||||||
|
selected-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.run-dialog {
|
.run-dialog {
|
||||||
@ -1573,6 +1780,90 @@ StTooltip StLabel {
|
|||||||
color: #444444;
|
color: #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ShellMountOperation Dialogs */
|
||||||
|
.shell-mount-operation-icon {
|
||||||
|
icon-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mount-password-reask {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog,
|
||||||
|
.mount-question-dialog {
|
||||||
|
spacing: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-subject,
|
||||||
|
.mount-question-dialog-subject {
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #666666;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-left: 17px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-subject:rtl,
|
||||||
|
.mount-question-dialog-subject:rtl {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-description,
|
||||||
|
.mount-question-dialog-description {
|
||||||
|
font-size: 10pt;
|
||||||
|
color: white;
|
||||||
|
padding-left: 17px;
|
||||||
|
width: 28em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-description:rtl,
|
||||||
|
.mount-question-dialog-description:rtl {
|
||||||
|
padding-right: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list {
|
||||||
|
font-size: 10pt;
|
||||||
|
max-height: 200px;
|
||||||
|
padding-top: 24px;
|
||||||
|
padding-left: 49px;
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list:rtl {
|
||||||
|
padding-right: 49px;
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item:ltr {
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item:rtl {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item-icon:ltr {
|
||||||
|
padding-right: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item-icon:rtl {
|
||||||
|
padding-left: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-processes-dialog-app-list-item-name {
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
/* PolicyKit Authentication Dialog */
|
/* PolicyKit Authentication Dialog */
|
||||||
.polkit-dialog {
|
.polkit-dialog {
|
||||||
/* this is the width of the entire modal popup */
|
/* this is the width of the entire modal popup */
|
||||||
@ -1622,9 +1913,16 @@ StTooltip StLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.polkit-dialog-password-entry {
|
.polkit-dialog-password-entry {
|
||||||
background-color: white;
|
background-gradient-start: rgb(236,236,236);
|
||||||
|
background-gradient-end: white;
|
||||||
|
background-gradient-direction: vertical;
|
||||||
color: black;
|
color: black;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
border: 2px solid #555753;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-password-entry:focus {
|
||||||
|
border: 2px solid #3465a4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.polkit-dialog-error-label {
|
.polkit-dialog-error-label {
|
||||||
@ -1645,6 +1943,17 @@ StTooltip StLabel {
|
|||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.network-dialog-show-password-checkbox {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
font-size: 10pt;
|
||||||
|
color: white;
|
||||||
|
spacing: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.network-dialog-secret-table {
|
||||||
|
spacing-rows: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Magnifier */
|
/* Magnifier */
|
||||||
|
|
||||||
@ -1655,3 +1964,58 @@ StTooltip StLabel {
|
|||||||
.magnifier-zoom-region.full-screen {
|
.magnifier-zoom-region.full-screen {
|
||||||
border-width: 0px;
|
border-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* On-screen Keyboard */
|
||||||
|
|
||||||
|
#keyboard {
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-layout {
|
||||||
|
spacing: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-row {
|
||||||
|
spacing: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-key {
|
||||||
|
min-height: 30px;
|
||||||
|
min-width: 30px;
|
||||||
|
background-gradient-start: rgba(255,245,245,0.4);
|
||||||
|
background-gradient-end: rgba(105,105,105,0.1);
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
font-size: 14pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #a0a0a0;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-key:grayed {
|
||||||
|
color: #808080;
|
||||||
|
border-color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-key:checked,
|
||||||
|
.keyboard-key:hover {
|
||||||
|
background: #303030;
|
||||||
|
border: 3px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-key:active {
|
||||||
|
background: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-subkeys {
|
||||||
|
color: white;
|
||||||
|
padding: 5px;
|
||||||
|
-arrow-border-radius: 10px;
|
||||||
|
-arrow-background-color: #090909;
|
||||||
|
-arrow-border-width: 2px;
|
||||||
|
-arrow-border-color: white;
|
||||||
|
-arrow-base: 20px;
|
||||||
|
-arrow-rise: 10px;
|
||||||
|
-boxpointer-gap: 5px;
|
||||||
|
}
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24"
|
|
||||||
height="16"
|
|
||||||
id="svg6503"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47pre4 r22446"
|
|
||||||
sodipodi:docname="mosaic-view-active.svg">
|
|
||||||
<defs
|
|
||||||
id="defs6505">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 16 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="32 : 16 : 1"
|
|
||||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
|
||||||
id="perspective6511" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective6494"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="11.197802"
|
|
||||||
inkscape:cx="-15.97056"
|
|
||||||
inkscape:cy="16"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="997"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata6508">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0,-16)">
|
|
||||||
<g
|
|
||||||
style="display:inline;fill:#cbcbcb;fill-opacity:1"
|
|
||||||
transform="translate(-449.85476,-685.85869)"
|
|
||||||
id="g5306">
|
|
||||||
<rect
|
|
||||||
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none"
|
|
||||||
id="rect5308"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="450.5"
|
|
||||||
y="710.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
|
|
||||||
id="rect5310"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="462.5"
|
|
||||||
y="702.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999976000000002;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
|
|
||||||
id="rect5312"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="450.5"
|
|
||||||
y="702.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
|
|
||||||
id="rect5314"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="462.5"
|
|
||||||
y="710.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.7 KiB |
@ -1,113 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24"
|
|
||||||
height="16"
|
|
||||||
id="svg6503"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47pre4 r22446"
|
|
||||||
sodipodi:docname="New document 19">
|
|
||||||
<defs
|
|
||||||
id="defs6505">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 16 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="32 : 16 : 1"
|
|
||||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
|
||||||
id="perspective6511" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective6494"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="11.197802"
|
|
||||||
inkscape:cx="16"
|
|
||||||
inkscape:cy="16"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="997"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata6508">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0,-16)">
|
|
||||||
<g
|
|
||||||
style="display:inline"
|
|
||||||
transform="translate(-449.85476,-685.85869)"
|
|
||||||
id="g5306">
|
|
||||||
<rect
|
|
||||||
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none"
|
|
||||||
id="rect5308"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="450.5"
|
|
||||||
y="710.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
|
|
||||||
id="rect5310"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="462.5"
|
|
||||||
y="702.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
|
|
||||||
id="rect5312"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="450.5"
|
|
||||||
y="702.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
|
|
||||||
id="rect5314"
|
|
||||||
width="11"
|
|
||||||
height="7"
|
|
||||||
x="462.5"
|
|
||||||
y="710.5"
|
|
||||||
rx="0.99999958"
|
|
||||||
ry="1" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.6 KiB |
@ -1,89 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="98"
|
|
||||||
height="98"
|
|
||||||
id="svg6375"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47 r22583"
|
|
||||||
sodipodi:docname="add-workspace.svg">
|
|
||||||
<defs
|
|
||||||
id="defs6377">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 16 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="32 : 16 : 1"
|
|
||||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
|
||||||
id="perspective6383" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective6366"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="3.9590209"
|
|
||||||
inkscape:cx="56.650687"
|
|
||||||
inkscape:cy="20.635343"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="997"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata6380">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0,66)">
|
|
||||||
<g
|
|
||||||
id="g2824"
|
|
||||||
transform="matrix(11.568551,0,0,11.698271,-78.828159,-304.81518)">
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
|
||||||
d="m 11.07363,21.36834 0,6.43903"
|
|
||||||
id="path5322" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
d="m 14.29314,24.58786 -6.43902,0"
|
|
||||||
id="path5324" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
style="fill:#000000;fill-opacity:0.98823529"
|
|
||||||
d="m 48.239516,97.908047 c -0.41677,-0.05102 -1.269253,-0.222408 -1.894408,-0.380859 -4.088493,-1.036262 -7.520781,-4.753234 -8.330163,-9.021094 -0.154947,-0.817026 -0.257819,-6.68112 -0.257819,-14.696556 l 0,-13.337088 -13.829177,-0.08909 C 10.802042,60.298796 10.026884,60.268266 8.6851548,59.783022 3.6288503,57.954375 0.62673331,53.828648 0.62673331,48.708554 c 0,-5.625522 4.25936019,-10.425065 9.97721469,-11.242548 0.987903,-0.141242 7.368912,-0.254994 14.460646,-0.257791 l 12.692532,-0.005 0,-13.586668 c 0,-14.6441583 0.03287,-15.0698926 1.364686,-17.6753047 2.185477,-4.2754229 6.938193,-6.75739913 11.687647,-6.10355607 3.382776,0.46569661 6.737962,2.72496967 8.414081,5.66577137 1.480816,2.5981315 1.519067,3.0522448 1.519067,18.0333334 l 0,13.666424 12.692533,0.005 c 7.091733,0.0028 13.472742,0.116549 14.460646,0.257791 6.395303,0.914337 10.804785,6.623716 9.941157,12.871766 -0.698243,5.051565 -4.792685,9.104635 -9.941157,9.840713 -0.987904,0.141242 -7.368913,0.254995 -14.460646,0.257791 l -12.692533,0.005 0,13.801945 c 0,13.031417 -0.02798,13.895893 -0.501177,15.484801 -1.526902,5.127058 -6.919246,8.802262 -12.001914,8.18002 z"
|
|
||||||
id="path2828"
|
|
||||||
transform="translate(0,-66)" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.0 KiB |
@ -2,24 +2,62 @@
|
|||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="119.97824"
|
width="119.97824"
|
||||||
height="119.97824"
|
height="119.97824"
|
||||||
id="svg7355"
|
id="svg7355"
|
||||||
version="1.1">
|
version="1.1"
|
||||||
|
inkscape:version="0.48.1 r9760"
|
||||||
|
sodipodi:docname="running-indicator.svg">
|
||||||
|
<metadata
|
||||||
|
id="metadata4175">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#2c1cff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="1"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1141"
|
||||||
|
id="namedview4173"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="8.1348081"
|
||||||
|
inkscape:cx="81.120662"
|
||||||
|
inkscape:cy="58.117986"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="26"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g30864" />
|
||||||
<defs
|
<defs
|
||||||
id="defs7357">
|
id="defs7357">
|
||||||
<radialGradient
|
<radialGradient
|
||||||
xlink:href="#linearGradient36429"
|
xlink:href="#linearGradient36429"
|
||||||
id="radialGradient7461"
|
id="radialGradient7461"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
gradientTransform="matrix(1.0525552,0,0,1.0525552,-2.5162753,-9.0000838)"
|
gradientTransform="matrix(1.011539,0,0,0.57582113,-0.39262194,71.83807)"
|
||||||
cx="47.878681"
|
cx="47.428951"
|
||||||
cy="171.25"
|
cy="167.16817"
|
||||||
fx="47.878681"
|
fx="47.428951"
|
||||||
fy="171.25"
|
fy="167.16817"
|
||||||
r="37" />
|
r="37" />
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="linearGradient36429">
|
id="linearGradient36429">
|
||||||
@ -59,7 +97,7 @@
|
|||||||
fx="49.067139"
|
fx="49.067139"
|
||||||
cy="242.50381"
|
cy="242.50381"
|
||||||
cx="49.067139"
|
cx="49.067139"
|
||||||
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
|
gradientTransform="matrix(1.1891549,0,0,0.15252127,-9.281289,132.52772)"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
id="radialGradient7488"
|
id="radialGradient7488"
|
||||||
xlink:href="#linearGradient36471" />
|
xlink:href="#linearGradient36471" />
|
||||||
@ -72,19 +110,21 @@
|
|||||||
id="g30864"
|
id="g30864"
|
||||||
transform="translate(255.223,70.118091)">
|
transform="translate(255.223,70.118091)">
|
||||||
<rect
|
<rect
|
||||||
ry="3.5996203"
|
ry="3.4593496"
|
||||||
rx="3.5996203"
|
rx="3.4593496"
|
||||||
y="98"
|
y="99.596962"
|
||||||
x="11"
|
x="12.596948"
|
||||||
height="74"
|
height="71.116341"
|
||||||
width="74"
|
width="71.116341"
|
||||||
id="rect14000"
|
id="rect14000"
|
||||||
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
|
style="opacity:0.37187500000000001;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
|
||||||
<path
|
<path
|
||||||
id="rect34520"
|
id="rect34520"
|
||||||
d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
|
d="m 83.273151,166.72152 c 0,1.96759 -1.584022,3.55163 -3.551629,3.55163 l -63.443032,0 c -1.967608,0 -3.551648,-1.58402 -3.551643,-3.55164 0,-5.85318 0,-5.85318 0,0"
|
||||||
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
|
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
|
||||||
inkscape:connector-curvature="0" />
|
connector-curvature="0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccscc" />
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 211 B |
@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="5.8600588"
|
|
||||||
height="9"
|
|
||||||
id="svg3647"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47 r22583"
|
|
||||||
sodipodi:docname="section-more.svg">
|
|
||||||
<defs
|
|
||||||
id="defs3649">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 526.18109 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
|
||||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
|
||||||
id="perspective3655" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective3603"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="82.777778"
|
|
||||||
inkscape:cx="2.9300294"
|
|
||||||
inkscape:cy="5.466443"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="997"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata3652">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-262.78425,-490.71933)">
|
|
||||||
<path
|
|
||||||
transform="matrix(0,-0.98149546,0.71467449,0,25.404986,578.15569)"
|
|
||||||
d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:flatsided="true"
|
|
||||||
sodipodi:arg2="1.5707963"
|
|
||||||
sodipodi:arg1="0.52359878"
|
|
||||||
sodipodi:r2="2.5"
|
|
||||||
sodipodi:r1="5"
|
|
||||||
sodipodi:cy="337.5"
|
|
||||||
sodipodi:cx="84.5"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
id="path5497-5"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
sodipodi:type="star" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.8 KiB |
@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="5.8600588"
|
|
||||||
height="9"
|
|
||||||
id="svg3647"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.46+devel"
|
|
||||||
sodipodi:docname="New document 6">
|
|
||||||
<defs
|
|
||||||
id="defs3649">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 526.18109 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
|
||||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
|
||||||
id="perspective3655" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective3603"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="0.35"
|
|
||||||
inkscape:cx="112.21575"
|
|
||||||
inkscape:cy="-32.642856"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="609"
|
|
||||||
inkscape:window-height="501"
|
|
||||||
inkscape:window-x="164"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="0" />
|
|
||||||
<metadata
|
|
||||||
id="metadata3652">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-262.78425,-490.71933)">
|
|
||||||
<path
|
|
||||||
transform="matrix(0,0.98149546,-0.71467449,0,506.02358,412.28296)"
|
|
||||||
d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:flatsided="true"
|
|
||||||
sodipodi:arg2="1.5707963"
|
|
||||||
sodipodi:arg1="0.52359878"
|
|
||||||
sodipodi:r2="2.5"
|
|
||||||
sodipodi:r1="5"
|
|
||||||
sodipodi:cy="337.5"
|
|
||||||
sodipodi:cx="84.5"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
id="path5497-5"
|
|
||||||
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
|
||||||
sodipodi:type="star" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 531 B |
@ -1,81 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24"
|
|
||||||
height="16"
|
|
||||||
id="svg6446"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47pre4 r22446"
|
|
||||||
sodipodi:docname="single-view-active.svg">
|
|
||||||
<defs
|
|
||||||
id="defs6448">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 16 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="32 : 16 : 1"
|
|
||||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
|
||||||
id="perspective6454" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective6441"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="11.197802"
|
|
||||||
inkscape:cx="0.014720032"
|
|
||||||
inkscape:cy="16"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="997"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata6451">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0,-17)">
|
|
||||||
<rect
|
|
||||||
ry="0.5"
|
|
||||||
rx="0.49999979"
|
|
||||||
y="17.483809"
|
|
||||||
x="0.53483802"
|
|
||||||
height="15"
|
|
||||||
width="23"
|
|
||||||
id="rect5304"
|
|
||||||
style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.4 KiB |
@ -1,81 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24"
|
|
||||||
height="16"
|
|
||||||
id="svg6446"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.47pre4 r22446"
|
|
||||||
sodipodi:docname="single-view.svg">
|
|
||||||
<defs
|
|
||||||
id="defs6448">
|
|
||||||
<inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 16 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="32 : 16 : 1"
|
|
||||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
|
||||||
id="perspective6454" />
|
|
||||||
<inkscape:perspective
|
|
||||||
id="perspective6441"
|
|
||||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
|
||||||
inkscape:vp_z="1 : 0.5 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_x="0 : 0.5 : 1"
|
|
||||||
sodipodi:type="inkscape:persp3d" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="11.197802"
|
|
||||||
inkscape:cx="0.014720032"
|
|
||||||
inkscape:cy="16"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:grid-bbox="true"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:window-width="1680"
|
|
||||||
inkscape:window-height="997"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="26"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata6451">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
transform="translate(0,-17)">
|
|
||||||
<rect
|
|
||||||
ry="0.5"
|
|
||||||
rx="0.49999979"
|
|
||||||
y="17.483809"
|
|
||||||
x="0.53483802"
|
|
||||||
height="15"
|
|
||||||
width="23"
|
|
||||||
id="rect5304"
|
|
||||||
style="fill:#626262;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.4 KiB |
@ -13,8 +13,8 @@
|
|||||||
height="22"
|
height="22"
|
||||||
id="svg3199"
|
id="svg3199"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.47 r22583"
|
inkscape:version="0.48.1 r9760"
|
||||||
sodipodi:docname="New document 11">
|
sodipodi:docname="toggle-on-intl.svg">
|
||||||
<defs
|
<defs
|
||||||
id="defs3201">
|
id="defs3201">
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
@ -39,14 +39,14 @@
|
|||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="0.35"
|
inkscape:zoom="1"
|
||||||
inkscape:cx="32.500004"
|
inkscape:cx="49.147112"
|
||||||
inkscape:cy="10.999997"
|
inkscape:cy="17.532036"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:window-width="609"
|
inkscape:window-width="1412"
|
||||||
inkscape:window-height="501"
|
inkscape:window-height="1067"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="26"
|
inkscape:window-y="26"
|
||||||
inkscape:window-maximized="0" />
|
inkscape:window-maximized="0" />
|
||||||
@ -58,7 +58,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@ -72,7 +72,7 @@
|
|||||||
transform="translate(-453.5,448.36218)"
|
transform="translate(-453.5,448.36218)"
|
||||||
id="g16453">
|
id="g16453">
|
||||||
<rect
|
<rect
|
||||||
style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
id="rect16256-9-4"
|
id="rect16256-9-4"
|
||||||
width="63.000004"
|
width="63.000004"
|
||||||
height="19"
|
height="19"
|
||||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
@ -13,8 +13,8 @@
|
|||||||
height="22"
|
height="22"
|
||||||
id="svg2857"
|
id="svg2857"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.47 r22583"
|
inkscape:version="0.48.1 r9760"
|
||||||
sodipodi:docname="New document 2">
|
sodipodi:docname="toggle-on-us.svg">
|
||||||
<defs
|
<defs
|
||||||
id="defs2859">
|
id="defs2859">
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
@ -40,16 +40,18 @@
|
|||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="1"
|
inkscape:zoom="1"
|
||||||
inkscape:cx="-69.642856"
|
inkscape:cx="19.689855"
|
||||||
inkscape:cy="42.428569"
|
inkscape:cy="2.0517979"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:window-width="609"
|
inkscape:window-width="941"
|
||||||
inkscape:window-height="501"
|
inkscape:window-height="751"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="2577"
|
||||||
inkscape:window-y="26"
|
inkscape:window-y="206"
|
||||||
inkscape:window-maximized="0" />
|
inkscape:window-maximized="0"
|
||||||
|
borderlayer="true"
|
||||||
|
inkscape:showpageshadow="false" />
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata2862">
|
id="metadata2862">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
@ -58,7 +60,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@ -72,7 +74,7 @@
|
|||||||
transform="translate(-351.35714,708.36218)"
|
transform="translate(-351.35714,708.36218)"
|
||||||
id="g16453">
|
id="g16453">
|
||||||
<rect
|
<rect
|
||||||
style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||||
id="rect16256-9-4"
|
id="rect16256-9-4"
|
||||||
width="63.000004"
|
width="63.000004"
|
||||||
height="19"
|
height="19"
|
||||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.8 KiB |
@ -2,6 +2,8 @@
|
|||||||
jsdir = $(pkgdatadir)/js
|
jsdir = $(pkgdatadir)/js
|
||||||
|
|
||||||
nobase_dist_js_DATA = \
|
nobase_dist_js_DATA = \
|
||||||
|
gdm/batch.js \
|
||||||
|
gdm/loginDialog.js \
|
||||||
misc/config.js \
|
misc/config.js \
|
||||||
misc/docInfo.js \
|
misc/docInfo.js \
|
||||||
misc/fileUtils.js \
|
misc/fileUtils.js \
|
||||||
@ -10,14 +12,17 @@ nobase_dist_js_DATA = \
|
|||||||
misc/history.js \
|
misc/history.js \
|
||||||
misc/modemManager.js \
|
misc/modemManager.js \
|
||||||
misc/params.js \
|
misc/params.js \
|
||||||
|
misc/screenSaver.js \
|
||||||
misc/util.js \
|
misc/util.js \
|
||||||
perf/core.js \
|
perf/core.js \
|
||||||
ui/altTab.js \
|
ui/altTab.js \
|
||||||
ui/appDisplay.js \
|
ui/appDisplay.js \
|
||||||
ui/appFavorites.js \
|
ui/appFavorites.js \
|
||||||
|
ui/automountManager.js \
|
||||||
|
ui/autorunManager.js \
|
||||||
ui/boxpointer.js \
|
ui/boxpointer.js \
|
||||||
ui/calendar.js \
|
ui/calendar.js \
|
||||||
ui/chrome.js \
|
ui/contactDisplay.js \
|
||||||
ui/ctrlAltTab.js \
|
ui/ctrlAltTab.js \
|
||||||
ui/dash.js \
|
ui/dash.js \
|
||||||
ui/dateMenu.js \
|
ui/dateMenu.js \
|
||||||
@ -27,6 +32,8 @@ nobase_dist_js_DATA = \
|
|||||||
ui/environment.js \
|
ui/environment.js \
|
||||||
ui/extensionSystem.js \
|
ui/extensionSystem.js \
|
||||||
ui/iconGrid.js \
|
ui/iconGrid.js \
|
||||||
|
ui/keyboard.js \
|
||||||
|
ui/layout.js \
|
||||||
ui/lightbox.js \
|
ui/lightbox.js \
|
||||||
ui/link.js \
|
ui/link.js \
|
||||||
ui/lookingGlass.js \
|
ui/lookingGlass.js \
|
||||||
@ -35,6 +42,8 @@ nobase_dist_js_DATA = \
|
|||||||
ui/main.js \
|
ui/main.js \
|
||||||
ui/messageTray.js \
|
ui/messageTray.js \
|
||||||
ui/modalDialog.js \
|
ui/modalDialog.js \
|
||||||
|
ui/networkAgent.js \
|
||||||
|
ui/shellMountOperation.js \
|
||||||
ui/notificationDaemon.js \
|
ui/notificationDaemon.js \
|
||||||
ui/overview.js \
|
ui/overview.js \
|
||||||
ui/panel.js \
|
ui/panel.js \
|
||||||
@ -48,7 +57,6 @@ nobase_dist_js_DATA = \
|
|||||||
ui/searchDisplay.js \
|
ui/searchDisplay.js \
|
||||||
ui/shellDBus.js \
|
ui/shellDBus.js \
|
||||||
ui/statusIconDispatcher.js \
|
ui/statusIconDispatcher.js \
|
||||||
ui/statusMenu.js \
|
|
||||||
ui/status/accessibility.js \
|
ui/status/accessibility.js \
|
||||||
ui/status/keyboard.js \
|
ui/status/keyboard.js \
|
||||||
ui/status/network.js \
|
ui/status/network.js \
|
||||||
@ -57,6 +65,7 @@ nobase_dist_js_DATA = \
|
|||||||
ui/status/bluetooth.js \
|
ui/status/bluetooth.js \
|
||||||
ui/telepathyClient.js \
|
ui/telepathyClient.js \
|
||||||
ui/tweener.js \
|
ui/tweener.js \
|
||||||
|
ui/userMenu.js \
|
||||||
ui/viewSelector.js \
|
ui/viewSelector.js \
|
||||||
ui/windowAttentionHandler.js \
|
ui/windowAttentionHandler.js \
|
||||||
ui/windowManager.js \
|
ui/windowManager.js \
|
||||||
|
228
js/gdm/batch.js
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
|
*
|
||||||
|
* Copyright 2011 Red Hat, Inc
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
function Task() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.prototype = {
|
||||||
|
_init: function(scope, handler) {
|
||||||
|
if (scope)
|
||||||
|
this.scope = scope;
|
||||||
|
else
|
||||||
|
this.scope = this;
|
||||||
|
|
||||||
|
this.handler = handler;
|
||||||
|
},
|
||||||
|
|
||||||
|
run: function() {
|
||||||
|
if (this.handler)
|
||||||
|
return this.handler.call(this.scope);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(Task.prototype);
|
||||||
|
|
||||||
|
function Hold() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hold.prototype = {
|
||||||
|
__proto__: Task.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
Task.prototype._init.call(this,
|
||||||
|
this,
|
||||||
|
function () {
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._acquisitions = 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
acquire: function() {
|
||||||
|
if (this._acquisitions <= 0)
|
||||||
|
throw new Error("Cannot acquire hold after it's been released");
|
||||||
|
this._acquisitions++;
|
||||||
|
},
|
||||||
|
|
||||||
|
acquireUntilAfter: function(hold) {
|
||||||
|
if (!hold.isAcquired())
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.acquire();
|
||||||
|
let signalId = hold.connect('release', Lang.bind(this, function() {
|
||||||
|
hold.disconnect(signalId);
|
||||||
|
this.release();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
release: function() {
|
||||||
|
this._acquisitions--;
|
||||||
|
|
||||||
|
if (this._acquisitions == 0)
|
||||||
|
this.emit('release');
|
||||||
|
},
|
||||||
|
|
||||||
|
isAcquired: function() {
|
||||||
|
return this._acquisitions > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Signals.addSignalMethods(Hold.prototype);
|
||||||
|
|
||||||
|
function Batch() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Batch.prototype = {
|
||||||
|
__proto__: Task.prototype,
|
||||||
|
|
||||||
|
_init: function(scope, tasks) {
|
||||||
|
Task.prototype._init.call(this);
|
||||||
|
|
||||||
|
this.tasks = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tasks.length; i++) {
|
||||||
|
let task;
|
||||||
|
|
||||||
|
if (tasks[i] instanceof Task) {
|
||||||
|
task = tasks[i];
|
||||||
|
} else if (typeof tasks[i] == 'function') {
|
||||||
|
task = new Task(scope, tasks[i]);
|
||||||
|
} else {
|
||||||
|
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tasks.push(task);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
process: function() {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
},
|
||||||
|
|
||||||
|
runTask: function() {
|
||||||
|
if (!(this._currentTaskIndex in this.tasks)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.tasks[this._currentTaskIndex].run();
|
||||||
|
},
|
||||||
|
|
||||||
|
_finish: function() {
|
||||||
|
this.hold.release();
|
||||||
|
},
|
||||||
|
|
||||||
|
nextTask: function() {
|
||||||
|
this._currentTaskIndex++;
|
||||||
|
|
||||||
|
// if the entire batch of tasks is finished, release
|
||||||
|
// the hold and notify anyone waiting on the batch
|
||||||
|
if (this._currentTaskIndex >= this.tasks.length) {
|
||||||
|
this._finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.process();
|
||||||
|
},
|
||||||
|
|
||||||
|
_start: function() {
|
||||||
|
// acquire a hold to get released when the entire
|
||||||
|
// batch of tasks is finished
|
||||||
|
this.hold = new Hold();
|
||||||
|
this._currentTaskIndex = 0;
|
||||||
|
this.process();
|
||||||
|
},
|
||||||
|
|
||||||
|
run: function() {
|
||||||
|
this._start();
|
||||||
|
|
||||||
|
// hold may be destroyed at this point
|
||||||
|
// if we're already done running
|
||||||
|
return this.hold;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(Batch.prototype);
|
||||||
|
|
||||||
|
function ConcurrentBatch() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentBatch.prototype = {
|
||||||
|
__proto__: Batch.prototype,
|
||||||
|
|
||||||
|
_init: function(scope, tasks) {
|
||||||
|
Batch.prototype._init.call(this, scope, tasks);
|
||||||
|
},
|
||||||
|
|
||||||
|
process: function() {
|
||||||
|
let hold = this.runTask();
|
||||||
|
|
||||||
|
if (hold) {
|
||||||
|
this.hold.acquireUntilAfter(hold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regardless of the state of the just run task,
|
||||||
|
// fire off the next one, so all the tasks can run
|
||||||
|
// concurrently.
|
||||||
|
this.nextTask();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(ConcurrentBatch.prototype);
|
||||||
|
|
||||||
|
function ConsecutiveBatch() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsecutiveBatch.prototype = {
|
||||||
|
__proto__: Batch.prototype,
|
||||||
|
|
||||||
|
_init: function(scope, tasks) {
|
||||||
|
Batch.prototype._init.call(this, scope, tasks);
|
||||||
|
},
|
||||||
|
|
||||||
|
process: function() {
|
||||||
|
let hold = this.runTask();
|
||||||
|
|
||||||
|
if (hold && hold.isAcquired()) {
|
||||||
|
// This task is inhibiting the batch. Wait on it
|
||||||
|
// before processing the next one.
|
||||||
|
let signalId = hold.connect('release',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
hold.disconnect(signalId);
|
||||||
|
this.nextTask();
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// This task finished, process the next one
|
||||||
|
this.nextTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(ConsecutiveBatch.prototype);
|
1262
js/gdm/loginDialog.js
Normal file
@ -109,7 +109,8 @@ const SessionManagerIface = {
|
|||||||
name: 'org.gnome.SessionManager',
|
name: 'org.gnome.SessionManager',
|
||||||
methods: [
|
methods: [
|
||||||
{ name: 'Logout', inSignature: 'u', outSignature: '' },
|
{ name: 'Logout', inSignature: 'u', outSignature: '' },
|
||||||
{ name: 'Shutdown', inSignature: '', outSignature: '' }
|
{ name: 'Shutdown', inSignature: '', outSignature: '' },
|
||||||
|
{ name: 'CanShutdown', inSignature: '', outSignature: 'b' }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,9 +77,9 @@ HistoryManager.prototype = {
|
|||||||
this._history[this._history.length - 1] != input) {
|
this._history[this._history.length - 1] != input) {
|
||||||
|
|
||||||
this._history.push(input);
|
this._history.push(input);
|
||||||
this._historyIndex = this._history.length;
|
|
||||||
this._save();
|
this._save();
|
||||||
}
|
}
|
||||||
|
this._historyIndex = this._history.length;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEntryKeyPress: function(entry, event) {
|
_onEntryKeyPress: function(entry, event) {
|
||||||
|
53
js/misc/screenSaver.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
const ScreenSaverIface = {
|
||||||
|
name: 'org.gnome.ScreenSaver',
|
||||||
|
methods: [{ name: 'GetActive',
|
||||||
|
inSignature: '',
|
||||||
|
outSignature: 'b' },
|
||||||
|
{ name: 'Lock',
|
||||||
|
inSignature: '' },
|
||||||
|
{ name: 'SetActive',
|
||||||
|
inSignature: 'b' }],
|
||||||
|
signals: [{ name: 'ActiveChanged',
|
||||||
|
inSignature: 'b' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
function ScreenSaverProxy() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenSaverProxy.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
DBus.session.proxifyObject(this,
|
||||||
|
'org.gnome.ScreenSaver',
|
||||||
|
'/org/gnome/ScreenSaver');
|
||||||
|
|
||||||
|
DBus.session.watch_name('org.gnome.ScreenSaver',
|
||||||
|
false, // do not launch a name-owner if none exists
|
||||||
|
Lang.bind(this, this._onSSAppeared),
|
||||||
|
Lang.bind(this, this._onSSVanished));
|
||||||
|
|
||||||
|
this.screenSaverActive = false;
|
||||||
|
this.connect('ActiveChanged',
|
||||||
|
Lang.bind(this, this._onActiveChanged));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSSAppeared: function(owner) {
|
||||||
|
this.GetActiveRemote(Lang.bind(this, function(isActive) {
|
||||||
|
this.screenSaverActive = isActive;
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSSVanished: function(oldOwner) {
|
||||||
|
this.screenSaverActive = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onActiveChanged: function(object, isActive) {
|
||||||
|
this.screenSaverActive = isActive;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DBus.proxifyPrototype(ScreenSaverProxy.prototype, ScreenSaverIface);
|
@ -7,9 +7,6 @@ const Shell = imports.gi.Shell;
|
|||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
|
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
|
||||||
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
|
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
|
||||||
|
|
||||||
@ -48,7 +45,7 @@ function spawn(argv) {
|
|||||||
// occur when trying to parse or start the program.
|
// occur when trying to parse or start the program.
|
||||||
function spawnCommandLine(command_line) {
|
function spawnCommandLine(command_line) {
|
||||||
try {
|
try {
|
||||||
let [success, argc, argv] = GLib.shell_parse_argv(command_line);
|
let [success, argv] = GLib.shell_parse_argv(command_line);
|
||||||
trySpawn(argv);
|
trySpawn(argv);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
_handleSpawnError(command_line, err);
|
_handleSpawnError(command_line, err);
|
||||||
@ -88,10 +85,10 @@ function trySpawn(argv)
|
|||||||
// Runs @command_line in the background. If launching @command_line
|
// Runs @command_line in the background. If launching @command_line
|
||||||
// fails, this will throw an error.
|
// fails, this will throw an error.
|
||||||
function trySpawnCommandLine(command_line) {
|
function trySpawnCommandLine(command_line) {
|
||||||
let success, argc, argv;
|
let success, argv;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
[success, argc, argv] = GLib.shell_parse_argv(command_line);
|
[success, argv] = GLib.shell_parse_argv(command_line);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Replace "Error invoking GLib.shell_parse_argv: " with
|
// Replace "Error invoking GLib.shell_parse_argv: " with
|
||||||
// something nicer
|
// something nicer
|
||||||
@ -150,7 +147,7 @@ const _IGNORED_WORDS = [
|
|||||||
'Incorporated',
|
'Incorporated',
|
||||||
'Ltd.',
|
'Ltd.',
|
||||||
'Limited.',
|
'Limited.',
|
||||||
'Intel?',
|
'Intel',
|
||||||
'chipset',
|
'chipset',
|
||||||
'adapter',
|
'adapter',
|
||||||
'[hex]',
|
'[hex]',
|
||||||
@ -181,7 +178,7 @@ const _IGNORED_PHRASES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function fixupPCIDescription(desc) {
|
function fixupPCIDescription(desc) {
|
||||||
desc.replace(/[_,]/, ' ');
|
desc = desc.replace(/[_,]/, ' ');
|
||||||
|
|
||||||
/* Attempt to shorten ID by ignoring certain phrases */
|
/* Attempt to shorten ID by ignoring certain phrases */
|
||||||
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
||||||
@ -197,7 +194,7 @@ function fixupPCIDescription(desc) {
|
|||||||
/* Attmept to shorten ID by ignoring certain individual words */
|
/* Attmept to shorten ID by ignoring certain individual words */
|
||||||
let words = desc.split(' ');
|
let words = desc.split(' ');
|
||||||
let out = [ ];
|
let out = [ ];
|
||||||
for (let i = 0; i < words; i++) {
|
for (let i = 0; i < words.length; i++) {
|
||||||
let item = words[i];
|
let item = words[i];
|
||||||
|
|
||||||
// skip empty items (that come out from consecutive spaces)
|
// skip empty items (that come out from consecutive spaces)
|
||||||
|
@ -113,10 +113,10 @@ function run() {
|
|||||||
|
|
||||||
for (let i = 0; i < 2; i++) {
|
for (let i = 0; i < 2; i++) {
|
||||||
Scripting.scriptEvent('applicationsShowStart');
|
Scripting.scriptEvent('applicationsShowStart');
|
||||||
Main.overview.viewSelector.switchTab('applications');
|
Main.overview._viewSelector.switchTab('applications');
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
Scripting.scriptEvent('applicationsShowDone');
|
Scripting.scriptEvent('applicationsShowDone');
|
||||||
Main.overview.viewSelector.switchTab('windows');
|
Main.overview._viewSelector.switchTab('windows');
|
||||||
yield Scripting.waitLeisure();
|
yield Scripting.waitLeisure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
115
js/ui/altTab.js
@ -14,7 +14,8 @@ const Tweener = imports.ui.tweener;
|
|||||||
|
|
||||||
const POPUP_APPICON_SIZE = 96;
|
const POPUP_APPICON_SIZE = 96;
|
||||||
const POPUP_SCROLL_TIME = 0.10; // seconds
|
const POPUP_SCROLL_TIME = 0.10; // seconds
|
||||||
const POPUP_FADE_TIME = 0.1; // seconds
|
const POPUP_DELAY_TIMEOUT = 150; // milliseconds
|
||||||
|
const POPUP_FADE_OUT_TIME = 0.1; // seconds
|
||||||
|
|
||||||
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
|
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ AltTabPopup.prototype = {
|
|||||||
this._currentWindow = -1;
|
this._currentWindow = -1;
|
||||||
this._thumbnailTimeoutId = 0;
|
this._thumbnailTimeoutId = 0;
|
||||||
this._motionTimeoutId = 0;
|
this._motionTimeoutId = 0;
|
||||||
|
this._initialDelayTimeoutId = 0;
|
||||||
|
|
||||||
this.thumbnailsVisible = false;
|
this.thumbnailsVisible = false;
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ AltTabPopup.prototype = {
|
|||||||
|
|
||||||
_allocate: function (actor, box, flags) {
|
_allocate: function (actor, box, flags) {
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
||||||
let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
|
let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
|
||||||
@ -87,7 +89,7 @@ AltTabPopup.prototype = {
|
|||||||
let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
|
let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
|
||||||
let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
|
let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
|
||||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
||||||
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
|
||||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
||||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||||
this._appSwitcher.actor.allocate(childBox, flags);
|
this._appSwitcher.actor.allocate(childBox, flags);
|
||||||
@ -97,8 +99,6 @@ AltTabPopup.prototype = {
|
|||||||
// those calculations
|
// those calculations
|
||||||
if (this._thumbnails) {
|
if (this._thumbnails) {
|
||||||
let icon = this._appIcons[this._currentApp].actor;
|
let icon = this._appIcons[this._currentApp].actor;
|
||||||
// Force a stage relayout to make sure we get the correct position
|
|
||||||
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
|
|
||||||
let [posX, posY] = icon.get_transformed_position();
|
let [posX, posY] = icon.get_transformed_position();
|
||||||
let thumbnailCenter = posX + icon.width / 2;
|
let thumbnailCenter = posX + icon.width / 2;
|
||||||
let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
|
let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
|
||||||
@ -121,7 +121,7 @@ AltTabPopup.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
show : function(backward, switch_group) {
|
show : function(backward, binding) {
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
let apps = tracker.get_running_apps ('');
|
let apps = tracker.get_running_apps ('');
|
||||||
|
|
||||||
@ -145,8 +145,12 @@ AltTabPopup.prototype = {
|
|||||||
|
|
||||||
this._appIcons = this._appSwitcher.icons;
|
this._appIcons = this._appSwitcher.icons;
|
||||||
|
|
||||||
|
// Need to force an allocation so we can figure out whether we
|
||||||
|
// need to scroll when selecting
|
||||||
|
this.actor.get_allocation_box();
|
||||||
|
|
||||||
// Make the initial selection
|
// Make the initial selection
|
||||||
if (switch_group) {
|
if (binding == 'switch_group') {
|
||||||
if (backward) {
|
if (backward) {
|
||||||
this._select(0, this._appIcons[0].cachedWindows.length - 1);
|
this._select(0, this._appIcons[0].cachedWindows.length - 1);
|
||||||
} else {
|
} else {
|
||||||
@ -155,30 +159,16 @@ AltTabPopup.prototype = {
|
|||||||
else
|
else
|
||||||
this._select(0, 0);
|
this._select(0, 0);
|
||||||
}
|
}
|
||||||
|
} else if (binding == 'switch_group_backward') {
|
||||||
|
this._select(0, this._appIcons[0].cachedWindows.length - 1);
|
||||||
|
} else if (binding == 'switch_windows_backward') {
|
||||||
|
this._select(this._appIcons.length - 1);
|
||||||
} else if (this._appIcons.length == 1) {
|
} else if (this._appIcons.length == 1) {
|
||||||
if (!backward && this._appIcons[0].cachedWindows.length > 1) {
|
this._select(0);
|
||||||
// For compatibility with the multi-app case below
|
|
||||||
this._select(0, 1, true);
|
|
||||||
} else
|
|
||||||
this._select(0);
|
|
||||||
} else if (backward) {
|
} else if (backward) {
|
||||||
this._select(this._appIcons.length - 1);
|
this._select(this._appIcons.length - 1);
|
||||||
} else {
|
} else {
|
||||||
let firstWindows = this._appIcons[0].cachedWindows;
|
this._select(1);
|
||||||
if (firstWindows.length > 1) {
|
|
||||||
let curAppNextWindow = firstWindows[1];
|
|
||||||
let nextAppWindow = this._appIcons[1].cachedWindows[0];
|
|
||||||
|
|
||||||
// If the next window of the current app is more-recently-used
|
|
||||||
// than the first window of the next app, then select it.
|
|
||||||
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
|
|
||||||
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
|
|
||||||
this._select(0, 1, true);
|
|
||||||
else
|
|
||||||
this._select(1);
|
|
||||||
} else {
|
|
||||||
this._select(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's a race condition; if the user released Alt before
|
// There's a race condition; if the user released Alt before
|
||||||
@ -192,13 +182,13 @@ AltTabPopup.prototype = {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.actor.opacity = 0;
|
// We delay showing the popup so that fast Alt+Tab users aren't
|
||||||
this.actor.show();
|
// disturbed by the popup briefly flashing.
|
||||||
Tweener.addTween(this.actor,
|
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
|
||||||
{ opacity: 255,
|
Lang.bind(this, function () {
|
||||||
time: POPUP_FADE_TIME,
|
this.actor.show();
|
||||||
transition: 'easeOutQuad'
|
this._initialDelayTimeoutId = 0;
|
||||||
});
|
}));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -229,37 +219,29 @@ AltTabPopup.prototype = {
|
|||||||
let keysym = event.get_key_symbol();
|
let keysym = event.get_key_symbol();
|
||||||
let event_state = Shell.get_event_state(event);
|
let event_state = Shell.get_event_state(event);
|
||||||
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
|
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
|
||||||
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
|
let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
|
||||||
|
|
||||||
this._disableHover();
|
this._disableHover();
|
||||||
|
|
||||||
if (action == Meta.KeyBindingAction.SWITCH_GROUP)
|
if (keysym == Clutter.Escape) {
|
||||||
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
|
|
||||||
else if (keysym == Clutter.Escape)
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
else if (this._thumbnailsFocused) {
|
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
|
||||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
|
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
|
||||||
if (backwards) {
|
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
|
||||||
if (this._currentWindow == 0 || this._currentWindow == -1)
|
this._select(this._currentApp, this._previousWindow());
|
||||||
this._select(this._previousApp());
|
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
|
||||||
else
|
this._select(backwards ? this._previousApp() : this._nextApp());
|
||||||
this._select(this._currentApp, this._previousWindow());
|
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) {
|
||||||
} else {
|
this._select(this._previousApp());
|
||||||
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
} else if (this._thumbnailsFocused) {
|
||||||
this._select(this._nextApp());
|
if (keysym == Clutter.Left)
|
||||||
else
|
|
||||||
this._select(this._currentApp, this._nextWindow());
|
|
||||||
}
|
|
||||||
else if (keysym == Clutter.Left)
|
|
||||||
this._select(this._currentApp, this._previousWindow());
|
this._select(this._currentApp, this._previousWindow());
|
||||||
else if (keysym == Clutter.Right)
|
else if (keysym == Clutter.Right)
|
||||||
this._select(this._currentApp, this._nextWindow());
|
this._select(this._currentApp, this._nextWindow());
|
||||||
else if (keysym == Clutter.Up)
|
else if (keysym == Clutter.Up)
|
||||||
this._select(this._currentApp, null, true);
|
this._select(this._currentApp, null, true);
|
||||||
} else {
|
} else {
|
||||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
|
if (keysym == Clutter.Left)
|
||||||
this._select(backwards ? this._previousApp() : this._nextApp());
|
|
||||||
else if (keysym == Clutter.Left)
|
|
||||||
this._select(this._previousApp());
|
this._select(this._previousApp());
|
||||||
else if (keysym == Clutter.Right)
|
else if (keysym == Clutter.Right)
|
||||||
this._select(this._nextApp());
|
this._select(this._nextApp());
|
||||||
@ -388,7 +370,7 @@ AltTabPopup.prototype = {
|
|||||||
if (this.actor.visible) {
|
if (this.actor.visible) {
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: POPUP_FADE_TIME,
|
time: POPUP_FADE_OUT_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this,
|
onComplete: Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
@ -409,6 +391,8 @@ AltTabPopup.prototype = {
|
|||||||
Mainloop.source_remove(this._motionTimeoutId);
|
Mainloop.source_remove(this._motionTimeoutId);
|
||||||
if (this._thumbnailTimeoutId != 0)
|
if (this._thumbnailTimeoutId != 0)
|
||||||
Mainloop.source_remove(this._thumbnailTimeoutId);
|
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||||
|
if (this._initialDelayTimeoutId != 0)
|
||||||
|
Mainloop.source_remove(this._initialDelayTimeoutId);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -601,7 +585,7 @@ SwitcherList.prototype = {
|
|||||||
this._rightArrow.opacity = this._rightGradient.opacity;
|
this._rightArrow.opacity = this._rightGradient.opacity;
|
||||||
},
|
},
|
||||||
|
|
||||||
addItem : function(item) {
|
addItem : function(item, label) {
|
||||||
let bbox = new St.Button({ style_class: 'item-box',
|
let bbox = new St.Button({ style_class: 'item-box',
|
||||||
reactive: true });
|
reactive: true });
|
||||||
|
|
||||||
@ -612,6 +596,8 @@ SwitcherList.prototype = {
|
|||||||
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
|
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
|
||||||
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
|
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
|
||||||
|
|
||||||
|
bbox.label_actor = label;
|
||||||
|
|
||||||
this._items.push(bbox);
|
this._items.push(bbox);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -644,7 +630,7 @@ SwitcherList.prototype = {
|
|||||||
this._items[this._highlighted].add_style_pseudo_class('selected');
|
this._items[this._highlighted].add_style_pseudo_class('selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
let monitor = global.get_primary_monitor();
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
|
let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
|
||||||
let [posX, posY] = this._items[index].get_transformed_position();
|
let [posX, posY] = this._items[index].get_transformed_position();
|
||||||
posX += this.actor.x;
|
posX += this.actor.x;
|
||||||
@ -672,7 +658,7 @@ SwitcherList.prototype = {
|
|||||||
|
|
||||||
_scrollToRight : function() {
|
_scrollToRight : function() {
|
||||||
this._scrollableLeft = true;
|
this._scrollableLeft = true;
|
||||||
let monitor = global.get_primary_monitor();
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
let padding = this.actor.get_theme_node().get_horizontal_padding();
|
let padding = this.actor.get_theme_node().get_horizontal_padding();
|
||||||
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
||||||
let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
|
let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
|
||||||
@ -769,7 +755,7 @@ SwitcherList.prototype = {
|
|||||||
let children = this._list.get_children();
|
let children = this._list.get_children();
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
|
let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
|
||||||
if (this.actor.allocation.x2 == primary.x + primary.width - parentRightPadding) {
|
if (this.actor.allocation.x2 == primary.x + primary.width - parentRightPadding) {
|
||||||
if (this._squareItems)
|
if (this._squareItems)
|
||||||
@ -899,7 +885,7 @@ AppSwitcher.prototype = {
|
|||||||
totalSpacing += this._separator.width + this._list.spacing;
|
totalSpacing += this._separator.width + this._list.spacing;
|
||||||
|
|
||||||
// We just assume the whole screen here due to weirdness happing with the passed width
|
// We just assume the whole screen here due to weirdness happing with the passed width
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
||||||
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
||||||
let height = 0;
|
let height = 0;
|
||||||
@ -997,7 +983,7 @@ AppSwitcher.prototype = {
|
|||||||
|
|
||||||
_addIcon : function(appIcon) {
|
_addIcon : function(appIcon) {
|
||||||
this.icons.push(appIcon);
|
this.icons.push(appIcon);
|
||||||
this.addItem(appIcon.actor);
|
this.addItem(appIcon.actor, appIcon.label);
|
||||||
|
|
||||||
let n = this._arrows.length;
|
let n = this._arrows.length;
|
||||||
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
|
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
|
||||||
@ -1067,9 +1053,12 @@ ThumbnailList.prototype = {
|
|||||||
this._labels.push(bin);
|
this._labels.push(bin);
|
||||||
bin.add_actor(name);
|
bin.add_actor(name);
|
||||||
box.add_actor(bin);
|
box.add_actor(bin);
|
||||||
|
|
||||||
|
this.addItem(box, name);
|
||||||
|
} else {
|
||||||
|
this.addItem(box, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addItem(box);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const GMenu = imports.gi.GMenu;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const AppFavorites = imports.ui.appFavorites;
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
@ -22,6 +22,7 @@ const Tweener = imports.ui.tweener;
|
|||||||
const Workspace = imports.ui.workspace;
|
const Workspace = imports.ui.workspace;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const MAX_APPLICATION_WORK_MILLIS = 75;
|
||||||
const MENU_POPUP_TIMEOUT = 600;
|
const MENU_POPUP_TIMEOUT = 600;
|
||||||
const SCROLL_TIME = 0.1;
|
const SCROLL_TIME = 0.1;
|
||||||
|
|
||||||
@ -34,7 +35,8 @@ AlphabeticalView.prototype = {
|
|||||||
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
|
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
|
||||||
this._appSystem = Shell.AppSystem.get_default();
|
this._appSystem = Shell.AppSystem.get_default();
|
||||||
|
|
||||||
this._filterApp = null;
|
this._pendingAppLaterId = 0;
|
||||||
|
this._appIcons = {}; // desktop file id
|
||||||
|
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||||
@ -42,7 +44,7 @@ AlphabeticalView.prototype = {
|
|||||||
this.actor = new St.ScrollView({ x_fill: true,
|
this.actor = new St.ScrollView({ x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
y_align: St.Align.START,
|
y_align: St.Align.START,
|
||||||
vfade: true });
|
style_class: 'vfade' });
|
||||||
this.actor.add_actor(box);
|
this.actor.add_actor(box);
|
||||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||||
@ -61,20 +63,17 @@ AlphabeticalView.prototype = {
|
|||||||
|
|
||||||
_removeAll: function() {
|
_removeAll: function() {
|
||||||
this._grid.removeAll();
|
this._grid.removeAll();
|
||||||
this._apps = [];
|
this._appIcons = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
_addApp: function(appInfo) {
|
_addApp: function(app) {
|
||||||
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
var id = app.get_id();
|
||||||
|
let appIcon = new AppWellIcon(app);
|
||||||
|
|
||||||
this._grid.addItem(appIcon.actor);
|
this._grid.addItem(appIcon.actor);
|
||||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||||
|
|
||||||
appIcon._appInfo = appInfo;
|
this._appIcons[id] = appIcon;
|
||||||
if (this._filterApp && !this._filterApp(appInfo))
|
|
||||||
appIcon.actor.hide();
|
|
||||||
|
|
||||||
this._apps.push(appIcon);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ensureIconVisible: function(icon) {
|
_ensureIconVisible: function(icon) {
|
||||||
@ -103,25 +102,33 @@ AlphabeticalView.prototype = {
|
|||||||
transition: 'easeOutQuad' });
|
transition: 'easeOutQuad' });
|
||||||
},
|
},
|
||||||
|
|
||||||
setFilter: function(filter) {
|
setVisibleApps: function(apps) {
|
||||||
this._filterApp = filter;
|
if (apps == null) { // null implies "all"
|
||||||
for (let i = 0; i < this._apps.length; i++)
|
for (var id in this._appIcons) {
|
||||||
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
|
var icon = this._appIcons[id];
|
||||||
|
icon.actor.visible = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set everything to not-visible, then set to visible what we should see
|
||||||
|
for (var id in this._appIcons) {
|
||||||
|
var icon = this._appIcons[id];
|
||||||
|
icon.actor.visible = false;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < apps.length; i++) {
|
||||||
|
var app = apps[i];
|
||||||
|
var id = app.get_id();
|
||||||
|
var icon = this._appIcons[id];
|
||||||
|
icon.actor.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(apps) {
|
setAppList: function(apps) {
|
||||||
let ids = [];
|
|
||||||
for (let i in apps)
|
|
||||||
ids.push(i);
|
|
||||||
ids.sort(function(a, b) {
|
|
||||||
return apps[a].get_name().localeCompare(apps[b].get_name());
|
|
||||||
});
|
|
||||||
|
|
||||||
this._removeAll();
|
this._removeAll();
|
||||||
|
for (var i = 0; i < apps.length; i++) {
|
||||||
for (let i = 0; i < ids.length; i++) {
|
var app = apps[i];
|
||||||
this._addApp(apps[ids[i]]);
|
this._addApp(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,21 +149,24 @@ ViewByCategories.prototype = {
|
|||||||
// -2 is a flag to indicate that nothing is selected
|
// -2 is a flag to indicate that nothing is selected
|
||||||
// (used only before the actor is mapped the first time)
|
// (used only before the actor is mapped the first time)
|
||||||
this._currentCategory = -2;
|
this._currentCategory = -2;
|
||||||
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
|
this._categories = [];
|
||||||
this._filters.connect('scroll-event', Lang.bind(this, this._scrollFilter));
|
this._apps = null;
|
||||||
|
|
||||||
|
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
|
||||||
|
this._categoryScroll = new St.ScrollView({ x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
style_class: 'vfade' });
|
||||||
|
this._categoryScroll.add_actor(this._categoryBox);
|
||||||
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
||||||
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
|
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||||
|
|
||||||
// Always select the "All" filter when switching to the app view
|
// Always select the "All" filter when switching to the app view
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
if (this.actor.mapped && this._allFilter)
|
if (this.actor.mapped && this._allCategoryButton)
|
||||||
this._selectCategory(-1);
|
this._selectCategory(-1);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._sections = [];
|
|
||||||
|
|
||||||
// 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
|
||||||
// our real contents
|
// our real contents
|
||||||
@ -164,77 +174,100 @@ ViewByCategories.prototype = {
|
|||||||
this.actor.add(this._focusDummy);
|
this.actor.add(this._focusDummy);
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollFilter: function(actor, event) {
|
|
||||||
let direction = event.get_scroll_direction();
|
|
||||||
if (direction == Clutter.ScrollDirection.UP)
|
|
||||||
this._selectCategory(Math.max(this._currentCategory - 1, -1))
|
|
||||||
else if (direction == Clutter.ScrollDirection.DOWN)
|
|
||||||
this._selectCategory(Math.min(this._currentCategory + 1, this._sections.length - 1));
|
|
||||||
},
|
|
||||||
|
|
||||||
_selectCategory: function(num) {
|
_selectCategory: function(num) {
|
||||||
if (this._currentCategory == num) // nothing to do
|
if (this._currentCategory == num) // nothing to do
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._currentCategory = num;
|
this._currentCategory = num;
|
||||||
|
|
||||||
if (num != -1)
|
if (num != -1) {
|
||||||
this._allFilter.remove_style_pseudo_class('selected');
|
var category = this._categories[num];
|
||||||
else
|
this._allCategoryButton.remove_style_pseudo_class('selected');
|
||||||
this._allFilter.add_style_pseudo_class('selected');
|
this._view.setVisibleApps(category.apps);
|
||||||
|
} else {
|
||||||
|
this._allCategoryButton.add_style_pseudo_class('selected');
|
||||||
|
this._view.setVisibleApps(null);
|
||||||
|
}
|
||||||
|
|
||||||
this._view.setFilter(Lang.bind(this, function(app) {
|
for (var i = 0; i < this._categories.length; i++) {
|
||||||
if (num == -1)
|
|
||||||
return true;
|
|
||||||
return this._sections[num].name == app.get_section();
|
|
||||||
}));
|
|
||||||
|
|
||||||
for (let i = 0; i < this._sections.length; i++) {
|
|
||||||
if (i == num)
|
if (i == num)
|
||||||
this._sections[i].filterActor.add_style_pseudo_class('selected');
|
this._categories[i].button.add_style_pseudo_class('selected');
|
||||||
else
|
else
|
||||||
this._sections[i].filterActor.remove_style_pseudo_class('selected');
|
this._categories[i].button.remove_style_pseudo_class('selected');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_addFilter: function(name, num) {
|
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
|
||||||
|
_loadCategory: function(dir, appList) {
|
||||||
|
var iter = dir.iter();
|
||||||
|
var nextType;
|
||||||
|
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||||
|
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||||
|
var entry = iter.get_entry();
|
||||||
|
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
||||||
|
if (!entry.get_app_info().get_nodisplay())
|
||||||
|
appList.push(app);
|
||||||
|
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||||
|
this._loadCategory(iter.get_directory(), appList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_addCategory: function(name, index, dir, allApps) {
|
||||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||||
style_class: 'app-filter',
|
style_class: 'app-filter',
|
||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
can_focus: true });
|
can_focus: true });
|
||||||
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
this._selectCategory(num);
|
this._selectCategory(index);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (num != -1)
|
var apps;
|
||||||
this._sections[num] = { filterActor: button,
|
if (dir == null) {
|
||||||
name: name };
|
apps = allApps;
|
||||||
else
|
this._allCategoryButton = button;
|
||||||
this._allFilter = button;
|
} else {
|
||||||
|
apps = [];
|
||||||
|
this._loadCategory(dir, apps);
|
||||||
|
this._categories.push({ apps: apps,
|
||||||
|
name: name,
|
||||||
|
button: button });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||||
},
|
},
|
||||||
|
|
||||||
_removeAll: function() {
|
_removeAll: function() {
|
||||||
this._sections = [];
|
this._categories = [];
|
||||||
this._filters.destroy_children();
|
this._categoryBox.destroy_children();
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(apps) {
|
refresh: function() {
|
||||||
this._removeAll();
|
this._removeAll();
|
||||||
|
|
||||||
let sections = this._appSystem.get_sections();
|
var allApps = Shell.AppSystem.get_default().get_all();
|
||||||
this._apps = apps;
|
allApps.sort(function(a, b) {
|
||||||
this._view.refresh(apps);
|
return a.compare_by_name(b);
|
||||||
|
});
|
||||||
|
|
||||||
/* Translators: Filter to display all applications */
|
/* Translators: Filter to display all applications */
|
||||||
this._addFilter(_("All"), -1);
|
this._addCategory(_("All"), -1, null, allApps);
|
||||||
|
|
||||||
if (!sections)
|
var tree = this._appSystem.get_tree();
|
||||||
return;
|
var root = tree.get_root_directory();
|
||||||
|
|
||||||
for (let i = 0; i < sections.length; i++)
|
var iter = root.iter();
|
||||||
this._addFilter(sections[i], i);
|
var nextType;
|
||||||
|
var i = 0;
|
||||||
|
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||||
|
if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||||
|
var dir = iter.get_directory();
|
||||||
|
this._addCategory(dir.get_name(), i, dir);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._view.setAppList(allApps);
|
||||||
this._selectCategory(-1);
|
this._selectCategory(-1);
|
||||||
|
|
||||||
if (this._focusDummy) {
|
if (this._focusDummy) {
|
||||||
@ -268,52 +301,7 @@ AllAppDisplay.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
|
this._appView.refresh();
|
||||||
return !app.get_is_nodisplay();
|
|
||||||
});
|
|
||||||
|
|
||||||
this._appView.refresh(apps);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function BaseAppSearchProvider() {
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseAppSearchProvider.prototype = {
|
|
||||||
__proto__: Search.SearchProvider.prototype,
|
|
||||||
|
|
||||||
_init: function(name) {
|
|
||||||
Search.SearchProvider.prototype._init.call(this, name);
|
|
||||||
this._appSys = Shell.AppSystem.get_default();
|
|
||||||
},
|
|
||||||
|
|
||||||
getResultMeta: function(resultId) {
|
|
||||||
let app = this._appSys.get_app(resultId);
|
|
||||||
if (!app)
|
|
||||||
return null;
|
|
||||||
return { 'id': resultId,
|
|
||||||
'name': app.get_name(),
|
|
||||||
'createIcon': function(size) {
|
|
||||||
return app.create_icon_texture(size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
activateResult: function(id, params) {
|
|
||||||
params = Params.parse(params, { workspace: null,
|
|
||||||
timestamp: null });
|
|
||||||
|
|
||||||
let app = this._appSys.get_app(id);
|
|
||||||
app.activate(params.workspace ? params.workspace.index() : -1);
|
|
||||||
},
|
|
||||||
|
|
||||||
dragActivateResult: function(id, params) {
|
|
||||||
params = Params.parse(params, { workspace: null,
|
|
||||||
timestamp: null });
|
|
||||||
|
|
||||||
let app = this._appSys.get_app(id);
|
|
||||||
app.open_new_window(params.workspace ? params.workspace.get_index() : -1);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -322,44 +310,104 @@ function AppSearchProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppSearchProvider.prototype = {
|
AppSearchProvider.prototype = {
|
||||||
__proto__: BaseAppSearchProvider.prototype,
|
__proto__: Search.SearchProvider.prototype,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
|
Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
|
||||||
|
this._appSys = Shell.AppSystem.get_default();
|
||||||
|
},
|
||||||
|
|
||||||
|
getResultMeta: function(app) {
|
||||||
|
return { 'id': app,
|
||||||
|
'name': app.get_name(),
|
||||||
|
'createIcon': function(size) {
|
||||||
|
return app.create_icon_texture(size);
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
return this._appSys.initial_search(false, terms);
|
return this._appSys.initial_search(terms);
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
return this._appSys.subsearch(false, previousResults, terms);
|
return this._appSys.subsearch(previousResults, terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
activateResult: function(app, params) {
|
||||||
|
params = Params.parse(params, { workspace: -1,
|
||||||
|
timestamp: 0 });
|
||||||
|
|
||||||
|
let event = Clutter.get_current_event();
|
||||||
|
let modifiers = event ? Shell.get_event_state(event) : 0;
|
||||||
|
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
|
||||||
|
|
||||||
|
if (openNewWindow)
|
||||||
|
app.open_new_window(params.workspace);
|
||||||
|
else
|
||||||
|
app.activate_full(params.workspace, params.timestamp);
|
||||||
|
},
|
||||||
|
|
||||||
|
dragActivateResult: function(id, params) {
|
||||||
|
params = Params.parse(params, { workspace: -1,
|
||||||
|
timestamp: 0 });
|
||||||
|
|
||||||
|
let app = this._appSys.lookup_app(id);
|
||||||
|
app.open_new_window(workspace);
|
||||||
},
|
},
|
||||||
|
|
||||||
createResultActor: function (resultMeta, terms) {
|
createResultActor: function (resultMeta, terms) {
|
||||||
let app = this._appSys.get_app(resultMeta['id']);
|
let app = resultMeta['id'];
|
||||||
let icon = new AppWellIcon(app);
|
let icon = new AppWellIcon(app);
|
||||||
return icon.actor;
|
return icon.actor;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function PrefsSearchProvider() {
|
function SettingsSearchProvider() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
PrefsSearchProvider.prototype = {
|
SettingsSearchProvider.prototype = {
|
||||||
__proto__: BaseAppSearchProvider.prototype,
|
__proto__: Search.SearchProvider.prototype,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
|
Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
|
||||||
|
this._appSys = Shell.AppSystem.get_default();
|
||||||
|
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
||||||
|
},
|
||||||
|
|
||||||
|
getResultMeta: function(pref) {
|
||||||
|
return { 'id': pref,
|
||||||
|
'name': pref.get_name(),
|
||||||
|
'createIcon': function(size) {
|
||||||
|
return pref.create_icon_texture(size);
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
return this._appSys.initial_search(true, terms);
|
return this._appSys.search_settings(terms);
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, terms) {
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
return this._appSys.subsearch(true, previousResults, terms);
|
return this._appSys.search_settings(terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
activateResult: function(pref, params) {
|
||||||
|
params = Params.parse(params, { workspace: -1,
|
||||||
|
timestamp: 0 });
|
||||||
|
|
||||||
|
pref.activate_full(params.workspace, params.timestamp);
|
||||||
|
},
|
||||||
|
|
||||||
|
dragActivateResult: function(pref, params) {
|
||||||
|
this.activateResult(pref, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
createResultActor: function (resultMeta, terms) {
|
||||||
|
let app = resultMeta['id'];
|
||||||
|
let icon = new AppWellIcon(app);
|
||||||
|
return icon.actor;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -385,12 +433,12 @@ AppIcon.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function AppWellIcon(app, iconParams) {
|
function AppWellIcon(app, iconParams, onActivateOverride) {
|
||||||
this._init(app, iconParams);
|
this._init(app, iconParams, onActivateOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppWellIcon.prototype = {
|
AppWellIcon.prototype = {
|
||||||
_init : function(app, iconParams) {
|
_init : function(app, iconParams, onActivateOverride) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
@ -403,6 +451,10 @@ AppWellIcon.prototype = {
|
|||||||
this.icon = new AppIcon(app, iconParams);
|
this.icon = new AppIcon(app, iconParams);
|
||||||
this.actor.set_child(this.icon.actor);
|
this.actor.set_child(this.icon.actor);
|
||||||
|
|
||||||
|
this.actor.label_actor = this.icon.label;
|
||||||
|
|
||||||
|
// A function callback to override the default "app.activate()"; used by preferences
|
||||||
|
this._onActivateOverride = onActivateOverride;
|
||||||
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('clicked', Lang.bind(this, this._onClicked));
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
||||||
@ -536,24 +588,28 @@ AppWellIcon.prototype = {
|
|||||||
this.emit('launching');
|
this.emit('launching');
|
||||||
let modifiers = Shell.get_event_state(event);
|
let modifiers = Shell.get_event_state(event);
|
||||||
|
|
||||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
if (this._onActivateOverride) {
|
||||||
&& this.app.state == Shell.AppState.RUNNING) {
|
this._onActivateOverride(event);
|
||||||
this.app.open_new_window(-1);
|
|
||||||
} else {
|
} else {
|
||||||
this.app.activate(-1);
|
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||||
|
&& this.app.state == Shell.AppState.RUNNING) {
|
||||||
|
this.app.open_new_window(-1);
|
||||||
|
} else {
|
||||||
|
this.app.activate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
shellWorkspaceLaunch : function(params) {
|
shellWorkspaceLaunch : function(params) {
|
||||||
params = Params.parse(params, { workspace: null,
|
params = Params.parse(params, { workspace: -1,
|
||||||
timestamp: null });
|
timestamp: 0 });
|
||||||
|
|
||||||
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
|
this.app.open_new_window(params.workspace);
|
||||||
},
|
},
|
||||||
|
|
||||||
getDragActor: function() {
|
getDragActor: function() {
|
||||||
return this.app.create_icon_texture(Main.overview.dash.iconSize);
|
return this.app.create_icon_texture(Main.overview.dashIconSize);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns the original actor that should align with the actor
|
// Returns the original actor that should align with the actor
|
||||||
@ -576,7 +632,7 @@ AppIconMenu.prototype = {
|
|||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||||
side = St.Side.RIGHT;
|
side = St.Side.RIGHT;
|
||||||
|
|
||||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
|
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
|
||||||
|
|
||||||
// We want to keep the item hovered while the menu is up
|
// We want to keep the item hovered while the menu is up
|
||||||
this.blockSourceEvents = true;
|
this.blockSourceEvents = true;
|
||||||
@ -617,17 +673,18 @@ AppIconMenu.prototype = {
|
|||||||
item._window = windows[i];
|
item._window = windows[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (windows.length > 0)
|
if (!this._source.app.is_window_backed()) {
|
||||||
|
if (windows.length > 0)
|
||||||
|
this._appendSeparator();
|
||||||
|
|
||||||
|
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
||||||
|
|
||||||
|
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
||||||
this._appendSeparator();
|
this._appendSeparator();
|
||||||
|
|
||||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
||||||
|
: _("Add to Favorites"));
|
||||||
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
}
|
||||||
this._appendSeparator();
|
|
||||||
|
|
||||||
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
|
||||||
: _("Add to Favorites"));
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_appendSeparator: function () {
|
_appendSeparator: function () {
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
@ -30,7 +28,7 @@ AppFavorites.prototype = {
|
|||||||
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
||||||
let appSys = Shell.AppSystem.get_default();
|
let appSys = Shell.AppSystem.get_default();
|
||||||
let apps = ids.map(function (id) {
|
let apps = ids.map(function (id) {
|
||||||
return appSys.get_app(id);
|
return appSys.lookup_app(id);
|
||||||
}).filter(function (app) {
|
}).filter(function (app) {
|
||||||
return app != null;
|
return app != null;
|
||||||
});
|
});
|
||||||
@ -67,7 +65,7 @@ AppFavorites.prototype = {
|
|||||||
if (appId in this._favorites)
|
if (appId in this._favorites)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let app = Shell.AppSystem.get_default().get_app(appId);
|
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||||
|
|
||||||
if (!app)
|
if (!app)
|
||||||
return false;
|
return false;
|
||||||
@ -86,9 +84,9 @@ AppFavorites.prototype = {
|
|||||||
if (!this._addFavorite(appId, pos))
|
if (!this._addFavorite(appId, pos))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let app = Shell.AppSystem.get_default().get_app(appId);
|
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||||
|
|
||||||
Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
||||||
this._removeFavorite(appId);
|
this._removeFavorite(appId);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@ -119,8 +117,8 @@ AppFavorites.prototype = {
|
|||||||
if (!this._removeFavorite(appId))
|
if (!this._removeFavorite(appId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||||
Lang.bind(this, function () {
|
Lang.bind(this, function () {
|
||||||
this._addFavorite(appId, pos);
|
this._addFavorite(appId, pos);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
278
js/ui/automountManager.js
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
const ScreenSaver = imports.misc.screenSaver;
|
||||||
|
|
||||||
|
// GSettings keys
|
||||||
|
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
||||||
|
const SETTING_ENABLE_AUTOMOUNT = 'automount';
|
||||||
|
|
||||||
|
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
|
||||||
|
|
||||||
|
const ConsoleKitSessionIface = {
|
||||||
|
name: 'org.freedesktop.ConsoleKit.Session',
|
||||||
|
methods: [{ name: 'IsActive',
|
||||||
|
inSignature: '',
|
||||||
|
outSignature: 'b' }],
|
||||||
|
signals: [{ name: 'ActiveChanged',
|
||||||
|
inSignature: 'b' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConsoleKitSessionProxy = DBus.makeProxyClass(ConsoleKitSessionIface);
|
||||||
|
|
||||||
|
const ConsoleKitManagerIface = {
|
||||||
|
name: 'org.freedesktop.ConsoleKit.Manager',
|
||||||
|
methods: [{ name: 'GetCurrentSession',
|
||||||
|
inSignature: '',
|
||||||
|
outSignature: 'o' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
function ConsoleKitManager() {
|
||||||
|
this._init();
|
||||||
|
};
|
||||||
|
|
||||||
|
ConsoleKitManager.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.sessionActive = true;
|
||||||
|
|
||||||
|
DBus.system.proxifyObject(this,
|
||||||
|
'org.freedesktop.ConsoleKit',
|
||||||
|
'/org/freedesktop/ConsoleKit/Manager');
|
||||||
|
|
||||||
|
DBus.system.watch_name('org.freedesktop.ConsoleKit',
|
||||||
|
false, // do not launch a name-owner if none exists
|
||||||
|
Lang.bind(this, this._onManagerAppeared),
|
||||||
|
Lang.bind(this, this._onManagerVanished));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onManagerAppeared: function(owner) {
|
||||||
|
this.GetCurrentSessionRemote(Lang.bind(this, this._onCurrentSession));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onManagerVanished: function(oldOwner) {
|
||||||
|
this.sessionActive = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCurrentSession: function(session) {
|
||||||
|
this._ckSession = new ConsoleKitSessionProxy(DBus.system, 'org.freedesktop.ConsoleKit', session);
|
||||||
|
|
||||||
|
this._ckSession.connect
|
||||||
|
('ActiveChanged', Lang.bind(this, function(object, isActive) {
|
||||||
|
this.sessionActive = isActive;
|
||||||
|
}));
|
||||||
|
this._ckSession.IsActiveRemote(Lang.bind(this, function(isActive) {
|
||||||
|
this.sessionActive = isActive;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
|
||||||
|
|
||||||
|
function AutomountManager() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutomountManager.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||||
|
this._volumeQueue = [];
|
||||||
|
|
||||||
|
this.ckListener = new ConsoleKitManager();
|
||||||
|
|
||||||
|
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
|
||||||
|
this._ssProxy.connect('ActiveChanged',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._screenSaverActiveChanged));
|
||||||
|
|
||||||
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
|
this._volumeMonitor.connect('volume-added',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onVolumeAdded));
|
||||||
|
this._volumeMonitor.connect('volume-removed',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onVolumeRemoved));
|
||||||
|
this._volumeMonitor.connect('drive-connected',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onDriveConnected));
|
||||||
|
this._volumeMonitor.connect('drive-disconnected',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onDriveDisconnected));
|
||||||
|
this._volumeMonitor.connect('drive-eject-button',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onDriveEjectButton));
|
||||||
|
|
||||||
|
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||||
|
},
|
||||||
|
|
||||||
|
_screenSaverActiveChanged: function(object, isActive) {
|
||||||
|
if (!isActive) {
|
||||||
|
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
|
||||||
|
this._checkAndMountVolume(volume);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the queue anyway
|
||||||
|
this._volumeQueue = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
_startupMountAll: function() {
|
||||||
|
let volumes = this._volumeMonitor.get_volumes();
|
||||||
|
volumes.forEach(Lang.bind(this, function(volume) {
|
||||||
|
this._checkAndMountVolume(volume, { checkSession: false,
|
||||||
|
useMountOp: false });
|
||||||
|
}));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDriveConnected: function() {
|
||||||
|
// if we're not in the current ConsoleKit session,
|
||||||
|
// or screensaver is active, don't play sounds
|
||||||
|
if (!this.ckListener.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._ssProxy.screenSaverActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
global.play_theme_sound(0, 'device-added-media');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDriveDisconnected: function() {
|
||||||
|
// if we're not in the current ConsoleKit session,
|
||||||
|
// or screensaver is active, don't play sounds
|
||||||
|
if (!this.ckListener.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._ssProxy.screenSaverActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
global.play_theme_sound(0, 'device-removed-media');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDriveEjectButton: function(monitor, drive) {
|
||||||
|
// TODO: this code path is not tested, as the GVfs volume monitor
|
||||||
|
// doesn't emit this signal just yet.
|
||||||
|
if (!this.ckListener.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we force stop/eject in this case, so we don't have to pass a
|
||||||
|
// mount operation object
|
||||||
|
if (drive.can_stop()) {
|
||||||
|
drive.stop
|
||||||
|
(Gio.MountUnmountFlags.FORCE, null, null,
|
||||||
|
Lang.bind(this, function(drive, res) {
|
||||||
|
try {
|
||||||
|
drive.stop_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
log("Unable to stop the drive after drive-eject-button " + e.toString());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else if (drive.can_eject()) {
|
||||||
|
drive.eject_with_operation
|
||||||
|
(Gio.MountUnmountFlags.FORCE, null, null,
|
||||||
|
Lang.bind(this, function(drive, res) {
|
||||||
|
try {
|
||||||
|
drive.eject_with_operation_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
log("Unable to eject the drive after drive-eject-button " + e.toString());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVolumeAdded: function(monitor, volume) {
|
||||||
|
this._checkAndMountVolume(volume);
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkAndMountVolume: function(volume, params) {
|
||||||
|
params = Params.parse(params, { checkSession: true,
|
||||||
|
useMountOp: true });
|
||||||
|
|
||||||
|
if (params.checkSession) {
|
||||||
|
// if we're not in the current ConsoleKit session,
|
||||||
|
// don't attempt automount
|
||||||
|
if (!this.ckListener.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._ssProxy.screenSaverActive) {
|
||||||
|
if (this._volumeQueue.indexOf(volume) == -1)
|
||||||
|
this._volumeQueue.push(volume);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
|
||||||
|
!volume.should_automount() ||
|
||||||
|
!volume.can_mount()) {
|
||||||
|
// allow the autorun to run anyway; this can happen if the
|
||||||
|
// mount gets added programmatically later, even if
|
||||||
|
// should_automount() or can_mount() are false, like for
|
||||||
|
// blank optical media.
|
||||||
|
this._allowAutorun(volume);
|
||||||
|
this._allowAutorunExpire(volume);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.useMountOp) {
|
||||||
|
let operation = new ShellMountOperation.ShellMountOperation(volume);
|
||||||
|
this._mountVolume(volume, operation.mountOp);
|
||||||
|
} else {
|
||||||
|
this._mountVolume(volume, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_mountVolume: function(volume, operation) {
|
||||||
|
this._allowAutorun(volume);
|
||||||
|
volume.mount(0, operation, null,
|
||||||
|
Lang.bind(this, this._onVolumeMounted));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVolumeMounted: function(volume, res) {
|
||||||
|
this._allowAutorunExpire(volume);
|
||||||
|
|
||||||
|
try {
|
||||||
|
volume.mount_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
let string = e.toString();
|
||||||
|
|
||||||
|
// FIXME: needs proper error code handling instead of this
|
||||||
|
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||||
|
if (string.indexOf('No key available with this passphrase') != -1)
|
||||||
|
this._reaskPassword(volume);
|
||||||
|
else
|
||||||
|
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVolumeRemoved: function(monitor, volume) {
|
||||||
|
this._volumeQueue =
|
||||||
|
this._volumeQueue.filter(function(element) {
|
||||||
|
return (element != volume);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_reaskPassword: function(volume) {
|
||||||
|
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
|
||||||
|
this._mountVolume(volume, operation.mountOp);
|
||||||
|
},
|
||||||
|
|
||||||
|
_allowAutorun: function(volume) {
|
||||||
|
volume.allowAutorun = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allowAutorunExpire: function(volume) {
|
||||||
|
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
||||||
|
volume.allowAutorun = false;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
635
js/ui/autorunManager.js
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
|
||||||
|
// GSettings keys
|
||||||
|
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
||||||
|
const SETTING_DISABLE_AUTORUN = 'autorun-never';
|
||||||
|
const SETTING_START_APP = 'autorun-x-content-start-app';
|
||||||
|
const SETTING_IGNORE = 'autorun-x-content-ignore';
|
||||||
|
const SETTING_OPEN_FOLDER = 'autorun-x-content-open-folder';
|
||||||
|
|
||||||
|
const AutorunSetting = {
|
||||||
|
RUN: 0,
|
||||||
|
IGNORE: 1,
|
||||||
|
FILES: 2,
|
||||||
|
ASK: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const HOTPLUG_ICON_SIZE = 16;
|
||||||
|
|
||||||
|
// misc utils
|
||||||
|
function ignoreAutorunForMount(mount) {
|
||||||
|
let root = mount.get_root();
|
||||||
|
let volume = mount.get_volume();
|
||||||
|
|
||||||
|
if ((root.is_native() && !isMountRootHidden(root)) ||
|
||||||
|
(volume && volume.allowAutorun && volume.should_automount()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMountRootHidden(root) {
|
||||||
|
let path = root.get_path();
|
||||||
|
|
||||||
|
// skip any mounts in hidden directory hierarchies
|
||||||
|
return (path.indexOf('/.') != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAppForMount(app, mount) {
|
||||||
|
let files = [];
|
||||||
|
let root = mount.get_root();
|
||||||
|
let retval = false;
|
||||||
|
|
||||||
|
files.push(root);
|
||||||
|
|
||||||
|
try {
|
||||||
|
retval = app.launch(files,
|
||||||
|
global.create_app_launch_context())
|
||||||
|
} catch (e) {
|
||||||
|
log('Unable to launch the application ' + app.get_name()
|
||||||
|
+ ': ' + e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
const HotplugSnifferIface = {
|
||||||
|
name: 'org.gnome.Shell.HotplugSniffer',
|
||||||
|
methods: [{ name: 'SniffURI',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: 'as' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const HotplugSniffer = function() {
|
||||||
|
this._init();
|
||||||
|
};
|
||||||
|
|
||||||
|
HotplugSniffer.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
DBus.session.proxifyObject(this,
|
||||||
|
'org.gnome.Shell.HotplugSniffer',
|
||||||
|
'/org/gnome/Shell/HotplugSniffer');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
DBus.proxifyPrototype(HotplugSniffer.prototype, HotplugSnifferIface);
|
||||||
|
|
||||||
|
function ContentTypeDiscoverer(callback) {
|
||||||
|
this._init(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentTypeDiscoverer.prototype = {
|
||||||
|
_init: function(callback) {
|
||||||
|
this._callback = callback;
|
||||||
|
},
|
||||||
|
|
||||||
|
guessContentTypes: function(mount) {
|
||||||
|
// guess mount's content types using GIO
|
||||||
|
mount.guess_content_type(false, null,
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onContentTypeGuessed));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onContentTypeGuessed: function(mount, res) {
|
||||||
|
let contentTypes = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
contentTypes = mount.guess_content_type_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
log('Unable to guess content types on added mount ' + mount.get_name()
|
||||||
|
+ ': ' + e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentTypes.length) {
|
||||||
|
this._emitCallback(mount, contentTypes);
|
||||||
|
} else {
|
||||||
|
let root = mount.get_root();
|
||||||
|
|
||||||
|
let hotplugSniffer = new HotplugSniffer();
|
||||||
|
hotplugSniffer.SniffURIRemote
|
||||||
|
(root.get_uri(), DBus.CALL_FLAG_START,
|
||||||
|
Lang.bind(this, function(contentTypes) {
|
||||||
|
this._emitCallback(mount, contentTypes);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_emitCallback: function(mount, contentTypes) {
|
||||||
|
if (!contentTypes)
|
||||||
|
contentTypes = [];
|
||||||
|
|
||||||
|
// we're not interested in win32 software content types here
|
||||||
|
contentTypes = contentTypes.filter(function(type) {
|
||||||
|
return (type != 'x-content/win32-software');
|
||||||
|
});
|
||||||
|
|
||||||
|
let apps = [];
|
||||||
|
contentTypes.forEach(function(type) {
|
||||||
|
let app = Gio.app_info_get_default_for_type(type, false);
|
||||||
|
|
||||||
|
if (app)
|
||||||
|
apps.push(app);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (apps.length == 0)
|
||||||
|
apps.push(Gio.app_info_get_default_for_type('inode/directory', false));
|
||||||
|
|
||||||
|
this._callback(mount, apps, contentTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutorunManager() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutorunManager.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
|
this._volumeMonitor.connect('mount-added',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onMountAdded));
|
||||||
|
this._volumeMonitor.connect('mount-removed',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onMountRemoved));
|
||||||
|
|
||||||
|
this._transDispatcher = new AutorunTransientDispatcher();
|
||||||
|
this._createResidentSource();
|
||||||
|
|
||||||
|
let mounts = this._volumeMonitor.get_mounts();
|
||||||
|
|
||||||
|
mounts.forEach(Lang.bind(this, function (mount) {
|
||||||
|
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||||
|
function (mount, apps) {
|
||||||
|
this._residentSource.addMount(mount, apps);
|
||||||
|
}));
|
||||||
|
|
||||||
|
discoverer.guessContentTypes(mount);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_createResidentSource: function() {
|
||||||
|
this._residentSource = new AutorunResidentSource();
|
||||||
|
this._residentSource.connect('destroy',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._createResidentSource));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMountAdded: function(monitor, mount) {
|
||||||
|
// don't do anything if our session is not the currently
|
||||||
|
// active one
|
||||||
|
if (!Main.automountManager.ckListener.sessionActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||||
|
function (mount, apps, contentTypes) {
|
||||||
|
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||||
|
this._residentSource.addMount(mount, apps);
|
||||||
|
}));
|
||||||
|
|
||||||
|
discoverer.guessContentTypes(mount);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMountRemoved: function(monitor, mount) {
|
||||||
|
this._transDispatcher.removeMount(mount);
|
||||||
|
this._residentSource.removeMount(mount);
|
||||||
|
},
|
||||||
|
|
||||||
|
ejectMount: function(mount) {
|
||||||
|
let mountOp = new ShellMountOperation.ShellMountOperation(mount);
|
||||||
|
|
||||||
|
// first, see if we have a drive
|
||||||
|
let drive = mount.get_drive();
|
||||||
|
let volume = mount.get_volume();
|
||||||
|
|
||||||
|
if (drive &&
|
||||||
|
drive.get_start_stop_type() == Gio.DriveStartStopType.SHUTDOWN &&
|
||||||
|
drive.can_stop()) {
|
||||||
|
drive.stop(0, mountOp.mountOp, null,
|
||||||
|
Lang.bind(this, this._onStop));
|
||||||
|
} else {
|
||||||
|
if (mount.can_eject()) {
|
||||||
|
mount.eject_with_operation(0, mountOp.mountOp, null,
|
||||||
|
Lang.bind(this, this._onEject));
|
||||||
|
} else if (volume && volume.can_eject()) {
|
||||||
|
volume.eject_with_operation(0, mountOp.mountOp, null,
|
||||||
|
Lang.bind(this, this._onEject));
|
||||||
|
} else if (drive && drive.can_eject()) {
|
||||||
|
drive.eject_with_operation(0, mountOp.mountOp, null,
|
||||||
|
Lang.bind(this, this._onEject));
|
||||||
|
} else if (mount.can_unmount()) {
|
||||||
|
mount.unmount_with_operation(0, mountOp.mountOp, null,
|
||||||
|
Lang.bind(this, this._onUnmount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUnmount: function(mount, res) {
|
||||||
|
try {
|
||||||
|
mount.unmount_with_operation_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||||
|
// but we can't access the error code from JS.
|
||||||
|
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||||
|
log('Unable to eject the mount ' + mount.get_name()
|
||||||
|
+ ': ' + e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEject: function(source, res) {
|
||||||
|
try {
|
||||||
|
source.eject_with_operation_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||||
|
// but we can't access the error code from JS.
|
||||||
|
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||||
|
log('Unable to eject the drive ' + source.get_name()
|
||||||
|
+ ': ' + e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStop: function(drive, res) {
|
||||||
|
try {
|
||||||
|
drive.stop_finish(res);
|
||||||
|
} catch (e) {
|
||||||
|
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||||
|
// but we can't access the error code from JS.
|
||||||
|
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||||
|
log('Unable to stop the drive ' + drive.get_name()
|
||||||
|
+ ': ' + e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutorunResidentSource() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutorunResidentSource.prototype = {
|
||||||
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
MessageTray.Source.prototype._init.call(this, _('Removable Devices'));
|
||||||
|
|
||||||
|
this._mounts = [];
|
||||||
|
|
||||||
|
this._notification = new AutorunResidentNotification(this);
|
||||||
|
this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
|
||||||
|
},
|
||||||
|
|
||||||
|
addMount: function(mount, apps) {
|
||||||
|
if (ignoreAutorunForMount(mount))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let filtered = this._mounts.filter(function (element) {
|
||||||
|
return (element.mount == mount);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (filtered.length != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let element = { mount: mount, apps: apps };
|
||||||
|
this._mounts.push(element);
|
||||||
|
this._redisplay();
|
||||||
|
},
|
||||||
|
|
||||||
|
removeMount: function(mount) {
|
||||||
|
this._mounts =
|
||||||
|
this._mounts.filter(function (element) {
|
||||||
|
return (element.mount != mount);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._redisplay();
|
||||||
|
},
|
||||||
|
|
||||||
|
_redisplay: function() {
|
||||||
|
if (this._mounts.length == 0) {
|
||||||
|
this._notification.destroy();
|
||||||
|
this.destroy();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._notification.updateForMounts(this._mounts);
|
||||||
|
|
||||||
|
// add ourselves as a source, and push the notification
|
||||||
|
if (!Main.messageTray.contains(this)) {
|
||||||
|
Main.messageTray.add(this);
|
||||||
|
this.pushNotification(this._notification);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createNotificationIcon: function(iconSize) {
|
||||||
|
return new St.Icon ({ icon_name: 'media-removable',
|
||||||
|
icon_type: St.IconType.FULLCOLOR,
|
||||||
|
icon_size: iconSize ? iconSize : this.ICON_SIZE });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutorunResidentNotification(source) {
|
||||||
|
this._init(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutorunResidentNotification.prototype = {
|
||||||
|
__proto__: MessageTray.Notification.prototype,
|
||||||
|
|
||||||
|
_init: function(source) {
|
||||||
|
MessageTray.Notification.prototype._init.call(this, source,
|
||||||
|
source.title, null,
|
||||||
|
{ customContent: true });
|
||||||
|
|
||||||
|
// set the notification as resident
|
||||||
|
this.setResident(true);
|
||||||
|
|
||||||
|
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this.addActor(this._layout,
|
||||||
|
{ x_expand: true,
|
||||||
|
x_fill: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
updateForMounts: function(mounts) {
|
||||||
|
// remove all the layout content
|
||||||
|
this._layout.destroy_children();
|
||||||
|
|
||||||
|
for (let idx = 0; idx < mounts.length; idx++) {
|
||||||
|
let element = mounts[idx];
|
||||||
|
|
||||||
|
let actor = this._itemForMount(element.mount, element.apps);
|
||||||
|
this._layout.add(actor, { x_fill: true,
|
||||||
|
expand: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_itemForMount: function(mount, apps) {
|
||||||
|
let item = new St.BoxLayout();
|
||||||
|
|
||||||
|
// prepare the mount button content
|
||||||
|
let mountLayout = new St.BoxLayout();
|
||||||
|
|
||||||
|
let mountIcon = new St.Icon({ gicon: mount.get_icon(),
|
||||||
|
style_class: 'hotplug-resident-mount-icon' });
|
||||||
|
mountLayout.add_actor(mountIcon);
|
||||||
|
|
||||||
|
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE });
|
||||||
|
let mountLabel =
|
||||||
|
new St.Label({ text: mount.get_name(),
|
||||||
|
style_class: 'hotplug-resident-mount-label',
|
||||||
|
track_hover: true,
|
||||||
|
reactive: true });
|
||||||
|
labelBin.add_actor(mountLabel);
|
||||||
|
mountLayout.add_actor(labelBin);
|
||||||
|
|
||||||
|
let mountButton = new St.Button({ child: mountLayout,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
x_fill: true,
|
||||||
|
style_class: 'hotplug-resident-mount',
|
||||||
|
button_mask: St.ButtonMask.ONE });
|
||||||
|
item.add(mountButton, { x_align: St.Align.START,
|
||||||
|
expand: true });
|
||||||
|
|
||||||
|
let ejectIcon =
|
||||||
|
new St.Icon({ icon_name: 'media-eject',
|
||||||
|
style_class: 'hotplug-resident-eject-icon' });
|
||||||
|
|
||||||
|
let ejectButton =
|
||||||
|
new St.Button({ style_class: 'hotplug-resident-eject-button',
|
||||||
|
button_mask: St.ButtonMask.ONE,
|
||||||
|
child: ejectIcon });
|
||||||
|
item.add(ejectButton, { x_align: St.Align.END });
|
||||||
|
|
||||||
|
// now connect signals
|
||||||
|
mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
|
||||||
|
startAppForMount(apps[0], mount);
|
||||||
|
}));
|
||||||
|
|
||||||
|
ejectButton.connect('clicked', Lang.bind(this, function() {
|
||||||
|
Main.autorunManager.ejectMount(mount);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return item;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutorunTransientDispatcher() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutorunTransientDispatcher.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._sources = [];
|
||||||
|
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||||
|
},
|
||||||
|
|
||||||
|
_getAutorunSettingForType: function(contentType) {
|
||||||
|
let runApp = this._settings.get_strv(SETTING_START_APP);
|
||||||
|
if (runApp.indexOf(contentType) != -1)
|
||||||
|
return AutorunSetting.RUN;
|
||||||
|
|
||||||
|
let ignore = this._settings.get_strv(SETTING_IGNORE);
|
||||||
|
if (ignore.indexOf(contentType) != -1)
|
||||||
|
return AutorunSetting.IGNORE;
|
||||||
|
|
||||||
|
let openFiles = this._settings.get_strv(SETTING_OPEN_FOLDER);
|
||||||
|
if (openFiles.indexOf(contentType) != -1)
|
||||||
|
return AutorunSetting.FILES;
|
||||||
|
|
||||||
|
return AutorunSetting.ASK;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSourceForMount: function(mount) {
|
||||||
|
let filtered =
|
||||||
|
this._sources.filter(function (source) {
|
||||||
|
return (source.mount == mount);
|
||||||
|
});
|
||||||
|
|
||||||
|
// we always make sure not to add two sources for the same
|
||||||
|
// mount in addMount(), so it's safe to assume filtered.length
|
||||||
|
// is always either 1 or 0.
|
||||||
|
if (filtered.length == 1)
|
||||||
|
return filtered[0];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addSource: function(mount, apps) {
|
||||||
|
// if we already have a source showing for this
|
||||||
|
// mount, return
|
||||||
|
if (this._getSourceForMount(mount))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add a new source
|
||||||
|
this._sources.push(new AutorunTransientSource(mount, apps));
|
||||||
|
},
|
||||||
|
|
||||||
|
addMount: function(mount, apps, contentTypes) {
|
||||||
|
// if autorun is disabled globally, return
|
||||||
|
if (this._settings.get_boolean(SETTING_DISABLE_AUTORUN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if the mount doesn't want to be autorun, return
|
||||||
|
if (ignoreAutorunForMount(mount))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let setting = this._getAutorunSettingForType(contentTypes[0]);
|
||||||
|
|
||||||
|
// check at the settings for the first content type
|
||||||
|
// to see whether we should ask
|
||||||
|
if (setting == AutorunSetting.IGNORE)
|
||||||
|
return; // return right away
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
let app = null;
|
||||||
|
|
||||||
|
if (setting == AutorunSetting.RUN) {
|
||||||
|
app = Gio.app_info_get_default_for_type(type, false);
|
||||||
|
} else if (setting == AutorunSetting.FILES) {
|
||||||
|
app = Gio.app_info_get_default_for_type('inode/directory', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app)
|
||||||
|
success = startAppForMount(app, mount);
|
||||||
|
|
||||||
|
// we fallback here also in case the settings did not specify 'ask',
|
||||||
|
// but we failed launching the default app or the default file manager
|
||||||
|
if (!success)
|
||||||
|
this._addSource(mount, apps);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeMount: function(mount) {
|
||||||
|
let source = this._getSourceForMount(mount);
|
||||||
|
|
||||||
|
// if we aren't tracking this mount, don't do anything
|
||||||
|
if (!source)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// destroy the notification source
|
||||||
|
source.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutorunTransientSource(mount, apps) {
|
||||||
|
this._init(mount, apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutorunTransientSource.prototype = {
|
||||||
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
|
_init: function(mount, apps) {
|
||||||
|
MessageTray.Source.prototype._init.call(this, mount.get_name());
|
||||||
|
|
||||||
|
this.mount = mount;
|
||||||
|
this.apps = apps;
|
||||||
|
|
||||||
|
this._notification = new AutorunTransientNotification(this);
|
||||||
|
this._setSummaryIcon(this.createNotificationIcon(this.ICON_SIZE));
|
||||||
|
|
||||||
|
// add ourselves as a source, and popup the notification
|
||||||
|
Main.messageTray.add(this);
|
||||||
|
this.notify(this._notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
createNotificationIcon: function(iconSize) {
|
||||||
|
return new St.Icon({ gicon: this.mount.get_icon(),
|
||||||
|
icon_size: iconSize ? iconSize : this.ICON_SIZE });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutorunTransientNotification(source) {
|
||||||
|
this._init(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutorunTransientNotification.prototype = {
|
||||||
|
__proto__: MessageTray.Notification.prototype,
|
||||||
|
|
||||||
|
_init: function(source) {
|
||||||
|
MessageTray.Notification.prototype._init.call(this, source,
|
||||||
|
source.title, null,
|
||||||
|
{ customContent: true });
|
||||||
|
|
||||||
|
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
||||||
|
vertical: true });
|
||||||
|
this.addActor(this._box);
|
||||||
|
|
||||||
|
this._mount = source.mount;
|
||||||
|
|
||||||
|
source.apps.forEach(Lang.bind(this, function (app) {
|
||||||
|
let actor = this._buttonForApp(app);
|
||||||
|
|
||||||
|
if (actor)
|
||||||
|
this._box.add(actor, { x_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._box.add(this._buttonForEject(), { x_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
|
||||||
|
// set the notification to transient and urgent, so that it
|
||||||
|
// expands out
|
||||||
|
this.setTransient(true);
|
||||||
|
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||||
|
},
|
||||||
|
|
||||||
|
_buttonForApp: function(app) {
|
||||||
|
let box = new St.BoxLayout();
|
||||||
|
let icon = new St.Icon({ gicon: app.get_icon(),
|
||||||
|
style_class: 'hotplug-notification-item-icon' });
|
||||||
|
box.add(icon);
|
||||||
|
|
||||||
|
let label = new St.Bin({ y_align: St.Align.MIDDLE,
|
||||||
|
child: new St.Label
|
||||||
|
({ text: _("Open with %s").format(app.get_display_name()) })
|
||||||
|
});
|
||||||
|
box.add(label);
|
||||||
|
|
||||||
|
let button = new St.Button({ child: box,
|
||||||
|
x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
button_mask: St.ButtonMask.ONE,
|
||||||
|
style_class: 'hotplug-notification-item' });
|
||||||
|
|
||||||
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
|
startAppForMount(app, this._mount);
|
||||||
|
this.destroy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
return button;
|
||||||
|
},
|
||||||
|
|
||||||
|
_buttonForEject: function() {
|
||||||
|
let box = new St.BoxLayout();
|
||||||
|
let icon = new St.Icon({ icon_name: 'media-eject',
|
||||||
|
style_class: 'hotplug-notification-item-icon' });
|
||||||
|
box.add(icon);
|
||||||
|
|
||||||
|
let label = new St.Bin({ y_align: St.Align.MIDDLE,
|
||||||
|
child: new St.Label
|
||||||
|
({ text: _("Eject") })
|
||||||
|
});
|
||||||
|
box.add(label);
|
||||||
|
|
||||||
|
let button = new St.Button({ child: box,
|
||||||
|
x_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
button_mask: St.ButtonMask.ONE,
|
||||||
|
style_class: 'hotplug-notification-item' });
|
||||||
|
|
||||||
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
|
Main.autorunManager.ejectMount(this._mount);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ 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 Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const POPUP_ANIMATION_TIME = 0.15;
|
const POPUP_ANIMATION_TIME = 0.15;
|
||||||
@ -179,7 +180,7 @@ BoxPointer.prototype = {
|
|||||||
this.bin.allocate(childBox, flags);
|
this.bin.allocate(childBox, flags);
|
||||||
|
|
||||||
if (this._sourceActor && this._sourceActor.mapped)
|
if (this._sourceActor && this._sourceActor.mapped)
|
||||||
this._reposition(this._sourceActor, this._gap, this._alignment);
|
this._reposition(this._sourceActor, this._alignment);
|
||||||
},
|
},
|
||||||
|
|
||||||
_drawBorder: function(area) {
|
_drawBorder: function(area) {
|
||||||
@ -305,19 +306,18 @@ BoxPointer.prototype = {
|
|||||||
cr.stroke();
|
cr.stroke();
|
||||||
},
|
},
|
||||||
|
|
||||||
setPosition: function(sourceActor, gap, alignment) {
|
setPosition: function(sourceActor, alignment) {
|
||||||
// We need to show it now to force an allocation,
|
// We need to show it now to force an allocation,
|
||||||
// so that we can query the correct size.
|
// so that we can query the correct size.
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
|
|
||||||
this._sourceActor = sourceActor;
|
this._sourceActor = sourceActor;
|
||||||
this._gap = gap;
|
|
||||||
this._alignment = alignment;
|
this._alignment = alignment;
|
||||||
|
|
||||||
this._reposition(sourceActor, gap, alignment);
|
this._reposition(sourceActor, alignment);
|
||||||
},
|
},
|
||||||
|
|
||||||
_reposition: function(sourceActor, gap, alignment) {
|
_reposition: function(sourceActor, alignment) {
|
||||||
// Position correctly relative to the sourceActor
|
// Position correctly relative to the sourceActor
|
||||||
let sourceNode = sourceActor.get_theme_node();
|
let sourceNode = sourceActor.get_theme_node();
|
||||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||||
@ -329,7 +329,7 @@ BoxPointer.prototype = {
|
|||||||
// We also want to keep it onscreen, and separated from the
|
// We also want to keep it onscreen, and separated from the
|
||||||
// edge by the same distance as the main part of the box is
|
// edge by the same distance as the main part of the box is
|
||||||
// separated from its sourceActor
|
// separated from its sourceActor
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
let themeNode = this.actor.get_theme_node();
|
let themeNode = this.actor.get_theme_node();
|
||||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||||
let arrowBase = themeNode.get_length('-arrow-base');
|
let arrowBase = themeNode.get_length('-arrow-base');
|
||||||
@ -337,6 +337,9 @@ BoxPointer.prototype = {
|
|||||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||||
let halfMargin = margin / 2;
|
let halfMargin = margin / 2;
|
||||||
|
|
||||||
|
let themeNode = this.actor.get_theme_node();
|
||||||
|
let gap = themeNode.get_length('-boxpointer-gap');
|
||||||
|
|
||||||
let resX, resY;
|
let resX, resY;
|
||||||
|
|
||||||
switch (this._arrowSide) {
|
switch (this._arrowSide) {
|
||||||
|
@ -8,9 +8,6 @@ const St = imports.gi.St;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
const C_ = Gettext.pgettext;
|
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
@ -354,17 +351,16 @@ function Calendar(eventSource) {
|
|||||||
|
|
||||||
Calendar.prototype = {
|
Calendar.prototype = {
|
||||||
_init: function(eventSource) {
|
_init: function(eventSource) {
|
||||||
this._eventSource = eventSource;
|
if (eventSource) {
|
||||||
|
this._eventSource = eventSource;
|
||||||
|
|
||||||
this._eventSource.connect('changed', Lang.bind(this,
|
this._eventSource.connect('changed', Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
this._update(false);
|
this._update(false);
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
this._weekStart = Shell.util_get_week_start();
|
||||||
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
|
||||||
// should add a C function so we can do the full handling.
|
|
||||||
this._weekStart = NaN;
|
|
||||||
this._weekdate = NaN;
|
this._weekdate = NaN;
|
||||||
this._digitWidth = NaN;
|
this._digitWidth = NaN;
|
||||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
||||||
@ -372,16 +368,6 @@ Calendar.prototype = {
|
|||||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||||
|
|
||||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
|
||||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
|
||||||
this._weekStart = parseInt(weekStartString.substring(20));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
|
|
||||||
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
|
|
||||||
this._weekStart = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the ordering for month/year in the calendar heading
|
// Find the ordering for month/year in the calendar heading
|
||||||
this._headerFormatWithoutYear = '%B';
|
this._headerFormatWithoutYear = '%B';
|
||||||
switch (Gettext_gtk30.gettext('calendar:MY')) {
|
switch (Gettext_gtk30.gettext('calendar:MY')) {
|
||||||
@ -570,13 +556,16 @@ Calendar.prototype = {
|
|||||||
while (true) {
|
while (true) {
|
||||||
let button = new St.Button({ label: iter.getDate().toString() });
|
let button = new St.Button({ label: iter.getDate().toString() });
|
||||||
|
|
||||||
|
if (!this._eventSource)
|
||||||
|
button.reactive = false;
|
||||||
|
|
||||||
let iterStr = iter.toUTCString();
|
let iterStr = iter.toUTCString();
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
let newlySelectedDate = new Date(iterStr);
|
let newlySelectedDate = new Date(iterStr);
|
||||||
this.setDate(newlySelectedDate, false);
|
this.setDate(newlySelectedDate, false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let hasEvents = this._eventSource.hasEvents(iter);
|
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
|
||||||
let styleClass = 'calendar-day-base calendar-day';
|
let styleClass = 'calendar-day-base calendar-day';
|
||||||
if (_isWorkDay(iter))
|
if (_isWorkDay(iter))
|
||||||
styleClass += ' calendar-work-day'
|
styleClass += ' calendar-work-day'
|
||||||
@ -623,7 +612,8 @@ Calendar.prototype = {
|
|||||||
}
|
}
|
||||||
// Signal to the event source that we are interested in events
|
// Signal to the event source that we are interested in events
|
||||||
// only from this date range
|
// only from this date range
|
||||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
if (this._eventSource)
|
||||||
|
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -641,17 +631,7 @@ EventsList.prototype = {
|
|||||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
this._weekStart = Shell.util_get_week_start();
|
||||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
|
||||||
this._weekStart = parseInt(weekStartString.substring(20));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(this._weekStart) ||
|
|
||||||
this._weekStart < 0 ||
|
|
||||||
this._weekStart > 6) {
|
|
||||||
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
|
|
||||||
this._weekStart = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._update();
|
this._update();
|
||||||
},
|
},
|
||||||
@ -670,6 +650,9 @@ EventsList.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
||||||
|
if (!this._eventSource)
|
||||||
|
return;
|
||||||
|
|
||||||
let events = this._eventSource.getEvents(begin, end);
|
let events = this._eventSource.getEvents(begin, end);
|
||||||
|
|
||||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
||||||
|
454
js/ui/chrome.js
@ -1,454 +0,0 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
||||||
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const Meta = imports.gi.Meta;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Signals = imports.signals;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const Params = imports.misc.params;
|
|
||||||
|
|
||||||
// This manages the shell "chrome"; the UI that's visible in the
|
|
||||||
// normal mode (ie, outside the Overview), that surrounds the main
|
|
||||||
// workspace content.
|
|
||||||
|
|
||||||
const defaultParams = {
|
|
||||||
visibleInOverview: false,
|
|
||||||
visibleInFullscreen: false,
|
|
||||||
affectsStruts: true,
|
|
||||||
affectsInputRegion: true
|
|
||||||
};
|
|
||||||
|
|
||||||
function Chrome() {
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Chrome.prototype = {
|
|
||||||
_init: function() {
|
|
||||||
// The group itself has zero size so it doesn't interfere with DND
|
|
||||||
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
|
||||||
|
|
||||||
this._monitors = [];
|
|
||||||
this._inOverview = false;
|
|
||||||
|
|
||||||
this._trackedActors = [];
|
|
||||||
|
|
||||||
global.screen.connect('monitors-changed',
|
|
||||||
Lang.bind(this, this._monitorsChanged));
|
|
||||||
global.screen.connect('restacked',
|
|
||||||
Lang.bind(this, this._windowsRestacked));
|
|
||||||
|
|
||||||
// Need to update struts on new workspaces when they are added
|
|
||||||
global.screen.connect('notify::n-workspaces',
|
|
||||||
Lang.bind(this, this._queueUpdateRegions));
|
|
||||||
|
|
||||||
Main.overview.connect('showing',
|
|
||||||
Lang.bind(this, this._overviewShowing));
|
|
||||||
Main.overview.connect('hidden',
|
|
||||||
Lang.bind(this, this._overviewHidden));
|
|
||||||
|
|
||||||
this._updateMonitors();
|
|
||||||
this._updateFullscreen();
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_allocated: function(actor, box, flags) {
|
|
||||||
let children = this.actor.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++)
|
|
||||||
children[i].allocate_preferred_size(flags);
|
|
||||||
},
|
|
||||||
|
|
||||||
// addActor:
|
|
||||||
// @actor: an actor to add to the chrome layer
|
|
||||||
// @params: (optional) additional params
|
|
||||||
//
|
|
||||||
// Adds @actor to the chrome layer and extends the input region
|
|
||||||
// and window manager struts to include it. (Window manager struts
|
|
||||||
// will only be affected if @actor is touching a screen edge.)
|
|
||||||
// Changes in @actor's size and position will automatically result
|
|
||||||
// in appropriate changes to the input region and struts. Changes
|
|
||||||
// in its visibility will affect the input region, but NOT the
|
|
||||||
// struts.
|
|
||||||
//
|
|
||||||
// If %visibleInOverview is %true in @params, @actor will remain
|
|
||||||
// visible when the overview is brought up. Otherwise it will
|
|
||||||
// automatically be hidden. Likewise, if %visibleInFullscreen is
|
|
||||||
// %true, the actor will be visible even when a fullscreen window
|
|
||||||
// should be covering it.
|
|
||||||
//
|
|
||||||
// If %affectsStruts or %affectsInputRegion is %false, the actor
|
|
||||||
// will not have the indicated effect.
|
|
||||||
addActor: function(actor, params) {
|
|
||||||
this.actor.add_actor(actor);
|
|
||||||
this._trackActor(actor, params);
|
|
||||||
},
|
|
||||||
|
|
||||||
// trackActor:
|
|
||||||
// @actor: a descendant of the chrome to begin tracking
|
|
||||||
// @params: parameters describing how to track @actor
|
|
||||||
//
|
|
||||||
// Tells the chrome to track @actor, which must be a descendant
|
|
||||||
// of an actor added via addActor(). This can be used to extend the
|
|
||||||
// struts or input region to cover specific children.
|
|
||||||
//
|
|
||||||
// @params can have any of the same values as in addActor(), though
|
|
||||||
// some possibilities don't make sense (eg, trying to have a
|
|
||||||
// %visibleInOverview child of a non-%visibleInOverview parent).
|
|
||||||
// By default, @actor has the same params as its chrome ancestor.
|
|
||||||
trackActor: function(actor, params) {
|
|
||||||
let ancestor = actor.get_parent();
|
|
||||||
let index = this._findActor(ancestor);
|
|
||||||
while (ancestor && index == -1) {
|
|
||||||
ancestor = ancestor.get_parent();
|
|
||||||
index = this._findActor(ancestor);
|
|
||||||
}
|
|
||||||
if (!ancestor)
|
|
||||||
throw new Error('actor is not a descendent of the chrome layer');
|
|
||||||
|
|
||||||
let ancestorData = this._trackedActors[index];
|
|
||||||
if (!params)
|
|
||||||
params = {};
|
|
||||||
// We can't use Params.parse here because we want to drop
|
|
||||||
// the extra values like ancestorData.actor
|
|
||||||
for (let prop in defaultParams) {
|
|
||||||
if (!params[prop])
|
|
||||||
params[prop] = ancestorData[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
this._trackActor(actor, params);
|
|
||||||
},
|
|
||||||
|
|
||||||
// untrackActor:
|
|
||||||
// @actor: an actor previously tracked via trackActor()
|
|
||||||
//
|
|
||||||
// Undoes the effect of trackActor()
|
|
||||||
untrackActor: function(actor) {
|
|
||||||
this._untrackActor(actor);
|
|
||||||
},
|
|
||||||
|
|
||||||
// removeActor:
|
|
||||||
// @actor: a child of the chrome layer
|
|
||||||
//
|
|
||||||
// Removes @actor from the chrome layer
|
|
||||||
removeActor: function(actor) {
|
|
||||||
this.actor.remove_actor(actor);
|
|
||||||
this._untrackActor(actor);
|
|
||||||
},
|
|
||||||
|
|
||||||
_findActor: function(actor) {
|
|
||||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
|
||||||
let actorData = this._trackedActors[i];
|
|
||||||
if (actorData.actor == actor)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
_trackActor: function(actor, params) {
|
|
||||||
if (this._findActor(actor) != -1)
|
|
||||||
throw new Error('trying to re-track existing chrome actor');
|
|
||||||
|
|
||||||
let actorData = Params.parse(params, defaultParams);
|
|
||||||
actorData.actor = actor;
|
|
||||||
actorData.visibleId = actor.connect('notify::visible',
|
|
||||||
Lang.bind(this, this._queueUpdateRegions));
|
|
||||||
actorData.allocationId = actor.connect('notify::allocation',
|
|
||||||
Lang.bind(this, this._queueUpdateRegions));
|
|
||||||
actorData.parentSetId = actor.connect('parent-set',
|
|
||||||
Lang.bind(this, this._actorReparented));
|
|
||||||
// Note that destroying actor will unset its parent, so we don't
|
|
||||||
// need to connect to 'destroy' too.
|
|
||||||
|
|
||||||
this._trackedActors.push(actorData);
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_untrackActor: function(actor) {
|
|
||||||
let i = this._findActor(actor);
|
|
||||||
|
|
||||||
if (i == -1)
|
|
||||||
return;
|
|
||||||
let actorData = this._trackedActors[i];
|
|
||||||
|
|
||||||
this._trackedActors.splice(i, 1);
|
|
||||||
actor.disconnect(actorData.visibleId);
|
|
||||||
actor.disconnect(actorData.allocationId);
|
|
||||||
actor.disconnect(actorData.parentSetId);
|
|
||||||
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_actorReparented: function(actor, oldParent) {
|
|
||||||
if (!this.actor.contains(actor))
|
|
||||||
this._untrackActor(actor);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateVisibility: function() {
|
|
||||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
|
||||||
let actorData = this._trackedActors[i];
|
|
||||||
if (this._inOverview && !actorData.visibleInOverview)
|
|
||||||
this.actor.set_skip_paint(actorData.actor, true);
|
|
||||||
else if (!this._inOverview && !actorData.visibleInFullscreen &&
|
|
||||||
this._findMonitorForActor(actorData.actor).inFullscreen)
|
|
||||||
this.actor.set_skip_paint(actorData.actor, true);
|
|
||||||
else
|
|
||||||
this.actor.set_skip_paint(actorData.actor, false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_overviewShowing: function() {
|
|
||||||
this._inOverview = true;
|
|
||||||
this._updateVisibility();
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_overviewHidden: function() {
|
|
||||||
this._inOverview = false;
|
|
||||||
this._updateVisibility();
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateMonitors: function() {
|
|
||||||
let monitors = global.get_monitors();
|
|
||||||
let primary = global.get_primary_monitor();
|
|
||||||
this._monitors = monitors;
|
|
||||||
for (let i = 0; i < monitors.length; i++) {
|
|
||||||
let monitor = monitors[i];
|
|
||||||
if (monitor.x == primary.x &&
|
|
||||||
monitor.y == primary.y &&
|
|
||||||
monitor.width == primary.width &&
|
|
||||||
monitor.height == primary.height)
|
|
||||||
this._primaryMonitor = monitor;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_findMonitorForRect: function(x, y, w, h) {
|
|
||||||
// First look at what monitor the center of the rectangle is at
|
|
||||||
let cx = x + w/2;
|
|
||||||
let cy = y + h/2;
|
|
||||||
for (let i = 0; i < this._monitors.length; i++) {
|
|
||||||
let monitor = this._monitors[i];
|
|
||||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
|
||||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
|
||||||
return monitor;
|
|
||||||
}
|
|
||||||
// If the center is not on a monitor, return the first overlapping monitor
|
|
||||||
for (let i = 0; i < this._monitors.length; i++) {
|
|
||||||
let monitor = this._monitors[i];
|
|
||||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
|
||||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
|
||||||
return monitor;
|
|
||||||
}
|
|
||||||
// otherwise on no monitor
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_findMonitorForWindow: function(window) {
|
|
||||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
|
||||||
},
|
|
||||||
|
|
||||||
// This call guarantees that we return some monitor to simplify usage of it
|
|
||||||
// In practice all tracked actors should be visible on some monitor anyway
|
|
||||||
_findMonitorForActor: function(actor) {
|
|
||||||
let [x, y] = actor.get_transformed_position();
|
|
||||||
let [w, h] = actor.get_transformed_size();
|
|
||||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
|
||||||
if (monitor)
|
|
||||||
return monitor;
|
|
||||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
|
||||||
},
|
|
||||||
|
|
||||||
_monitorsChanged: function() {
|
|
||||||
this._updateMonitors();
|
|
||||||
|
|
||||||
// Update everything that depends on monitor positions
|
|
||||||
this._updateFullscreen();
|
|
||||||
this._updateVisibility();
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_queueUpdateRegions: function() {
|
|
||||||
if (!this._updateRegionIdle)
|
|
||||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
|
||||||
Meta.PRIORITY_BEFORE_REDRAW);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateFullscreen: function() {
|
|
||||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
|
||||||
|
|
||||||
// Reset all monitors to not fullscreen
|
|
||||||
for (let i = 0; i < this._monitors.length; i++)
|
|
||||||
this._monitors[i].inFullscreen = false;
|
|
||||||
|
|
||||||
// The chrome layer should be visible unless there is a window
|
|
||||||
// with layer FULLSCREEN, or a window with layer
|
|
||||||
// OVERRIDE_REDIRECT that covers the whole screen.
|
|
||||||
// ('override_redirect' is not actually a layer above all
|
|
||||||
// other windows, but this seems to be how mutter treats it
|
|
||||||
// currently...) If we wanted to be extra clever, we could
|
|
||||||
// figure out when an OVERRIDE_REDIRECT window was trying to
|
|
||||||
// partially overlap us, and then adjust the input region and
|
|
||||||
// our clip region accordingly...
|
|
||||||
|
|
||||||
// @windows is sorted bottom to top.
|
|
||||||
|
|
||||||
for (let i = windows.length - 1; i > -1; i--) {
|
|
||||||
let window = windows[i];
|
|
||||||
let layer = window.get_meta_window().get_layer();
|
|
||||||
|
|
||||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
|
||||||
let monitor = this._findMonitorForWindow(window);
|
|
||||||
if (monitor)
|
|
||||||
monitor.inFullscreen = true;
|
|
||||||
}
|
|
||||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
|
||||||
let monitor = this._findMonitorForWindow(window);
|
|
||||||
if (monitor &&
|
|
||||||
window.x <= monitor.x &&
|
|
||||||
window.x + window.width >= monitor.x + monitor.width &&
|
|
||||||
window.y <= monitor.y &&
|
|
||||||
window.y + window.height >= monitor.y + monitor.height)
|
|
||||||
monitor.inFullscreen = true;
|
|
||||||
} else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_windowsRestacked: function() {
|
|
||||||
let wasInFullscreen = [];
|
|
||||||
for (let i = 0; i < this._monitors.length; i++)
|
|
||||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
|
||||||
|
|
||||||
this._updateFullscreen();
|
|
||||||
|
|
||||||
let changed = false;
|
|
||||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
|
||||||
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
|
||||||
changed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
this._updateVisibility();
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateRegions: function() {
|
|
||||||
let rects = [], struts = [], i;
|
|
||||||
|
|
||||||
delete this._updateRegionIdle;
|
|
||||||
|
|
||||||
for (i = 0; i < this._trackedActors.length; i++) {
|
|
||||||
let actorData = this._trackedActors[i];
|
|
||||||
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let [x, y] = actorData.actor.get_transformed_position();
|
|
||||||
let [w, h] = actorData.actor.get_transformed_size();
|
|
||||||
x = Math.round(x);
|
|
||||||
y = Math.round(y);
|
|
||||||
w = Math.round(w);
|
|
||||||
h = Math.round(h);
|
|
||||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
|
||||||
|
|
||||||
if (actorData.affectsInputRegion &&
|
|
||||||
actorData.actor.get_paint_visibility() &&
|
|
||||||
!this.actor.get_skip_paint(actorData.actor))
|
|
||||||
rects.push(rect);
|
|
||||||
|
|
||||||
if (!actorData.affectsStruts)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Limit struts to the size of the screen
|
|
||||||
let x1 = Math.max(x, 0);
|
|
||||||
let x2 = Math.min(x + w, global.screen_width);
|
|
||||||
let y1 = Math.max(y, 0);
|
|
||||||
let y2 = Math.min(y + h, global.screen_height);
|
|
||||||
|
|
||||||
// NetWM struts are not really powerful enought to handle
|
|
||||||
// a multi-monitor scenario, they only describe what happens
|
|
||||||
// around the outer sides of the full display region. However
|
|
||||||
// it can describe a partial region along each side, so
|
|
||||||
// we can support having the struts only affect the
|
|
||||||
// primary monitor. This should be enough as we only have
|
|
||||||
// chrome affecting the struts on the primary monitor so
|
|
||||||
// far.
|
|
||||||
//
|
|
||||||
// Metacity wants to know what side of the screen the
|
|
||||||
// strut is considered to be attached to. If the actor is
|
|
||||||
// only touching one edge, or is touching the entire
|
|
||||||
// border of the primary monitor, then it's obvious which
|
|
||||||
// side to call it. If it's in a corner, we pick a side
|
|
||||||
// arbitrarily. If it doesn't touch any edges, or it spans
|
|
||||||
// the width/height across the middle of the screen, then
|
|
||||||
// we don't create a strut for it at all.
|
|
||||||
let side;
|
|
||||||
let primary = this._primaryMonitor;
|
|
||||||
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
|
||||||
if (y1 <= primary.y)
|
|
||||||
side = Meta.Side.TOP;
|
|
||||||
else if (y2 >= primary.y + primary.height)
|
|
||||||
side = Meta.Side.BOTTOM;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
|
||||||
if (x1 <= 0)
|
|
||||||
side = Meta.Side.LEFT;
|
|
||||||
else if (x2 >= global.screen_width)
|
|
||||||
side = Meta.Side.RIGHT;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
} else if (x1 <= 0)
|
|
||||||
side = Meta.Side.LEFT;
|
|
||||||
else if (y1 <= 0)
|
|
||||||
side = Meta.Side.TOP;
|
|
||||||
else if (x2 >= global.screen_width)
|
|
||||||
side = Meta.Side.RIGHT;
|
|
||||||
else if (y2 >= global.screen_height)
|
|
||||||
side = Meta.Side.BOTTOM;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Ensure that the strut rects goes all the way to the screen edge,
|
|
||||||
// as this really what mutter expects.
|
|
||||||
switch (side) {
|
|
||||||
case Meta.Side.TOP:
|
|
||||||
y1 = 0;
|
|
||||||
break;
|
|
||||||
case Meta.Side.BOTTOM:
|
|
||||||
y2 = global.screen_height;
|
|
||||||
break;
|
|
||||||
case Meta.Side.LEFT:
|
|
||||||
x1 = 0;
|
|
||||||
break;
|
|
||||||
case Meta.Side.RIGHT:
|
|
||||||
x2 = global.screen_width;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
|
||||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
|
||||||
struts.push(strut);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.set_stage_input_region(rects);
|
|
||||||
|
|
||||||
let screen = global.screen;
|
|
||||||
for (let w = 0; w < screen.n_workspaces; w++) {
|
|
||||||
let workspace = screen.get_workspace_by_index(w);
|
|
||||||
workspace.set_builtin_struts(struts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Signals.addSignalMethods(Chrome.prototype);
|
|
179
js/ui/contactDisplay.js
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Folks = imports.gi.Folks
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
const IconGrid = imports.ui.iconGrid;
|
||||||
|
const Search = imports.ui.search;
|
||||||
|
const SearchDisplay = imports.ui.searchDisplay;
|
||||||
|
|
||||||
|
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||||
|
const ICON_SIZE = 81;
|
||||||
|
|
||||||
|
function launchContact(id) {
|
||||||
|
Util.spawn(['gnome-contacts', '-i', id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This class represents a shown contact search result in the overview */
|
||||||
|
function Contact(id) {
|
||||||
|
this._init(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact.prototype = {
|
||||||
|
_init: function(id) {
|
||||||
|
this.individual = Shell.ContactSystem.get_default().get_individual(id);
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ style_class: 'contact',
|
||||||
|
reactive: true,
|
||||||
|
track_hover: true });
|
||||||
|
|
||||||
|
let content = new St.BoxLayout( { style_class: 'contact-content',
|
||||||
|
vertical: false });
|
||||||
|
this.actor.set_child(content);
|
||||||
|
|
||||||
|
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
|
||||||
|
icon_size: ICON_SIZE,
|
||||||
|
style_class: 'contact-icon' });
|
||||||
|
if (this.individual.avatar != null)
|
||||||
|
icon.gicon = this.individual.avatar;
|
||||||
|
else
|
||||||
|
icon.icon_name = 'avatar-default';
|
||||||
|
|
||||||
|
content.add(icon, { x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let details = new St.BoxLayout({ style_class: 'contact-details',
|
||||||
|
vertical: true });
|
||||||
|
content.add(details, { x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let aliasText = this.individual.alias || _("Unknown");
|
||||||
|
let aliasLabel = new St.Label({ text: aliasText,
|
||||||
|
style_class: 'contact-details-alias' });
|
||||||
|
details.add(aliasLabel, { x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let presence = this._createPresence(this.individual.presence_type);
|
||||||
|
details.add(presence, { x_fill: false,
|
||||||
|
y_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
},
|
||||||
|
|
||||||
|
_createPresence: function(presence) {
|
||||||
|
let text;
|
||||||
|
let iconName;
|
||||||
|
|
||||||
|
switch(presence) {
|
||||||
|
case Folks.PresenceType.AVAILABLE:
|
||||||
|
text = _("Available");
|
||||||
|
iconName = 'user-available';
|
||||||
|
break;
|
||||||
|
case Folks.PresenceType.AWAY:
|
||||||
|
case Folks.PresenceType.EXTENDED_AWAY:
|
||||||
|
text = _("Away");
|
||||||
|
iconName = 'user-away';
|
||||||
|
break;
|
||||||
|
case Folks.PresenceType.BUSY:
|
||||||
|
text = _("Busy");
|
||||||
|
iconName = 'user-busy';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = _("Offline");
|
||||||
|
iconName = 'user-offline';
|
||||||
|
}
|
||||||
|
|
||||||
|
let icon = new St.Icon({ icon_name: iconName,
|
||||||
|
icon_type: St.IconType.FULLCOLOR,
|
||||||
|
icon_size: 16,
|
||||||
|
style_class: 'contact-details-status-icon' });
|
||||||
|
let label = new St.Label({ text: text });
|
||||||
|
|
||||||
|
let box = new St.BoxLayout({ vertical: false,
|
||||||
|
style_class: 'contact-details-status' });
|
||||||
|
box.add(icon, { x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
box.add(label, { x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
|
||||||
|
createIcon: function(size) {
|
||||||
|
let tc = St.TextureCache.get_default();
|
||||||
|
let icon = this.individual.avatar;
|
||||||
|
|
||||||
|
if (icon != null) {
|
||||||
|
return tc.load_gicon(null, icon, size);
|
||||||
|
} else {
|
||||||
|
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Searches for and returns contacts */
|
||||||
|
function ContactSearchProvider() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactSearchProvider.prototype = {
|
||||||
|
__proto__: Search.SearchProvider.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
Search.SearchProvider.prototype._init.call(this, _("CONTACTS"));
|
||||||
|
this._contactSys = Shell.ContactSystem.get_default();
|
||||||
|
},
|
||||||
|
|
||||||
|
getResultMeta: function(id) {
|
||||||
|
let contact = new Contact(id);
|
||||||
|
return { 'id': id,
|
||||||
|
'name': contact.alias,
|
||||||
|
'createIcon': function(size) {
|
||||||
|
return contact.createIcon(size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialResultSet: function(terms) {
|
||||||
|
return this._contactSys.initial_search(terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
|
return this._contactSys.subsearch(previousResults, terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
createResultActor: function(resultMeta, terms) {
|
||||||
|
let contact = new Contact(resultMeta.id);
|
||||||
|
return contact.actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
createResultContainerActor: function() {
|
||||||
|
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||||
|
xAlign: St.Align.START });
|
||||||
|
grid.actor.style_class = 'contact-grid';
|
||||||
|
|
||||||
|
let actor = new SearchDisplay.GridSearchResults(this, grid);
|
||||||
|
return actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
activateResult: function(id, params) {
|
||||||
|
launchContact(id);
|
||||||
|
}
|
||||||
|
};
|
@ -153,14 +153,14 @@ CtrlAltTabPopup.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
alloc.min_size = primary.width;
|
alloc.min_size = primary.width;
|
||||||
alloc.natural_size = primary.width;
|
alloc.natural_size = primary.width;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
alloc.min_size = primary.height;
|
alloc.min_size = primary.height;
|
||||||
alloc.natural_size = primary.height;
|
alloc.natural_size = primary.height;
|
||||||
@ -168,7 +168,7 @@ CtrlAltTabPopup.prototype = {
|
|||||||
|
|
||||||
_allocate: function (actor, box, flags) {
|
_allocate: function (actor, box, flags) {
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
||||||
let vPadding = this.actor.get_theme_node().get_vertical_padding();
|
let vPadding = this.actor.get_theme_node().get_vertical_padding();
|
||||||
@ -323,6 +323,6 @@ CtrlAltTabSwitcher.prototype = {
|
|||||||
let text = new St.Label({ text: item.name });
|
let text = new St.Label({ text: item.name });
|
||||||
box.add(text, { x_fill: false });
|
box.add(text, { x_fill: false });
|
||||||
|
|
||||||
this.addItem(box);
|
this.addItem(box, text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,6 @@ const Lang = imports.lang;
|
|||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const AppDisplay = imports.ui.appDisplay;
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
const AppFavorites = imports.ui.appFavorites;
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
@ -209,7 +207,7 @@ RemoveFavoriteIcon.prototype = {
|
|||||||
let app = null;
|
let app = null;
|
||||||
if (source instanceof AppDisplay.AppWellIcon) {
|
if (source instanceof AppDisplay.AppWellIcon) {
|
||||||
let appSystem = Shell.AppSystem.get_default();
|
let appSystem = Shell.AppSystem.get_default();
|
||||||
app = appSystem.get_app(source.getId());
|
app = appSystem.lookup_app(source.getId());
|
||||||
} else if (source.metaWindow) {
|
} else if (source.metaWindow) {
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
app = tracker.get_window_app(source.metaWindow);
|
app = tracker.get_window_app(source.metaWindow);
|
||||||
@ -326,13 +324,13 @@ Dash.prototype = {
|
|||||||
this._favRemoveTarget = null;
|
this._favRemoveTarget = null;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
DND.removeMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragMotion: function(dragEvent) {
|
_onDragMotion: function(dragEvent) {
|
||||||
let app = null;
|
let app = null;
|
||||||
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
||||||
app = this._appSystem.get_app(dragEvent.source.getId());
|
app = this._appSystem.lookup_app(dragEvent.source.getId());
|
||||||
else if (dragEvent.source.metaWindow)
|
else if (dragEvent.source.metaWindow)
|
||||||
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
||||||
else
|
else
|
||||||
@ -344,7 +342,10 @@ Dash.prototype = {
|
|||||||
|
|
||||||
let srcIsFavorite = (id in favorites);
|
let srcIsFavorite = (id in favorites);
|
||||||
|
|
||||||
if (srcIsFavorite && this._favRemoveTarget == null) {
|
if (srcIsFavorite &&
|
||||||
|
dragEvent.source.actor &&
|
||||||
|
this.actor.contains (dragEvent.source.actor) &&
|
||||||
|
this._favRemoveTarget == null) {
|
||||||
this._favRemoveTarget = new RemoveFavoriteIcon();
|
this._favRemoveTarget = new RemoveFavoriteIcon();
|
||||||
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
||||||
this._box.add(this._favRemoveTarget.actor);
|
this._box.add(this._favRemoveTarget.actor);
|
||||||
@ -436,6 +437,7 @@ Dash.prototype = {
|
|||||||
|
|
||||||
let oldIconSize = this.iconSize;
|
let oldIconSize = this.iconSize;
|
||||||
this.iconSize = newIconSize;
|
this.iconSize = newIconSize;
|
||||||
|
this.emit('icon-size-changed');
|
||||||
|
|
||||||
let scale = oldIconSize / newIconSize;
|
let scale = oldIconSize / newIconSize;
|
||||||
for (let i = 0; i < iconChildren.length; i++) {
|
for (let i = 0; i < iconChildren.length; i++) {
|
||||||
@ -618,12 +620,12 @@ Dash.prototype = {
|
|||||||
handleDragOver : function(source, actor, x, y, time) {
|
handleDragOver : function(source, actor, x, y, time) {
|
||||||
let app = null;
|
let app = null;
|
||||||
if (source instanceof AppDisplay.AppWellIcon)
|
if (source instanceof AppDisplay.AppWellIcon)
|
||||||
app = this._appSystem.get_app(source.getId());
|
app = this._appSystem.lookup_app(source.getId());
|
||||||
else if (source.metaWindow)
|
else if (source.metaWindow)
|
||||||
app = this._tracker.get_window_app(source.metaWindow);
|
app = this._tracker.get_window_app(source.metaWindow);
|
||||||
|
|
||||||
// Don't allow favoriting of transient apps
|
// Don't allow favoriting of transient apps
|
||||||
if (app == null || app.is_transient())
|
if (app == null || app.is_window_backed())
|
||||||
return DND.DragMotionResult.NO_DROP;
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
||||||
@ -703,13 +705,13 @@ Dash.prototype = {
|
|||||||
acceptDrop : function(source, actor, x, y, time) {
|
acceptDrop : function(source, actor, x, y, time) {
|
||||||
let app = null;
|
let app = null;
|
||||||
if (source instanceof AppDisplay.AppWellIcon) {
|
if (source instanceof AppDisplay.AppWellIcon) {
|
||||||
app = this._appSystem.get_app(source.getId());
|
app = this._appSystem.lookup_app(source.getId());
|
||||||
} else if (source.metaWindow) {
|
} else if (source.metaWindow) {
|
||||||
app = this._tracker.get_window_app(source.metaWindow);
|
app = this._tracker.get_window_app(source.metaWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow favoriting of transient apps
|
// Don't allow favoriting of transient apps
|
||||||
if (app == null || app.is_transient()) {
|
if (app == null || app.is_window_backed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,14 @@ const Cairo = imports.cairo;
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
|
const Params = imports.misc.params;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Calendar = imports.ui.calendar;
|
const Calendar = imports.ui.calendar;
|
||||||
|
const UPowerGlib = imports.gi.UPowerGlib;
|
||||||
|
|
||||||
// in org.gnome.desktop.interface
|
// in org.gnome.desktop.interface
|
||||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||||
@ -41,19 +41,19 @@ function _onVertSepRepaint (area)
|
|||||||
};
|
};
|
||||||
|
|
||||||
function DateMenuButton() {
|
function DateMenuButton() {
|
||||||
this._init();
|
this._init.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
DateMenuButton.prototype = {
|
DateMenuButton.prototype = {
|
||||||
__proto__: PanelMenu.Button.prototype,
|
__proto__: PanelMenu.Button.prototype,
|
||||||
|
|
||||||
_init: function() {
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { showEvents: true });
|
||||||
|
|
||||||
let item;
|
let item;
|
||||||
let hbox;
|
let hbox;
|
||||||
let vbox;
|
let vbox;
|
||||||
|
|
||||||
this._eventSource = new Calendar.DBusEventSource();
|
|
||||||
|
|
||||||
let menuAlignment = 0.25;
|
let menuAlignment = 0.25;
|
||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||||
menuAlignment = 1.0 - menuAlignment;
|
menuAlignment = 1.0 - menuAlignment;
|
||||||
@ -62,7 +62,7 @@ DateMenuButton.prototype = {
|
|||||||
this._clock = new St.Label();
|
this._clock = new St.Label();
|
||||||
this.actor.set_child(this._clock);
|
this.actor.set_child(this._clock);
|
||||||
|
|
||||||
hbox = new St.BoxLayout({name: 'calendarArea'});
|
hbox = new St.BoxLayout({name: 'calendarArea' });
|
||||||
this.menu.addActor(hbox);
|
this.menu.addActor(hbox);
|
||||||
|
|
||||||
// Fill up the first column
|
// Fill up the first column
|
||||||
@ -75,43 +75,58 @@ DateMenuButton.prototype = {
|
|||||||
this._date.style_class = 'datemenu-date-label';
|
this._date.style_class = 'datemenu-date-label';
|
||||||
vbox.add(this._date);
|
vbox.add(this._date);
|
||||||
|
|
||||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
if (params.showEvents) {
|
||||||
|
this._eventSource = new Calendar.DBusEventSource();
|
||||||
|
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||||
|
} else {
|
||||||
|
this._eventSource = null;
|
||||||
|
this._eventList = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Calendar
|
// Calendar
|
||||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
this._calendar = new Calendar.Calendar(this._eventSource);
|
||||||
|
|
||||||
this._calendar.connect('selected-date-changed',
|
this._calendar.connect('selected-date-changed',
|
||||||
Lang.bind(this, function(calendar, date) {
|
Lang.bind(this, function(calendar, date) {
|
||||||
|
// we know this._eventList is defined here, because selected-data-changed
|
||||||
|
// only gets emitted when the user clicks a date in the calendar,
|
||||||
|
// and the calender makes those dates unclickable when instantiated with
|
||||||
|
// a null event source
|
||||||
this._eventList.setDate(date);
|
this._eventList.setDate(date);
|
||||||
}));
|
}));
|
||||||
vbox.add(this._calendar.actor);
|
vbox.add(this._calendar.actor);
|
||||||
|
|
||||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
|
||||||
item.setColumnWidths(1);
|
if (item) {
|
||||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
|
separator.setColumnWidths(1);
|
||||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
item.actor.can_focus = false;
|
|
||||||
vbox.add(item.actor);
|
|
||||||
|
|
||||||
// Add vertical separator
|
item.actor.can_focus = false;
|
||||||
|
item.actor.reparent(vbox);
|
||||||
|
}
|
||||||
|
|
||||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
if (params.showEvents) {
|
||||||
pseudo_class: 'highlighted' });
|
// Add vertical separator
|
||||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
|
||||||
hbox.add(item);
|
|
||||||
|
|
||||||
// Fill up the second column
|
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||||
|
pseudo_class: 'highlighted' });
|
||||||
|
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||||
|
hbox.add(item);
|
||||||
|
|
||||||
vbox = new St.BoxLayout({vertical: true});
|
// Fill up the second column
|
||||||
hbox.add(vbox, { expand: true });
|
vbox = new St.BoxLayout({name: 'calendarEventsArea',
|
||||||
|
vertical: true});
|
||||||
|
hbox.add(vbox, { expand: true });
|
||||||
|
|
||||||
// Event list
|
// Event list
|
||||||
vbox.add(this._eventList.actor, { expand: true });
|
vbox.add(this._eventList.actor, { expand: true });
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||||
item.actor.can_focus = false;
|
item.actor.can_focus = false;
|
||||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
}
|
||||||
|
|
||||||
// Whenever the menu is opened, select today
|
// Whenever the menu is opened, select today
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||||
@ -144,6 +159,10 @@ DateMenuButton.prototype = {
|
|||||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||||
|
|
||||||
|
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
||||||
|
this._upClient = new UPowerGlib.Client();
|
||||||
|
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
|
||||||
|
|
||||||
// Start the clock
|
// Start the clock
|
||||||
this._updateClockAndDate();
|
this._updateClockAndDate();
|
||||||
},
|
},
|
||||||
@ -159,12 +178,12 @@ DateMenuButton.prototype = {
|
|||||||
switch (format) {
|
switch (format) {
|
||||||
case '24h':
|
case '24h':
|
||||||
if (showDate)
|
if (showDate)
|
||||||
/* Translators: This is the time format with date used
|
/* Translators: This is the time format with date used
|
||||||
in 24-hour mode. */
|
in 24-hour mode. */
|
||||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||||
: _("%a %b %e, %R");
|
: _("%a %b %e, %R");
|
||||||
else
|
else
|
||||||
/* Translators: This is the time format without date used
|
/* Translators: This is the time format without date used
|
||||||
in 24-hour mode. */
|
in 24-hour mode. */
|
||||||
clockFormat = showSeconds ? _("%a %R:%S")
|
clockFormat = showSeconds ? _("%a %R:%S")
|
||||||
: _("%a %R");
|
: _("%a %R");
|
||||||
@ -172,12 +191,12 @@ DateMenuButton.prototype = {
|
|||||||
case '12h':
|
case '12h':
|
||||||
default:
|
default:
|
||||||
if (showDate)
|
if (showDate)
|
||||||
/* Translators: This is a time format with date used
|
/* Translators: This is a time format with date used
|
||||||
for AM/PM. */
|
for AM/PM. */
|
||||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||||
: _("%a %b %e, %l:%M %p");
|
: _("%a %b %e, %l:%M %p");
|
||||||
else
|
else
|
||||||
/* Translators: This is a time format without date used
|
/* Translators: This is a time format without date used
|
||||||
for AM/PM. */
|
for AM/PM. */
|
||||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||||
: _("%a %l:%M %p");
|
: _("%a %l:%M %p");
|
||||||
@ -198,15 +217,26 @@ DateMenuButton.prototype = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onPreferencesActivate: function() {
|
|
||||||
this.menu.close();
|
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
|
|
||||||
app.activate(-1);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onOpenCalendarActivate: function() {
|
_onOpenCalendarActivate: function() {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
// TODO: pass the selected day
|
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||||
Util.spawn(['evolution', '-c', 'calendar']);
|
let tool = calendarSettings.get_string('exec');
|
||||||
|
if (tool.length == 0 || tool == 'evolution') {
|
||||||
|
// TODO: pass the selected day
|
||||||
|
Util.spawn(['evolution', '-c', 'calendar']);
|
||||||
|
} else {
|
||||||
|
let needTerm = calendarSettings.get_boolean('needs-term');
|
||||||
|
if (needTerm) {
|
||||||
|
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||||
|
let term = terminalSettings.get_string('exec');
|
||||||
|
let arg = terminalSettings.get_string('exec-arg');
|
||||||
|
if (arg != '')
|
||||||
|
Util.spawn([term, arg, tool]);
|
||||||
|
else
|
||||||
|
Util.spawn([term, tool]);
|
||||||
|
} else {
|
||||||
|
Util.spawnCommandLine(tool)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -61,7 +61,7 @@ function addDragMonitor(monitor) {
|
|||||||
dragMonitors.push(monitor);
|
dragMonitors.push(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeMonitor(monitor) {
|
function removeDragMonitor(monitor) {
|
||||||
for (let i = 0; i < dragMonitors.length; i++)
|
for (let i = 0; i < dragMonitors.length; i++)
|
||||||
if (dragMonitors[i] == monitor) {
|
if (dragMonitors[i] == monitor) {
|
||||||
dragMonitors.splice(i, 1);
|
dragMonitors.splice(i, 1);
|
||||||
@ -284,13 +284,13 @@ _Draggable.prototype = {
|
|||||||
this._dragOffsetY = actorStageY - this._dragStartY;
|
this._dragOffsetY = actorStageY - this._dragStartY;
|
||||||
|
|
||||||
// Set the actor's scale such that it will keep the same
|
// Set the actor's scale such that it will keep the same
|
||||||
// transformed size when it's reparented to the stage
|
// transformed size when it's reparented to the uiGroup
|
||||||
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
|
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
|
||||||
this.actor.set_scale(scaledWidth / this.actor.width,
|
this.actor.set_scale(scaledWidth / this.actor.width,
|
||||||
scaledHeight / this.actor.height);
|
scaledHeight / this.actor.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dragActor.reparent(this.actor.get_stage());
|
this._dragActor.reparent(Main.uiGroup);
|
||||||
this._dragActor.raise_top();
|
this._dragActor.raise_top();
|
||||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ _Draggable.prototype = {
|
|||||||
return true;
|
return true;
|
||||||
// If it accepted the drop without taking the actor,
|
// If it accepted the drop without taking the actor,
|
||||||
// handle it ourselves.
|
// handle it ourselves.
|
||||||
if (this._dragActor.get_parent() == this._dragActor.get_stage()) {
|
if (this._dragActor.get_parent() == Main.uiGroup) {
|
||||||
if (this._restoreOnSuccess) {
|
if (this._restoreOnSuccess) {
|
||||||
this._restoreDragActor(event.get_time());
|
this._restoreDragActor(event.get_time());
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const DocInfo = imports.misc.docInfo;
|
const DocInfo = imports.misc.docInfo;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Search = imports.ui.search;
|
const Search = imports.ui.search;
|
||||||
@ -33,11 +30,11 @@ DocSearchProvider.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
activateResult: function(id, params) {
|
activateResult: function(id, params) {
|
||||||
params = Params.parse(params, { workspace: null,
|
params = Params.parse(params, { workspace: -1,
|
||||||
timestamp: null });
|
timestamp: 0 });
|
||||||
|
|
||||||
let docInfo = this._docManager.lookupByUri(id);
|
let docInfo = this._docManager.lookupByUri(id);
|
||||||
docInfo.launch(params.workspace ? params.workspace.index() : -1);
|
docInfo.launch(params.workspace);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms) {
|
||||||
|
@ -22,11 +22,8 @@ const DBus = imports.dbus;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
const AccountsService = imports.gi.AccountsService;
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gdm = imports.gi.Gdm;
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
@ -116,7 +113,7 @@ function findAppFromInhibitor(inhibitor) {
|
|||||||
let app = null;
|
let app = null;
|
||||||
for (let i = 0; i < candidateDesktopFiles.length; i++) {
|
for (let i = 0; i < candidateDesktopFiles.length; i++) {
|
||||||
try {
|
try {
|
||||||
app = appSystem.get_app(candidateDesktopFiles[i]);
|
app = appSystem.lookup_app(candidateDesktopFiles[i]);
|
||||||
|
|
||||||
if (app)
|
if (app)
|
||||||
break;
|
break;
|
||||||
@ -176,7 +173,7 @@ ListItem.prototype = {
|
|||||||
|
|
||||||
_onClicked: function() {
|
_onClicked: function() {
|
||||||
this.emit('activate');
|
this.emit('activate');
|
||||||
this._app.activate(-1);
|
this._app.activate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Signals.addSignalMethods(ListItem.prototype);
|
Signals.addSignalMethods(ListItem.prototype);
|
||||||
@ -240,7 +237,7 @@ EndSessionDialog.prototype = {
|
|||||||
_init: function() {
|
_init: function() {
|
||||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
|
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
|
||||||
|
|
||||||
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
|
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
||||||
|
|
||||||
this._secondsLeft = 0;
|
this._secondsLeft = 0;
|
||||||
this._totalSecondsToStayOpen = 0;
|
this._totalSecondsToStayOpen = 0;
|
||||||
@ -408,10 +405,6 @@ EndSessionDialog.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateButtons: function() {
|
_updateButtons: function() {
|
||||||
if (this.state != ModalDialog.State.OPENING &&
|
|
||||||
this.state != ModalDialog.State.OPENED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
let buttons = [{ action: Lang.bind(this, this.cancel),
|
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||||
label: _("Cancel"),
|
label: _("Cancel"),
|
||||||
@ -521,11 +514,12 @@ EndSessionDialog.prototype = {
|
|||||||
this._inhibitors.push(inhibitor);
|
this._inhibitors.push(inhibitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._updateButtons();
|
||||||
|
|
||||||
if (!this.open(timestamp))
|
if (!this.open(timestamp))
|
||||||
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
|
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
|
||||||
"Cannot grab pointer and keyboard");
|
"Cannot grab pointer and keyboard");
|
||||||
|
|
||||||
this._updateButtons();
|
|
||||||
this._updateContent();
|
this._updateContent();
|
||||||
|
|
||||||
let signalId = this.connect('opened',
|
let signalId = this.connect('opened',
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
imports.gi.versions.Clutter = '1.0';
|
||||||
|
imports.gi.versions.Gio = '2.0';
|
||||||
|
imports.gi.versions.Gdk = '3.0';
|
||||||
|
imports.gi.versions.GdkPixbuf = '2.0';
|
||||||
|
imports.gi.versions.Gtk = '3.0';
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;;
|
const Clutter = imports.gi.Clutter;;
|
||||||
|
const Gettext = imports.gettext;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
|
||||||
|
|
||||||
const Tweener = imports.ui.tweener;
|
// We can't import shell JS modules yet, because they may have
|
||||||
|
// variable initializations, etc, that depend on init() already having
|
||||||
|
// been run.
|
||||||
|
|
||||||
const Format = imports.misc.format;
|
|
||||||
|
|
||||||
// "monkey patch" in some varargs ClutterContainer methods; we need
|
// "monkey patch" in some varargs ClutterContainer methods; we need
|
||||||
// to do this per-container class since there is no representation
|
// to do this per-container class since there is no representation
|
||||||
@ -31,49 +39,42 @@ function _patchContainerClass(containerClass) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace @method with something that throws an error instead
|
function init() {
|
||||||
function _blockMethod(method, replacement, reason) {
|
// Add some bindings to the global JS namespace; (gjs keeps the web
|
||||||
let match = method.match(/^(.+)\.([^.]+)$/);
|
// browser convention of having that namespace be called 'window'.)
|
||||||
if (!match)
|
window.global = Shell.Global.get();
|
||||||
throw new Error('Bad method name "' + method + '"');
|
|
||||||
let proto = 'imports.gi.' + match[1] + '.prototype';
|
|
||||||
let property = match[2];
|
|
||||||
|
|
||||||
if (!global.set_property_mutable(proto, property, true))
|
window._ = Gettext.gettext;
|
||||||
throw new Error('Bad method name "' + method + '"');
|
window.C_ = Gettext.pgettext;
|
||||||
|
window.ngettext = Gettext.ngettext;
|
||||||
|
|
||||||
// eval() is evil in general, but we know it's safe here since
|
// Set the default direction for St widgets (this needs to be done before any use of St)
|
||||||
// set_property_mutable() would have failed if proto was
|
if (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL) {
|
||||||
// malformed.
|
St.Widget.set_default_direction(St.TextDirection.RTL);
|
||||||
let node = eval(proto);
|
}
|
||||||
|
|
||||||
let msg = 'Do not use "' + method + '".';
|
// Miscellaneous monkeypatching
|
||||||
if (replacement)
|
_patchContainerClass(St.BoxLayout);
|
||||||
msg += ' Use "' + replacement + '" instead.';
|
_patchContainerClass(St.Table);
|
||||||
if (reason)
|
|
||||||
msg += ' (' + reason + ')';
|
|
||||||
|
|
||||||
node[property] = function() {
|
Clutter.Actor.prototype.toString = function() {
|
||||||
throw new Error(msg);
|
return St.describe_actor(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
global.set_property_mutable(proto, property, false);
|
let origToString = Object.prototype.toString;
|
||||||
}
|
Object.prototype.toString = function() {
|
||||||
|
let base = origToString.call(this);
|
||||||
function init() {
|
if ('actor' in this && this.actor instanceof Clutter.Actor)
|
||||||
Tweener.init();
|
return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1));
|
||||||
String.prototype.format = Format.format;
|
else
|
||||||
|
return base;
|
||||||
|
};
|
||||||
|
|
||||||
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
|
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
|
||||||
Date.prototype.toLocaleFormat = function(format) {
|
Date.prototype.toLocaleFormat = function(format) {
|
||||||
return Shell.util_format_date(format, this.getTime());
|
return Shell.util_format_date(format, this.getTime());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set the default direction for St widgets (this needs to be done before any use of St)
|
|
||||||
if (Gettext_gtk30.gettext('default:LTR') == 'default:RTL') {
|
|
||||||
St.Widget.set_default_direction(St.TextDirection.RTL);
|
|
||||||
}
|
|
||||||
|
|
||||||
let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
|
let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
|
||||||
if (slowdownEnv) {
|
if (slowdownEnv) {
|
||||||
let factor = parseFloat(slowdownEnv);
|
let factor = parseFloat(slowdownEnv);
|
||||||
@ -81,24 +82,10 @@ function init() {
|
|||||||
St.set_slow_down_factor(factor);
|
St.set_slow_down_factor(factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
_patchContainerClass(St.BoxLayout);
|
// OK, now things are initialized enough that we can import shell JS
|
||||||
_patchContainerClass(St.Table);
|
const Format = imports.misc.format;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
Clutter.Actor.prototype.toString = function() {
|
Tweener.init();
|
||||||
return St.describe_actor(this);
|
String.prototype.format = Format.format;
|
||||||
};
|
|
||||||
|
|
||||||
if (window.global === undefined) // test environment
|
|
||||||
return;
|
|
||||||
|
|
||||||
_blockMethod('Clutter.Event.get_state', 'Shell.get_event_state',
|
|
||||||
'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.');
|
|
||||||
_blockMethod('Gdk.Window.get_device_position', 'global.get_pointer',
|
|
||||||
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
|
|
||||||
|
|
||||||
// Now close the back door to prevent extensions from trying to
|
|
||||||
// abuse it. We can't actually delete it since
|
|
||||||
// Shell.Global.prototype itself is read-only.
|
|
||||||
global.set_property_mutable('imports.gi.Shell.Global.prototype', 'set_property_mutable', true);
|
|
||||||
Shell.Global.prototype.set_property_mutable = undefined;
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Soup = imports.gi.Soup;
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
|
|
||||||
@ -10,7 +15,12 @@ const ExtensionState = {
|
|||||||
ENABLED: 1,
|
ENABLED: 1,
|
||||||
DISABLED: 2,
|
DISABLED: 2,
|
||||||
ERROR: 3,
|
ERROR: 3,
|
||||||
OUT_OF_DATE: 4
|
OUT_OF_DATE: 4,
|
||||||
|
DOWNLOADING: 5,
|
||||||
|
|
||||||
|
// Used as an error state for operations on unknown extensions,
|
||||||
|
// should never be in a real extensionMeta object.
|
||||||
|
UNINSTALLED: 99
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExtensionType = {
|
const ExtensionType = {
|
||||||
@ -18,15 +28,40 @@ const ExtensionType = {
|
|||||||
PER_USER: 2
|
PER_USER: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _httpSession = new Soup.SessionAsync();
|
||||||
|
|
||||||
|
// The unfortunate state of gjs, gobject-introspection and libsoup
|
||||||
|
// means that I have to do a hack to add a feature.
|
||||||
|
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||||
|
|
||||||
|
if (Soup.Session.prototype.add_feature != null)
|
||||||
|
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||||
|
|
||||||
// Maps uuid -> metadata object
|
// Maps uuid -> metadata object
|
||||||
const extensionMeta = {};
|
const extensionMeta = {};
|
||||||
// Maps uuid -> importer object (extension directory tree)
|
// Maps uuid -> importer object (extension directory tree)
|
||||||
const extensions = {};
|
const extensions = {};
|
||||||
// Array of uuids
|
// Maps uuid -> extension state object (returned from init())
|
||||||
var disabledExtensions;
|
const extensionStateObjs = {};
|
||||||
|
// Arrays of uuids
|
||||||
|
var enabledExtensions;
|
||||||
// GFile for user extensions
|
// GFile for user extensions
|
||||||
var userExtensionsDir = null;
|
var userExtensionsDir = null;
|
||||||
|
|
||||||
|
// We don't really have a class to add signals on. So, create
|
||||||
|
// a simple dummy object, add the signal methods, and export those
|
||||||
|
// publically.
|
||||||
|
var _signals = {};
|
||||||
|
Signals.addSignalMethods(_signals);
|
||||||
|
|
||||||
|
const connect = Lang.bind(_signals, _signals.connect);
|
||||||
|
const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||||
|
|
||||||
|
// UUID => Array of error messages
|
||||||
|
var errors = {};
|
||||||
|
|
||||||
|
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* versionCheck:
|
* versionCheck:
|
||||||
* @required: an array of versions we're compatible with
|
* @required: an array of versions we're compatible with
|
||||||
@ -57,13 +92,150 @@ function versionCheck(required, current) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function installExtensionFromManifestURL(uuid, url) {
|
||||||
|
_httpSession.queue_message(
|
||||||
|
Soup.Message.new('GET', url),
|
||||||
|
function(session, message) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||||
|
logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest;
|
||||||
|
try {
|
||||||
|
manifest = JSON.parse(message.response_body.data);
|
||||||
|
} catch (e) {
|
||||||
|
logExtensionError(uuid, 'parsing: ' + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid != manifest['uuid']) {
|
||||||
|
logExtensionError(uuid, 'manifest: manifest uuids do not match');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let meta = extensionMeta[uuid] = { uuid: uuid,
|
||||||
|
state: ExtensionState.DOWNLOADING,
|
||||||
|
error: '' };
|
||||||
|
|
||||||
|
_signals.emit('extension-state-changed', meta);
|
||||||
|
|
||||||
|
installExtensionFromManifest(manifest, meta);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function installExtensionFromManifest(manifest, meta) {
|
||||||
|
let uuid = manifest['uuid'];
|
||||||
|
let name = manifest['name'];
|
||||||
|
|
||||||
|
if (!versionCheck(manifest['shell-version'], Config.PACKAGE_VERSION)) {
|
||||||
|
meta.state = ExtensionState.OUT_OF_DATE;
|
||||||
|
logExtensionError(uuid, 'version: ' + name + ' is not compatible with current GNOME Shell version', meta.state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = manifest['__installer'];
|
||||||
|
_httpSession.queue_message(Soup.Message.new('GET', url),
|
||||||
|
function(session, message) {
|
||||||
|
gotExtensionZipFile(session, message, uuid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotExtensionZipFile(session, message, uuid) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||||
|
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: use a GFile mkstemp-type method once one exists
|
||||||
|
let fd, tmpzip;
|
||||||
|
try {
|
||||||
|
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
|
||||||
|
} catch (e) {
|
||||||
|
logExtensionError(uuid, 'tempfile: ' + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = new Gio.UnixOutputStream({ fd: fd });
|
||||||
|
let dir = userExtensionsDir.get_child(uuid);
|
||||||
|
Shell.write_soup_message_to_stream(stream, message);
|
||||||
|
stream.close(null);
|
||||||
|
let [success, pid] = GLib.spawn_async(null,
|
||||||
|
['unzip', '-uod', dir.get_path(), '--', tmpzip],
|
||||||
|
null,
|
||||||
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||||
|
null);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
logExtensionError(uuid, 'extract: could not extract');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||||
|
GLib.spawn_close_pid(pid);
|
||||||
|
loadExtension(dir, true, ExtensionType.PER_USER);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableExtension(uuid) {
|
||||||
|
let meta = extensionMeta[uuid];
|
||||||
|
if (!meta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (meta.state != ExtensionState.ENABLED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let extensionState = extensionStateObjs[uuid];
|
||||||
|
|
||||||
|
try {
|
||||||
|
extensionState.disable();
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.state = ExtensionState.DISABLED;
|
||||||
|
_signals.emit('extension-state-changed', meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableExtension(uuid) {
|
||||||
|
let meta = extensionMeta[uuid];
|
||||||
|
if (!meta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (meta.state != ExtensionState.DISABLED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let extensionState = extensionStateObjs[uuid];
|
||||||
|
|
||||||
|
try {
|
||||||
|
extensionState.enable();
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.state = ExtensionState.ENABLED;
|
||||||
|
_signals.emit('extension-state-changed', meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logExtensionError(uuid, message, state) {
|
||||||
|
if (!errors[uuid]) errors[uuid] = [];
|
||||||
|
errors[uuid].push(message);
|
||||||
|
global.logError('Extension "%s" had error: %s'.format(uuid, message));
|
||||||
|
state = state || ExtensionState.ERROR;
|
||||||
|
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||||
|
error: message,
|
||||||
|
state: state });
|
||||||
|
}
|
||||||
|
|
||||||
function loadExtension(dir, enabled, type) {
|
function loadExtension(dir, enabled, type) {
|
||||||
let info;
|
let info;
|
||||||
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
|
let uuid = dir.get_basename();
|
||||||
|
|
||||||
let metadataFile = dir.get_child('metadata.json');
|
let metadataFile = dir.get_child('metadata.json');
|
||||||
if (!metadataFile.query_exists(null)) {
|
if (!metadataFile.query_exists(null)) {
|
||||||
global.logError(baseErrorString + 'Missing metadata.json');
|
logExtensionError(uuid, 'Missing metadata.json');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,61 +243,65 @@ function loadExtension(dir, enabled, type) {
|
|||||||
try {
|
try {
|
||||||
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
|
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
|
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let meta;
|
let meta;
|
||||||
try {
|
try {
|
||||||
meta = JSON.parse(metadataContents);
|
meta = JSON.parse(metadataContents);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
|
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||||
for (let i = 0; i < requiredProperties.length; i++) {
|
for (let i = 0; i < requiredProperties.length; i++) {
|
||||||
let prop = requiredProperties[i];
|
let prop = requiredProperties[i];
|
||||||
if (!meta[prop]) {
|
if (!meta[prop]) {
|
||||||
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
|
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extensions[meta.uuid] != undefined) {
|
if (extensions[uuid] != undefined) {
|
||||||
global.logError(baseErrorString + "extension already loaded");
|
logExtensionError(uuid, "extension already loaded");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encourage people to add this
|
// Encourage people to add this
|
||||||
if (!meta['url']) {
|
if (!meta['url']) {
|
||||||
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
|
global.log('Warning: Missing "url" property in metadata.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
let base = dir.get_basename();
|
if (uuid != meta.uuid) {
|
||||||
if (base != meta.uuid) {
|
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||||
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||||
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
|
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
extensionMeta[meta.uuid] = meta;
|
extensionMeta[uuid] = meta;
|
||||||
extensionMeta[meta.uuid].type = type;
|
meta.type = type;
|
||||||
extensionMeta[meta.uuid].path = dir.get_path();
|
meta.path = dir.get_path();
|
||||||
if (!enabled) {
|
meta.error = '';
|
||||||
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to error, we set success as the last step
|
// Default to error, we set success as the last step
|
||||||
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
|
meta.state = ExtensionState.ERROR;
|
||||||
|
|
||||||
|
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||||
|
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||||
|
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
||||||
|
meta.state = ExtensionState.OUT_OF_DATE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let extensionJs = dir.get_child('extension.js');
|
let extensionJs = dir.get_child('extension.js');
|
||||||
if (!extensionJs.query_exists(null)) {
|
if (!extensionJs.query_exists(null)) {
|
||||||
global.logError(baseErrorString + 'Missing extension.js');
|
logExtensionError(uuid, 'Missing extension.js');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let stylesheetPath = null;
|
let stylesheetPath = null;
|
||||||
@ -136,47 +312,94 @@ function loadExtension(dir, enabled, type) {
|
|||||||
try {
|
try {
|
||||||
theme.load_stylesheet(stylesheetFile.get_path());
|
theme.load_stylesheet(stylesheetFile.get_path());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
|
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let extensionModule;
|
let extensionModule;
|
||||||
|
let extensionState = null;
|
||||||
try {
|
try {
|
||||||
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
||||||
extensionModule = extensions[meta.uuid].extension;
|
extensionModule = extensions[meta.uuid].extension;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (stylesheetPath != null)
|
if (stylesheetPath != null)
|
||||||
theme.unload_stylesheet(stylesheetPath);
|
theme.unload_stylesheet(stylesheetPath);
|
||||||
global.logError(baseErrorString + e);
|
logExtensionError(uuid, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!extensionModule.main) {
|
|
||||||
global.logError(baseErrorString + 'missing \'main\' function');
|
if (!extensionModule.init) {
|
||||||
|
logExtensionError(uuid, 'missing \'init\' function');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
extensionModule.main(meta);
|
extensionState = extensionModule.init(meta);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (stylesheetPath != null)
|
if (stylesheetPath != null)
|
||||||
theme.unload_stylesheet(stylesheetPath);
|
theme.unload_stylesheet(stylesheetPath);
|
||||||
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
|
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
|
|
||||||
|
if (!extensionState)
|
||||||
|
extensionState = extensionModule;
|
||||||
|
extensionStateObjs[uuid] = extensionState;
|
||||||
|
|
||||||
|
if (!extensionState.enable) {
|
||||||
|
logExtensionError(uuid, 'missing \'enable\' function');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!extensionState.disable) {
|
||||||
|
logExtensionError(uuid, 'missing \'disable\' function');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.state = ExtensionState.DISABLED;
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
enableExtension(uuid);
|
||||||
|
|
||||||
|
_signals.emit('extension-loaded', meta.uuid);
|
||||||
|
_signals.emit('extension-state-changed', meta);
|
||||||
global.log('Loaded extension ' + meta.uuid);
|
global.log('Loaded extension ' + meta.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onEnabledExtensionsChanged() {
|
||||||
|
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||||
|
|
||||||
|
// Find and enable all the newly enabled extensions: UUIDs found in the
|
||||||
|
// new setting, but not in the old one.
|
||||||
|
newEnabledExtensions.filter(function(uuid) {
|
||||||
|
return enabledExtensions.indexOf(uuid) == -1;
|
||||||
|
}).forEach(function(uuid) {
|
||||||
|
enableExtension(uuid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||||
|
// old setting, but not in the new one.
|
||||||
|
enabledExtensions.filter(function(item) {
|
||||||
|
return newEnabledExtensions.indexOf(item) == -1;
|
||||||
|
}).forEach(function(uuid) {
|
||||||
|
disableExtension(uuid);
|
||||||
|
});
|
||||||
|
|
||||||
|
enabledExtensions = newEnabledExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
||||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||||
try {
|
try {
|
||||||
userExtensionsDir.make_directory_with_parents(null);
|
if (!userExtensionsDir.query_exists(null))
|
||||||
|
userExtensionsDir.make_directory_with_parents(null);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
global.logError('' + e);
|
global.logError('' + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
|
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||||
|
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loadExtensionsIn(dir, type) {
|
function _loadExtensionsIn(dir, type) {
|
||||||
@ -194,8 +417,8 @@ function _loadExtensionsIn(dir, type) {
|
|||||||
if (fileType != Gio.FileType.DIRECTORY)
|
if (fileType != Gio.FileType.DIRECTORY)
|
||||||
continue;
|
continue;
|
||||||
let name = info.get_name();
|
let name = info.get_name();
|
||||||
let enabled = disabledExtensions.indexOf(name) < 0;
|
|
||||||
let child = dir.get_child(name);
|
let child = dir.get_child(name);
|
||||||
|
let enabled = enabledExtensions.indexOf(name) != -1;
|
||||||
loadExtension(child, enabled, type);
|
loadExtension(child, enabled, type);
|
||||||
}
|
}
|
||||||
fileEnum.close(null);
|
fileEnum.close(null);
|
||||||
|
@ -42,10 +42,10 @@ BaseIcon.prototype = {
|
|||||||
box.add_actor(this._iconBin);
|
box.add_actor(this._iconBin);
|
||||||
|
|
||||||
if (params.showLabel) {
|
if (params.showLabel) {
|
||||||
this._name = new St.Label({ text: label });
|
this.label = new St.Label({ text: label });
|
||||||
box.add_actor(this._name);
|
box.add_actor(this.label);
|
||||||
} else {
|
} else {
|
||||||
this._name = null;
|
this.label = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.createIcon)
|
if (params.createIcon)
|
||||||
@ -67,8 +67,8 @@ BaseIcon.prototype = {
|
|||||||
|
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
|
|
||||||
if (this._name) {
|
if (this.label) {
|
||||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
|
let [labelMinHeight, labelNatHeight] = this.label.get_preferred_height(-1);
|
||||||
preferredHeight += this._spacing + labelNatHeight;
|
preferredHeight += this._spacing + labelNatHeight;
|
||||||
|
|
||||||
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
|
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
|
||||||
@ -79,7 +79,7 @@ BaseIcon.prototype = {
|
|||||||
childBox.x2 = availWidth;
|
childBox.x2 = availWidth;
|
||||||
childBox.y1 = iconSize + this._spacing;
|
childBox.y1 = iconSize + this._spacing;
|
||||||
childBox.y2 = childBox.y1 + labelHeight;
|
childBox.y2 = childBox.y1 + labelHeight;
|
||||||
this._name.allocate(childBox, flags);
|
this.label.allocate(childBox, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
childBox.x1 = Math.floor((availWidth - iconNatWidth) / 2);
|
childBox.x1 = Math.floor((availWidth - iconNatWidth) / 2);
|
||||||
@ -98,8 +98,8 @@ BaseIcon.prototype = {
|
|||||||
alloc.min_size = iconMinHeight;
|
alloc.min_size = iconMinHeight;
|
||||||
alloc.natural_size = iconNatHeight;
|
alloc.natural_size = iconNatHeight;
|
||||||
|
|
||||||
if (this._name) {
|
if (this.label) {
|
||||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
|
let [labelMinHeight, labelNatHeight] = this.label.get_preferred_height(forWidth);
|
||||||
alloc.min_size += this._spacing + labelMinHeight;
|
alloc.min_size += this._spacing + labelMinHeight;
|
||||||
alloc.natural_size += this._spacing + labelNatHeight;
|
alloc.natural_size += this._spacing + labelNatHeight;
|
||||||
}
|
}
|
||||||
|
532
js/ui/keyboard.js
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Caribou = imports.gi.Caribou;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const Gdk = imports.gi.Gdk;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
|
||||||
|
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||||
|
const SHOW_KEYBOARD = 'show-keyboard';
|
||||||
|
const KEYBOARD_TYPE = 'keyboard-type';
|
||||||
|
|
||||||
|
// Key constants taken from Antler
|
||||||
|
// FIXME: ought to be moved into libcaribou
|
||||||
|
const PRETTY_KEYS = {
|
||||||
|
'BackSpace': '\u232b',
|
||||||
|
'space': ' ',
|
||||||
|
'Return': '\u23ce',
|
||||||
|
'Caribou_Prefs': '\u2328',
|
||||||
|
'Caribou_ShiftUp': '\u2b06',
|
||||||
|
'Caribou_ShiftDown': '\u2b07',
|
||||||
|
'Caribou_Emoticons': '\u263a',
|
||||||
|
'Caribou_Symbols': '123',
|
||||||
|
'Caribou_Symbols_More': '{#*',
|
||||||
|
'Caribou_Alpha': 'Abc',
|
||||||
|
'Tab': 'Tab',
|
||||||
|
'Escape': 'Esc',
|
||||||
|
'Control_L': 'Ctrl',
|
||||||
|
'Alt_L': 'Alt'
|
||||||
|
};
|
||||||
|
|
||||||
|
const CaribouKeyboardIface = {
|
||||||
|
name: 'org.gnome.Caribou.Keyboard',
|
||||||
|
methods: [ { name: 'Show',
|
||||||
|
inSignature: 'u',
|
||||||
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'Hide',
|
||||||
|
inSignature: 'u',
|
||||||
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'SetCursorLocation',
|
||||||
|
inSignature: 'iiii',
|
||||||
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'SetEntryLocation',
|
||||||
|
inSignature: 'iiii',
|
||||||
|
outSignature: ''
|
||||||
|
} ],
|
||||||
|
properties: [ { name: 'Name',
|
||||||
|
signature: 's',
|
||||||
|
access: 'read' } ]
|
||||||
|
};
|
||||||
|
|
||||||
|
function Key() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key.prototype = {
|
||||||
|
_init : function(key) {
|
||||||
|
this._key = key;
|
||||||
|
|
||||||
|
this.actor = this._makeKey();
|
||||||
|
|
||||||
|
this._extended_keys = this._key.get_extended_keys();
|
||||||
|
this._extended_keyboard = null;
|
||||||
|
|
||||||
|
if (this._key.name == "Control_L" || this._key.name == "Alt_L")
|
||||||
|
this._key.latch = true;
|
||||||
|
|
||||||
|
this._key.connect('key-pressed', Lang.bind(this, function ()
|
||||||
|
{ this.actor.checked = true }));
|
||||||
|
this._key.connect('key-released', Lang.bind(this, function ()
|
||||||
|
{ this.actor.checked = false; }));
|
||||||
|
|
||||||
|
if (this._extended_keys.length > 0) {
|
||||||
|
this._grabbed = false;
|
||||||
|
this._eventCaptureId = 0;
|
||||||
|
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
||||||
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
// Adds style to existing keyboard style to avoid repetition
|
||||||
|
this._boxPointer.actor.add_style_class_name('keyboard-subkeys');
|
||||||
|
this._getExtendedKeys();
|
||||||
|
this.actor._extended_keys = this._extended_keyboard;
|
||||||
|
this._boxPointer.actor.hide();
|
||||||
|
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeKey: function () {
|
||||||
|
let label = this._key.name;
|
||||||
|
|
||||||
|
if (label.length > 1) {
|
||||||
|
let pretty = PRETTY_KEYS[label];
|
||||||
|
if (pretty)
|
||||||
|
label = pretty;
|
||||||
|
else
|
||||||
|
label = this._getUnichar(this._key);
|
||||||
|
}
|
||||||
|
|
||||||
|
label = GLib.markup_escape_text(label, -1);
|
||||||
|
let button = new St.Button ({ label: label,
|
||||||
|
style_class: 'keyboard-key' });
|
||||||
|
|
||||||
|
button.key_width = this._key.width;
|
||||||
|
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
|
||||||
|
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
|
||||||
|
|
||||||
|
return button;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getUnichar: function(key) {
|
||||||
|
let keyval = key.keyval;
|
||||||
|
let unichar = Gdk.keyval_to_unicode(keyval);
|
||||||
|
if (unichar) {
|
||||||
|
return String.fromCharCode(unichar);
|
||||||
|
} else {
|
||||||
|
return key.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getExtendedKeys: function () {
|
||||||
|
this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||||
|
vertical: false });
|
||||||
|
for (let i = 0; i < this._extended_keys.length; ++i) {
|
||||||
|
let extended_key = this._extended_keys[i];
|
||||||
|
let label = this._getUnichar(extended_key);
|
||||||
|
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
|
||||||
|
key.extended_key = extended_key;
|
||||||
|
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
|
||||||
|
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
|
||||||
|
this._extended_keyboard.add(key);
|
||||||
|
}
|
||||||
|
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEventCapture: function (actor, event) {
|
||||||
|
let source = event.get_source();
|
||||||
|
let type = event.type();
|
||||||
|
|
||||||
|
if ((type == Clutter.EventType.BUTTON_PRESS ||
|
||||||
|
type == Clutter.EventType.BUTTON_RELEASE) &&
|
||||||
|
this._extended_keyboard.contains(source)) {
|
||||||
|
source.extended_key.press();
|
||||||
|
source.extended_key.release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type == Clutter.EventType.BUTTON_PRESS) {
|
||||||
|
this._boxPointer.actor.hide();
|
||||||
|
this._ungrab();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_ungrab: function () {
|
||||||
|
global.stage.disconnect(this._eventCaptureId);
|
||||||
|
this._eventCaptureId = 0;
|
||||||
|
this._grabbed = false;
|
||||||
|
Main.popModal(this.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShowSubkeysChanged: function () {
|
||||||
|
if (this._key.show_subkeys) {
|
||||||
|
this.actor.fake_release();
|
||||||
|
this._boxPointer.actor.raise_top();
|
||||||
|
this._boxPointer.setPosition(this.actor, 0.5);
|
||||||
|
this._boxPointer.show(true);
|
||||||
|
this.actor.set_hover(false);
|
||||||
|
if (!this._grabbed) {
|
||||||
|
Main.pushModal(this.actor);
|
||||||
|
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
||||||
|
this._grabbed = true;
|
||||||
|
}
|
||||||
|
this._key.release();
|
||||||
|
} else {
|
||||||
|
if (this._grabbed)
|
||||||
|
this._ungrab();
|
||||||
|
this._boxPointer.hide(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Keyboard() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard.prototype = {
|
||||||
|
_init: function () {
|
||||||
|
DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
|
||||||
|
DBus.session.acquire_name('org.gnome.Caribou.Keyboard', 0, null, null);
|
||||||
|
|
||||||
|
this._timestamp = global.get_current_time();
|
||||||
|
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
||||||
|
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
||||||
|
Main.layoutManager.trackChrome(this.actor);
|
||||||
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||||
|
|
||||||
|
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||||
|
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||||
|
this._settingsChanged();
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
if (this._enableKeyboard)
|
||||||
|
this._redraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
_settingsChanged: function () {
|
||||||
|
this._enableKeyboard = this._keyboardSettings.get_boolean(SHOW_KEYBOARD);
|
||||||
|
if (!this._enableKeyboard && !this._keyboard)
|
||||||
|
return;
|
||||||
|
if (this._enableKeyboard && this._keyboard &&
|
||||||
|
this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._keyboard)
|
||||||
|
this._destroyKeyboard();
|
||||||
|
if (this._enableKeyboard)
|
||||||
|
this._setupKeyboard();
|
||||||
|
else
|
||||||
|
Main.layoutManager.hideKeyboard(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
_destroyKeyboard: function() {
|
||||||
|
if (this._keyboardNotifyId)
|
||||||
|
this._keyboard.disconnect(this._keyboardNotifyId);
|
||||||
|
if (this._focusNotifyId)
|
||||||
|
global.stage.disconnect(this._focusNotifyId);
|
||||||
|
this._keyboard = null;
|
||||||
|
this.actor.destroy_children();
|
||||||
|
|
||||||
|
this._destroySource();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupKeyboard: function() {
|
||||||
|
this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
|
||||||
|
this._groups = {};
|
||||||
|
this._current_page = null;
|
||||||
|
|
||||||
|
// Initialize keyboard key measurements
|
||||||
|
this._numOfHorizKeys = 0;
|
||||||
|
this._numOfVertKeys = 0;
|
||||||
|
|
||||||
|
this._addKeys();
|
||||||
|
|
||||||
|
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
||||||
|
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||||
|
this._createSource();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyFocusChanged: function () {
|
||||||
|
let focus = global.stage.key_focus;
|
||||||
|
|
||||||
|
if (focus == global.stage || focus == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (focus instanceof Clutter.Text)
|
||||||
|
this.show();
|
||||||
|
else {
|
||||||
|
if (focus._extended_keys == null)
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_addKeys: function () {
|
||||||
|
let groups = this._keyboard.get_groups();
|
||||||
|
for (let i = 0; i < groups.length; ++i) {
|
||||||
|
let gname = groups[i];
|
||||||
|
let group = this._keyboard.get_group(gname);
|
||||||
|
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
||||||
|
let layers = {};
|
||||||
|
let levels = group.get_levels();
|
||||||
|
for (let j = 0; j < levels.length; ++j) {
|
||||||
|
let lname = levels[j];
|
||||||
|
let level = group.get_level(lname);
|
||||||
|
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||||
|
vertical: true });
|
||||||
|
this._loadRows(level, layout);
|
||||||
|
layers[lname] = layout;
|
||||||
|
this.actor.add(layout, { x_fill: false });
|
||||||
|
|
||||||
|
layout.hide();
|
||||||
|
}
|
||||||
|
this._groups[gname] = layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setActiveLayer();
|
||||||
|
},
|
||||||
|
|
||||||
|
_getTrayIcon: function () {
|
||||||
|
let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' });
|
||||||
|
trayButton.key_width = 1;
|
||||||
|
trayButton.connect('button-press-event', Lang.bind(this, function () {
|
||||||
|
Main.messageTray.toggle();
|
||||||
|
}));
|
||||||
|
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||||
|
trayButton.reactive = false;
|
||||||
|
trayButton.add_style_pseudo_class('grayed');
|
||||||
|
}));
|
||||||
|
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||||
|
trayButton.reactive = true;
|
||||||
|
trayButton.remove_style_pseudo_class('grayed');
|
||||||
|
}));
|
||||||
|
|
||||||
|
return trayButton;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addRows : function (keys, layout) {
|
||||||
|
let keyboard_row = new St.BoxLayout();
|
||||||
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
|
let children = keys[i].get_children();
|
||||||
|
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
|
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
|
for (let j = 0; j < children.length; ++j) {
|
||||||
|
if (this._numOfHorizKeys == 0)
|
||||||
|
this._numOfHorizKeys = children.length;
|
||||||
|
let key = children[j];
|
||||||
|
let button = new Key(key);
|
||||||
|
|
||||||
|
if (key.align == 'right')
|
||||||
|
right_box.add(button.actor);
|
||||||
|
else
|
||||||
|
left_box.add(button.actor);
|
||||||
|
if (key.name == "Caribou_Prefs") {
|
||||||
|
key.connect('key-released', Lang.bind(this, this.hide));
|
||||||
|
|
||||||
|
// Add new key for hiding message tray
|
||||||
|
right_box.add(this._getTrayIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||||
|
}
|
||||||
|
layout.add(keyboard_row);
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadRows : function (level, layout) {
|
||||||
|
let rows = level.get_rows();
|
||||||
|
for (let i = 0; i < rows.length; ++i) {
|
||||||
|
let row = rows[i];
|
||||||
|
if (this._numOfVertKeys == 0)
|
||||||
|
this._numOfVertKeys = rows.length;
|
||||||
|
this._addRows(row.get_columns(), layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_redraw: function () {
|
||||||
|
let monitor = Main.layoutManager.bottomMonitor;
|
||||||
|
let maxHeight = monitor.height / 3;
|
||||||
|
this.actor.width = monitor.width;
|
||||||
|
|
||||||
|
let layout = this._current_page;
|
||||||
|
let verticalSpacing = layout.get_theme_node().get_length('spacing');
|
||||||
|
let padding = layout.get_theme_node().get_length('padding');
|
||||||
|
|
||||||
|
let box = layout.get_children()[0].get_children()[0];
|
||||||
|
let horizontalSpacing = box.get_theme_node().get_length('spacing');
|
||||||
|
let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing;
|
||||||
|
let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys);
|
||||||
|
|
||||||
|
let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing;
|
||||||
|
let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys);
|
||||||
|
|
||||||
|
let keySize = Math.min(keyWidth, keyHeight);
|
||||||
|
this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding;
|
||||||
|
|
||||||
|
let rows = this._current_page.get_children();
|
||||||
|
for (let i = 0; i < rows.length; ++i) {
|
||||||
|
let keyboard_row = rows[i];
|
||||||
|
let boxes = keyboard_row.get_children();
|
||||||
|
for (let j = 0; j < boxes.length; ++j) {
|
||||||
|
let keys = boxes[j].get_children();
|
||||||
|
for (let k = 0; k < keys.length; ++k) {
|
||||||
|
let child = keys[k];
|
||||||
|
child.width = keySize * child.key_width;
|
||||||
|
child.height = keySize;
|
||||||
|
if (child._extended_keys) {
|
||||||
|
let extended_keys = child._extended_keys.get_children();
|
||||||
|
for (let n = 0; n < extended_keys.length; ++n) {
|
||||||
|
let extended_key = extended_keys[n];
|
||||||
|
extended_key.width = keySize;
|
||||||
|
extended_key.height = keySize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLevelChanged: function () {
|
||||||
|
this._setActiveLayer();
|
||||||
|
this._redraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onGroupChanged: function () {
|
||||||
|
this._setActiveLayer();
|
||||||
|
this._redraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setActiveLayer: function () {
|
||||||
|
let active_group_name = this._keyboard.active_group;
|
||||||
|
let active_group = this._keyboard.get_group(active_group_name);
|
||||||
|
let active_level = active_group.active_level;
|
||||||
|
let layers = this._groups[active_group_name];
|
||||||
|
|
||||||
|
if (this._current_page != null) {
|
||||||
|
this._current_page.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._current_page = layers[active_level];
|
||||||
|
this._current_page.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_createSource: function () {
|
||||||
|
if (this._source == null) {
|
||||||
|
this._source = new KeyboardSource(this);
|
||||||
|
this._source.setTransient(true);
|
||||||
|
Main.messageTray.add(this._source);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_destroySource: function () {
|
||||||
|
if (this._source) {
|
||||||
|
this._source.destroy();
|
||||||
|
this._source = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function () {
|
||||||
|
this._redraw();
|
||||||
|
|
||||||
|
Main.layoutManager.showKeyboard();
|
||||||
|
this._destroySource();
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function () {
|
||||||
|
Main.layoutManager.hideKeyboard();
|
||||||
|
this._createSource();
|
||||||
|
},
|
||||||
|
|
||||||
|
_moveTemporarily: function () {
|
||||||
|
let currentWindow = global.screen.get_display().focus_window;
|
||||||
|
let rect = currentWindow.get_outer_rect();
|
||||||
|
|
||||||
|
let newX = rect.x;
|
||||||
|
let newY = 3 * this.actor.height / 2;
|
||||||
|
currentWindow.move_frame(true, newX, newY);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setLocation: function (x, y) {
|
||||||
|
if (y >= 2 * this.actor.height)
|
||||||
|
this._moveTemporarily();
|
||||||
|
},
|
||||||
|
|
||||||
|
// D-Bus methods
|
||||||
|
Show: function(timestamp) {
|
||||||
|
if (timestamp - this._timestamp < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._timestamp = timestamp;
|
||||||
|
this.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
Hide: function(timestamp) {
|
||||||
|
if (timestamp - this._timestamp < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._timestamp = timestamp;
|
||||||
|
this.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
SetCursorLocation: function(x, y, w, h) {
|
||||||
|
this._setLocation(x, y);
|
||||||
|
},
|
||||||
|
|
||||||
|
SetEntryLocation: function(x, y, w, h) {
|
||||||
|
this._setLocation(x, y);
|
||||||
|
},
|
||||||
|
|
||||||
|
get Name() {
|
||||||
|
return 'gnome-shell';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
|
||||||
|
|
||||||
|
function KeyboardSource() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardSource.prototype = {
|
||||||
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
|
_init: function(keyboard) {
|
||||||
|
this._keyboard = keyboard;
|
||||||
|
MessageTray.Source.prototype._init.call(this, _("Keyboard"));
|
||||||
|
|
||||||
|
this._setSummaryIcon(this.createNotificationIcon());
|
||||||
|
},
|
||||||
|
|
||||||
|
createNotificationIcon: function() {
|
||||||
|
return new St.Icon({ icon_name: 'input-keyboard',
|
||||||
|
icon_type: St.IconType.SYMBOLIC,
|
||||||
|
icon_size: this.ICON_SIZE });
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSummaryClick: function() {
|
||||||
|
let event = Clutter.get_current_event();
|
||||||
|
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.open();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function() {
|
||||||
|
this._keyboard.show();
|
||||||
|
}
|
||||||
|
};
|
928
js/ui/layout.js
Normal file
@ -0,0 +1,928 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const ScreenSaver = imports.misc.screenSaver;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||||
|
const STARTUP_ANIMATION_TIME = 0.2;
|
||||||
|
const KEYBOARD_ANIMATION_TIME = 0.5;
|
||||||
|
|
||||||
|
function LayoutManager() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutManager.prototype = {
|
||||||
|
_init: function () {
|
||||||
|
this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
|
||||||
|
this.monitors = [];
|
||||||
|
this.primaryMonitor = null;
|
||||||
|
this.primaryIndex = -1;
|
||||||
|
this._hotCorners = [];
|
||||||
|
this._leftPanelBarrier = 0;
|
||||||
|
this._rightPanelBarrier = 0;
|
||||||
|
this._trayBarrier = 0;
|
||||||
|
|
||||||
|
this._chrome = new Chrome(this);
|
||||||
|
|
||||||
|
this.panelBox = new St.BoxLayout({ name: 'panelBox',
|
||||||
|
vertical: true });
|
||||||
|
this.addChrome(this.panelBox, { affectsStruts: true });
|
||||||
|
this.panelBox.connect('allocation-changed',
|
||||||
|
Lang.bind(this, this._updatePanelBarriers));
|
||||||
|
|
||||||
|
// bottomBox contains the tray and keyboard (which move
|
||||||
|
// together, since the tray slides up from the top of the
|
||||||
|
// keyboard when the keyboard is visible).
|
||||||
|
this._bottomBox = new St.BoxLayout({ name: 'bottomBox',
|
||||||
|
vertical: true });
|
||||||
|
this.addChrome(this._bottomBox, { visibleInFullscreen: true });
|
||||||
|
|
||||||
|
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
|
||||||
|
this.trayBox.connect('allocation-changed',
|
||||||
|
Lang.bind(this, this._updateTrayBarrier));
|
||||||
|
this._bottomBox.add_actor(this.trayBox);
|
||||||
|
|
||||||
|
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox' });
|
||||||
|
this._bottomBox.add_actor(this.keyboardBox);
|
||||||
|
|
||||||
|
global.screen.connect('monitors-changed',
|
||||||
|
Lang.bind(this, this._monitorsChanged));
|
||||||
|
this._monitorsChanged();
|
||||||
|
},
|
||||||
|
|
||||||
|
// This is called by Main after everything else is constructed;
|
||||||
|
// Chrome.init() needs access to Main.overview, which didn't exist
|
||||||
|
// yet when the LayoutManager was constructed.
|
||||||
|
init: function() {
|
||||||
|
this._chrome.init();
|
||||||
|
|
||||||
|
this._startupAnimation();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateMonitors: function() {
|
||||||
|
let screen = global.screen;
|
||||||
|
|
||||||
|
this.monitors = [];
|
||||||
|
let nMonitors = screen.get_n_monitors();
|
||||||
|
for (let i = 0; i < nMonitors; i++)
|
||||||
|
this.monitors.push(screen.get_monitor_geometry(i));
|
||||||
|
|
||||||
|
if (nMonitors == 1) {
|
||||||
|
this.primaryIndex = this.bottomIndex = 0;
|
||||||
|
} else {
|
||||||
|
// If there are monitors below the primary, then we need
|
||||||
|
// to split primary from bottom.
|
||||||
|
this.primaryIndex = this.bottomIndex = screen.get_primary_monitor();
|
||||||
|
for (let i = 0; i < this.monitors.length; i++) {
|
||||||
|
let monitor = this.monitors[i];
|
||||||
|
if (this._isAboveOrBelowPrimary(monitor)) {
|
||||||
|
if (monitor.y > this.monitors[this.bottomIndex].y)
|
||||||
|
this.bottomIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.primaryMonitor = this.monitors[this.primaryIndex];
|
||||||
|
this.bottomMonitor = this.monitors[this.bottomIndex];
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateHotCorners: function() {
|
||||||
|
// destroy old hot corners
|
||||||
|
for (let i = 0; i < this._hotCorners.length; i++)
|
||||||
|
this._hotCorners[i].destroy();
|
||||||
|
this._hotCorners = [];
|
||||||
|
|
||||||
|
// build new hot corners
|
||||||
|
for (let i = 0; i < this.monitors.length; i++) {
|
||||||
|
if (i == this.primaryIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let monitor = this.monitors[i];
|
||||||
|
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
|
||||||
|
let cornerY = monitor.y;
|
||||||
|
|
||||||
|
let haveTopLeftCorner = true;
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
|
||||||
|
let besideY = cornerY;
|
||||||
|
let aboveX = cornerX;
|
||||||
|
let aboveY = cornerY - 1;
|
||||||
|
|
||||||
|
for (let j = 0; j < this.monitors.length; j++) {
|
||||||
|
if (i == j)
|
||||||
|
continue;
|
||||||
|
let otherMonitor = this.monitors[j];
|
||||||
|
if (besideX >= otherMonitor.x &&
|
||||||
|
besideX < otherMonitor.x + otherMonitor.width &&
|
||||||
|
besideY >= otherMonitor.y &&
|
||||||
|
besideY < otherMonitor.y + otherMonitor.height) {
|
||||||
|
haveTopLeftCorner = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (aboveX >= otherMonitor.x &&
|
||||||
|
aboveX < otherMonitor.x + otherMonitor.width &&
|
||||||
|
aboveY >= otherMonitor.y &&
|
||||||
|
aboveY < otherMonitor.y + otherMonitor.height) {
|
||||||
|
haveTopLeftCorner = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!haveTopLeftCorner)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let corner = new HotCorner();
|
||||||
|
this._hotCorners.push(corner);
|
||||||
|
corner.actor.set_position(cornerX, cornerY);
|
||||||
|
this._chrome.addActor(corner.actor);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBoxes: function() {
|
||||||
|
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
|
||||||
|
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||||
|
|
||||||
|
this._bottomBox.set_position(this.bottomMonitor.x,
|
||||||
|
this.bottomMonitor.y + this.bottomMonitor.height);
|
||||||
|
this._bottomBox.set_size(this.bottomMonitor.width, -1);
|
||||||
|
|
||||||
|
this.trayBox.width = this.bottomMonitor.width;
|
||||||
|
|
||||||
|
// Set trayBox's clip to show things above it, but not below
|
||||||
|
// it (so it's not visible behind the keyboard). The exact
|
||||||
|
// height of the clip doesn't matter, as long as it's taller
|
||||||
|
// than any Notification.actor.
|
||||||
|
this.trayBox.set_clip(0, -this.bottomMonitor.height,
|
||||||
|
this.bottomMonitor.width, this.bottomMonitor.height);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePanelBarriers: function() {
|
||||||
|
if (this._leftPanelBarrier)
|
||||||
|
global.destroy_pointer_barrier(this._leftPanelBarrier);
|
||||||
|
if (this._rightPanelBarrier)
|
||||||
|
global.destroy_pointer_barrier(this._rightPanelBarrier);
|
||||||
|
|
||||||
|
if (this.panelBox.height) {
|
||||||
|
let primary = this.primaryMonitor;
|
||||||
|
this._leftPanelBarrier =
|
||||||
|
global.create_pointer_barrier(primary.x, primary.y,
|
||||||
|
primary.x, primary.y + this.panelBox.height,
|
||||||
|
1 /* BarrierPositiveX */);
|
||||||
|
this._rightPanelBarrier =
|
||||||
|
global.create_pointer_barrier(primary.x + primary.width, primary.y,
|
||||||
|
primary.x + primary.width, primary.y + this.panelBox.height,
|
||||||
|
4 /* BarrierNegativeX */);
|
||||||
|
} else {
|
||||||
|
this._leftPanelBarrier = 0;
|
||||||
|
this._rightPanelBarrier = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateTrayBarrier: function() {
|
||||||
|
let monitor = this.bottomMonitor;
|
||||||
|
|
||||||
|
if (this._trayBarrier)
|
||||||
|
global.destroy_pointer_barrier(this._trayBarrier);
|
||||||
|
|
||||||
|
if (Main.messageTray) {
|
||||||
|
this._trayBarrier =
|
||||||
|
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
|
||||||
|
monitor.x + monitor.width, monitor.y + monitor.height,
|
||||||
|
4 /* BarrierNegativeX */);
|
||||||
|
} else {
|
||||||
|
this._trayBarrier = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_monitorsChanged: function() {
|
||||||
|
this._updateMonitors();
|
||||||
|
this._updateBoxes();
|
||||||
|
this._updateHotCorners();
|
||||||
|
|
||||||
|
this.emit('monitors-changed');
|
||||||
|
},
|
||||||
|
|
||||||
|
_isAboveOrBelowPrimary: function(monitor) {
|
||||||
|
let primary = this.monitors[this.primaryIndex];
|
||||||
|
let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width;
|
||||||
|
let primaryLeft = primary.x, primaryRight = primary.x + primary.width;
|
||||||
|
|
||||||
|
if ((monitorLeft >= primaryLeft && monitorLeft <= primaryRight) ||
|
||||||
|
(monitorRight >= primaryLeft && monitorRight <= primaryRight) ||
|
||||||
|
(primaryLeft >= monitorLeft && primaryLeft <= monitorRight) ||
|
||||||
|
(primaryRight >= monitorLeft && primaryRight <= monitorRight))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get focusIndex() {
|
||||||
|
let focusWindow = global.display.focus_window;
|
||||||
|
|
||||||
|
if (focusWindow) {
|
||||||
|
let wrect = focusWindow.get_outer_rect();
|
||||||
|
for (let i = 0; i < this.monitors.length; i++) {
|
||||||
|
let monitor = this.monitors[i];
|
||||||
|
|
||||||
|
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
|
||||||
|
monitor.x + monitor.width > wrect.x &&
|
||||||
|
monitor.y + monitor.height > wrect.y)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.primaryIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
get focusMonitor() {
|
||||||
|
return this.monitors[this.focusIndex];
|
||||||
|
},
|
||||||
|
|
||||||
|
_startupAnimation: function() {
|
||||||
|
this.panelBox.anchor_y = this.panelBox.height;
|
||||||
|
Tweener.addTween(this.panelBox,
|
||||||
|
{ anchor_y: 0,
|
||||||
|
time: STARTUP_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showKeyboard: function () {
|
||||||
|
Main.messageTray.hide();
|
||||||
|
Tweener.addTween(this._bottomBox,
|
||||||
|
{ anchor_y: this._bottomBox.height,
|
||||||
|
time: KEYBOARD_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: this._showKeyboardComplete,
|
||||||
|
onCompleteScope: this
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showKeyboardComplete: function() {
|
||||||
|
// Poke Chrome to update the input shape; it doesn't notice
|
||||||
|
// anchor point changes
|
||||||
|
this._chrome.updateRegions();
|
||||||
|
|
||||||
|
this._bottomBox.connect('notify::height', Lang.bind(this, function () {
|
||||||
|
this._bottomBoxAnchor = this._bottomBox.height;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
hideKeyboard: function (immediate) {
|
||||||
|
Main.messageTray.hide();
|
||||||
|
Tweener.addTween(this._bottomBox,
|
||||||
|
{ anchor_y: 0,
|
||||||
|
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: this._showKeyboardComplete,
|
||||||
|
onCompleteScope: this
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideKeyboardComplete: function() {
|
||||||
|
this._chrome.updateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
// addChrome:
|
||||||
|
// @actor: an actor to add to the chrome layer
|
||||||
|
// @params: (optional) additional params
|
||||||
|
//
|
||||||
|
// Adds @actor to the chrome layer and (unless %affectsInputRegion
|
||||||
|
// in @params is %false) extends the input region to include it.
|
||||||
|
// Changes in @actor's size, position, and visibility will
|
||||||
|
// automatically result in appropriate changes to the input
|
||||||
|
// region.
|
||||||
|
//
|
||||||
|
// If %affectsStruts in @params is %true (and @actor is along a
|
||||||
|
// screen edge), then @actor's size and position will also affect
|
||||||
|
// the window manager struts. Changes to @actor's visibility will
|
||||||
|
// NOT affect whether or not the strut is present, however.
|
||||||
|
//
|
||||||
|
// If %visibleInFullscreen in @params is %true, the actor will be
|
||||||
|
// visible even when a fullscreen window should be covering it.
|
||||||
|
addChrome: function(actor, params) {
|
||||||
|
this._chrome.addActor(actor, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
// trackChrome:
|
||||||
|
// @actor: a descendant of the chrome to begin tracking
|
||||||
|
// @params: parameters describing how to track @actor
|
||||||
|
//
|
||||||
|
// Tells the chrome to track @actor, which must be a descendant
|
||||||
|
// of an actor added via addChrome(). This can be used to extend the
|
||||||
|
// struts or input region to cover specific children.
|
||||||
|
//
|
||||||
|
// @params can have any of the same values as in addChrome(),
|
||||||
|
// though some possibilities don't make sense (eg, trying to have
|
||||||
|
// a %visibleInFullscreen child of a non-%visibleInFullscreen
|
||||||
|
// parent). By default, @actor has the same params as its chrome
|
||||||
|
// ancestor.
|
||||||
|
trackChrome: function(actor, params) {
|
||||||
|
this._chrome.trackActor(actor, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
// untrackChrome:
|
||||||
|
// @actor: an actor previously tracked via trackChrome()
|
||||||
|
//
|
||||||
|
// Undoes the effect of trackChrome()
|
||||||
|
untrackChrome: function(actor) {
|
||||||
|
this._chrome.untrackActor(actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
// removeChrome:
|
||||||
|
// @actor: a child of the chrome layer
|
||||||
|
//
|
||||||
|
// Removes @actor from the chrome layer
|
||||||
|
removeChrome: function(actor) {
|
||||||
|
this._chrome.removeActor(actor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(LayoutManager.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
// HotCorner:
|
||||||
|
//
|
||||||
|
// This class manages a "hot corner" that can toggle switching to
|
||||||
|
// overview.
|
||||||
|
function HotCorner() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
HotCorner.prototype = {
|
||||||
|
_init : function() {
|
||||||
|
// We use this flag to mark the case where the user has entered the
|
||||||
|
// hot corner and has not left both the hot corner and a surrounding
|
||||||
|
// guard area (the "environs"). This avoids triggering the hot corner
|
||||||
|
// multiple times due to an accidental jitter.
|
||||||
|
this._entered = false;
|
||||||
|
|
||||||
|
this.actor = new Clutter.Group({ name: 'hot-corner-environs',
|
||||||
|
width: 3,
|
||||||
|
height: 3,
|
||||||
|
reactive: true });
|
||||||
|
|
||||||
|
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
opacity: 0,
|
||||||
|
reactive: true });
|
||||||
|
this._corner._delegate = this;
|
||||||
|
|
||||||
|
this.actor.add_actor(this._corner);
|
||||||
|
|
||||||
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||||
|
this._corner.set_position(this.actor.width - this._corner.width, 0);
|
||||||
|
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
|
} else {
|
||||||
|
this._corner.set_position(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._activationTime = 0;
|
||||||
|
|
||||||
|
this.actor.connect('leave-event',
|
||||||
|
Lang.bind(this, this._onEnvironsLeft));
|
||||||
|
|
||||||
|
// Clicking on the hot corner environs should result in the
|
||||||
|
// same behavior as clicking on the hot corner.
|
||||||
|
this.actor.connect('button-release-event',
|
||||||
|
Lang.bind(this, this._onCornerClicked));
|
||||||
|
|
||||||
|
// In addition to being triggered by the mouse enter event,
|
||||||
|
// the hot corner can be triggered by clicking on it. This is
|
||||||
|
// useful if the user wants to undo the effect of triggering
|
||||||
|
// the hot corner once in the hot corner.
|
||||||
|
this._corner.connect('enter-event',
|
||||||
|
Lang.bind(this, this._onCornerEntered));
|
||||||
|
this._corner.connect('button-release-event',
|
||||||
|
Lang.bind(this, this._onCornerClicked));
|
||||||
|
this._corner.connect('leave-event',
|
||||||
|
Lang.bind(this, this._onCornerLeft));
|
||||||
|
|
||||||
|
// Cache the three ripples instead of dynamically creating and destroying them.
|
||||||
|
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||||
|
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||||
|
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||||
|
|
||||||
|
Main.uiGroup.add_actor(this._ripple1);
|
||||||
|
Main.uiGroup.add_actor(this._ripple2);
|
||||||
|
Main.uiGroup.add_actor(this._ripple3);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.actor.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
|
||||||
|
// We draw a ripple by using a source image and animating it scaling
|
||||||
|
// outwards and fading away. We want the ripples to move linearly
|
||||||
|
// or it looks unrealistic, but if the opacity of the ripple goes
|
||||||
|
// linearly to zero it fades away too quickly, so we use Tweener's
|
||||||
|
// 'onUpdate' to give a non-linear curve to the fade-away and make
|
||||||
|
// it more visible in the middle section.
|
||||||
|
|
||||||
|
ripple._opacity = startOpacity;
|
||||||
|
|
||||||
|
if (ripple.get_direction() == St.TextDirection.RTL)
|
||||||
|
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
|
|
||||||
|
ripple.visible = true;
|
||||||
|
ripple.opacity = 255 * Math.sqrt(startOpacity);
|
||||||
|
ripple.scale_x = ripple.scale_y = startScale;
|
||||||
|
|
||||||
|
let [x, y] = this._corner.get_transformed_position();
|
||||||
|
ripple.x = x;
|
||||||
|
ripple.y = y;
|
||||||
|
|
||||||
|
Tweener.addTween(ripple, { _opacity: 0,
|
||||||
|
scale_x: finalScale,
|
||||||
|
scale_y: finalScale,
|
||||||
|
delay: delay,
|
||||||
|
time: time,
|
||||||
|
transition: 'linear',
|
||||||
|
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
|
||||||
|
onComplete: function() { ripple.visible = false; } });
|
||||||
|
},
|
||||||
|
|
||||||
|
rippleAnimation: function() {
|
||||||
|
// Show three concentric ripples expanding outwards; the exact
|
||||||
|
// parameters were found by trial and error, so don't look
|
||||||
|
// for them to make perfect sense mathematically
|
||||||
|
|
||||||
|
// delay time scale opacity => scale
|
||||||
|
this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5);
|
||||||
|
this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25);
|
||||||
|
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
|
if (source != Main.xdndHandler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||||
|
this.rippleAnimation();
|
||||||
|
Main.overview.showTemporarily();
|
||||||
|
Main.overview.beginItemDrag(actor);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCornerEntered : function() {
|
||||||
|
if (!this._entered) {
|
||||||
|
this._entered = true;
|
||||||
|
if (!Main.overview.animationInProgress) {
|
||||||
|
this._activationTime = Date.now() / 1000;
|
||||||
|
|
||||||
|
this.rippleAnimation();
|
||||||
|
Main.overview.toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCornerClicked : function() {
|
||||||
|
if (this.shouldToggleOverviewOnClick())
|
||||||
|
Main.overview.toggle();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCornerLeft : function(actor, event) {
|
||||||
|
if (event.get_related() != this.actor)
|
||||||
|
this._entered = false;
|
||||||
|
// Consume event, otherwise this will confuse onEnvironsLeft
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEnvironsLeft : function(actor, event) {
|
||||||
|
if (event.get_related() != this._corner)
|
||||||
|
this._entered = false;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Checks if the Activities button is currently sensitive to
|
||||||
|
// clicks. The first call to this function within the
|
||||||
|
// HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
|
||||||
|
// triggered will return false. This avoids opening and closing
|
||||||
|
// the overview if the user both triggered the hot corner and
|
||||||
|
// clicked the Activities button.
|
||||||
|
shouldToggleOverviewOnClick: function() {
|
||||||
|
if (Main.overview.animationInProgress)
|
||||||
|
return false;
|
||||||
|
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// This manages the shell "chrome"; the UI that's visible in the
|
||||||
|
// normal mode (ie, outside the Overview), that surrounds the main
|
||||||
|
// workspace content.
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
visibleInFullscreen: false,
|
||||||
|
affectsStruts: false,
|
||||||
|
affectsInputRegion: true
|
||||||
|
};
|
||||||
|
|
||||||
|
function Chrome() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Chrome.prototype = {
|
||||||
|
_init: function(layoutManager) {
|
||||||
|
this._layoutManager = layoutManager;
|
||||||
|
|
||||||
|
// The group itself has zero size so it doesn't interfere with DND
|
||||||
|
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
|
||||||
|
Main.uiGroup.add_actor(this.actor);
|
||||||
|
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
||||||
|
|
||||||
|
this._monitors = [];
|
||||||
|
this._inOverview = false;
|
||||||
|
|
||||||
|
this._trackedActors = [];
|
||||||
|
|
||||||
|
this._layoutManager.connect('monitors-changed',
|
||||||
|
Lang.bind(this, this._relayout));
|
||||||
|
global.screen.connect('restacked',
|
||||||
|
Lang.bind(this, this._windowsRestacked));
|
||||||
|
|
||||||
|
// Need to update struts on new workspaces when they are added
|
||||||
|
global.screen.connect('notify::n-workspaces',
|
||||||
|
Lang.bind(this, this._queueUpdateRegions));
|
||||||
|
|
||||||
|
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||||
|
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
|
||||||
|
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
|
||||||
|
function(result, err) {
|
||||||
|
if (!err)
|
||||||
|
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._relayout();
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
Main.overview.connect('showing',
|
||||||
|
Lang.bind(this, this._overviewShowing));
|
||||||
|
Main.overview.connect('hidden',
|
||||||
|
Lang.bind(this, this._overviewHidden));
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocated: function(actor, box, flags) {
|
||||||
|
let children = this.actor.get_children();
|
||||||
|
for (let i = 0; i < children.length; i++)
|
||||||
|
children[i].allocate_preferred_size(flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
addActor: function(actor, params) {
|
||||||
|
this.actor.add_actor(actor);
|
||||||
|
this._trackActor(actor, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
trackActor: function(actor, params) {
|
||||||
|
let ancestor = actor.get_parent();
|
||||||
|
let index = this._findActor(ancestor);
|
||||||
|
while (ancestor && index == -1) {
|
||||||
|
ancestor = ancestor.get_parent();
|
||||||
|
index = this._findActor(ancestor);
|
||||||
|
}
|
||||||
|
if (!ancestor)
|
||||||
|
throw new Error('actor is not a descendent of the chrome layer');
|
||||||
|
|
||||||
|
let ancestorData = this._trackedActors[index];
|
||||||
|
if (!params)
|
||||||
|
params = {};
|
||||||
|
// We can't use Params.parse here because we want to drop
|
||||||
|
// the extra values like ancestorData.actor
|
||||||
|
for (let prop in defaultParams) {
|
||||||
|
if (!params[prop])
|
||||||
|
params[prop] = ancestorData[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
this._trackActor(actor, params);
|
||||||
|
},
|
||||||
|
|
||||||
|
untrackActor: function(actor) {
|
||||||
|
this._untrackActor(actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeActor: function(actor) {
|
||||||
|
this.actor.remove_actor(actor);
|
||||||
|
this._untrackActor(actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_findActor: function(actor) {
|
||||||
|
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||||
|
let actorData = this._trackedActors[i];
|
||||||
|
if (actorData.actor == actor)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_trackActor: function(actor, params) {
|
||||||
|
if (this._findActor(actor) != -1)
|
||||||
|
throw new Error('trying to re-track existing chrome actor');
|
||||||
|
|
||||||
|
let actorData = Params.parse(params, defaultParams);
|
||||||
|
actorData.actor = actor;
|
||||||
|
actorData.visibleId = actor.connect('notify::visible',
|
||||||
|
Lang.bind(this, this._queueUpdateRegions));
|
||||||
|
actorData.allocationId = actor.connect('notify::allocation',
|
||||||
|
Lang.bind(this, this._queueUpdateRegions));
|
||||||
|
actorData.parentSetId = actor.connect('parent-set',
|
||||||
|
Lang.bind(this, this._actorReparented));
|
||||||
|
// Note that destroying actor will unset its parent, so we don't
|
||||||
|
// need to connect to 'destroy' too.
|
||||||
|
|
||||||
|
this._trackedActors.push(actorData);
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_untrackActor: function(actor) {
|
||||||
|
let i = this._findActor(actor);
|
||||||
|
|
||||||
|
if (i == -1)
|
||||||
|
return;
|
||||||
|
let actorData = this._trackedActors[i];
|
||||||
|
|
||||||
|
this._trackedActors.splice(i, 1);
|
||||||
|
actor.disconnect(actorData.visibleId);
|
||||||
|
actor.disconnect(actorData.allocationId);
|
||||||
|
actor.disconnect(actorData.parentSetId);
|
||||||
|
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_actorReparented: function(actor, oldParent) {
|
||||||
|
if (!this.actor.contains(actor))
|
||||||
|
this._untrackActor(actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateVisibility: function() {
|
||||||
|
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||||
|
let actorData = this._trackedActors[i];
|
||||||
|
if (!this._inOverview && !actorData.visibleInFullscreen &&
|
||||||
|
this._findMonitorForActor(actorData.actor).inFullscreen)
|
||||||
|
this.actor.set_skip_paint(actorData.actor, true);
|
||||||
|
else
|
||||||
|
this.actor.set_skip_paint(actorData.actor, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_overviewShowing: function() {
|
||||||
|
this._inOverview = true;
|
||||||
|
this._updateVisibility();
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_overviewHidden: function() {
|
||||||
|
this._inOverview = false;
|
||||||
|
this._updateVisibility();
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_relayout: function() {
|
||||||
|
this._monitors = this._layoutManager.monitors;
|
||||||
|
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
||||||
|
|
||||||
|
this._updateFullscreen();
|
||||||
|
this._updateVisibility();
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
|
||||||
|
this.actor.visible = !screenSaverActive;
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_findMonitorForRect: function(x, y, w, h) {
|
||||||
|
// First look at what monitor the center of the rectangle is at
|
||||||
|
let cx = x + w/2;
|
||||||
|
let cy = y + h/2;
|
||||||
|
for (let i = 0; i < this._monitors.length; i++) {
|
||||||
|
let monitor = this._monitors[i];
|
||||||
|
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||||
|
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
// If the center is not on a monitor, return the first overlapping monitor
|
||||||
|
for (let i = 0; i < this._monitors.length; i++) {
|
||||||
|
let monitor = this._monitors[i];
|
||||||
|
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||||
|
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
// otherwise on no monitor
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_findMonitorForWindow: function(window) {
|
||||||
|
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||||
|
},
|
||||||
|
|
||||||
|
// This call guarantees that we return some monitor to simplify usage of it
|
||||||
|
// In practice all tracked actors should be visible on some monitor anyway
|
||||||
|
_findMonitorForActor: function(actor) {
|
||||||
|
let [x, y] = actor.get_transformed_position();
|
||||||
|
let [w, h] = actor.get_transformed_size();
|
||||||
|
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||||
|
if (monitor)
|
||||||
|
return monitor;
|
||||||
|
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueUpdateRegions: function() {
|
||||||
|
if (!this._updateRegionIdle)
|
||||||
|
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
|
||||||
|
Meta.PRIORITY_BEFORE_REDRAW);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateFullscreen: function() {
|
||||||
|
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||||
|
|
||||||
|
// Reset all monitors to not fullscreen
|
||||||
|
for (let i = 0; i < this._monitors.length; i++)
|
||||||
|
this._monitors[i].inFullscreen = false;
|
||||||
|
|
||||||
|
// The chrome layer should be visible unless there is a window
|
||||||
|
// with layer FULLSCREEN, or a window with layer
|
||||||
|
// OVERRIDE_REDIRECT that covers the whole screen.
|
||||||
|
// ('override_redirect' is not actually a layer above all
|
||||||
|
// other windows, but this seems to be how mutter treats it
|
||||||
|
// currently...) If we wanted to be extra clever, we could
|
||||||
|
// figure out when an OVERRIDE_REDIRECT window was trying to
|
||||||
|
// partially overlap us, and then adjust the input region and
|
||||||
|
// our clip region accordingly...
|
||||||
|
|
||||||
|
// @windows is sorted bottom to top.
|
||||||
|
|
||||||
|
for (let i = windows.length - 1; i > -1; i--) {
|
||||||
|
let window = windows[i];
|
||||||
|
let layer = window.get_meta_window().get_layer();
|
||||||
|
|
||||||
|
// Skip minimized windows
|
||||||
|
if (!window.showing_on_its_workspace())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||||
|
let monitor = this._findMonitorForWindow(window);
|
||||||
|
if (monitor)
|
||||||
|
monitor.inFullscreen = true;
|
||||||
|
}
|
||||||
|
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||||
|
let monitor = this._findMonitorForWindow(window);
|
||||||
|
if (monitor &&
|
||||||
|
window.x <= monitor.x &&
|
||||||
|
window.x + window.width >= monitor.x + monitor.width &&
|
||||||
|
window.y <= monitor.y &&
|
||||||
|
window.y + window.height >= monitor.y + monitor.height)
|
||||||
|
monitor.inFullscreen = true;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowsRestacked: function() {
|
||||||
|
let wasInFullscreen = [];
|
||||||
|
for (let i = 0; i < this._monitors.length; i++)
|
||||||
|
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||||
|
|
||||||
|
this._updateFullscreen();
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||||
|
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
this._updateVisibility();
|
||||||
|
this._queueUpdateRegions();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRegions: function() {
|
||||||
|
let rects = [], struts = [], i;
|
||||||
|
|
||||||
|
if (this._updateRegionIdle) {
|
||||||
|
Mainloop.source_remove(this._updateRegionIdle);
|
||||||
|
delete this._updateRegionIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < this._trackedActors.length; i++) {
|
||||||
|
let actorData = this._trackedActors[i];
|
||||||
|
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let [x, y] = actorData.actor.get_transformed_position();
|
||||||
|
let [w, h] = actorData.actor.get_transformed_size();
|
||||||
|
x = Math.round(x);
|
||||||
|
y = Math.round(y);
|
||||||
|
w = Math.round(w);
|
||||||
|
h = Math.round(h);
|
||||||
|
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
||||||
|
|
||||||
|
if (actorData.affectsInputRegion &&
|
||||||
|
actorData.actor.get_paint_visibility() &&
|
||||||
|
!this.actor.get_skip_paint(actorData.actor))
|
||||||
|
rects.push(rect);
|
||||||
|
|
||||||
|
if (!actorData.affectsStruts)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Limit struts to the size of the screen
|
||||||
|
let x1 = Math.max(x, 0);
|
||||||
|
let x2 = Math.min(x + w, global.screen_width);
|
||||||
|
let y1 = Math.max(y, 0);
|
||||||
|
let y2 = Math.min(y + h, global.screen_height);
|
||||||
|
|
||||||
|
// NetWM struts are not really powerful enought to handle
|
||||||
|
// a multi-monitor scenario, they only describe what happens
|
||||||
|
// around the outer sides of the full display region. However
|
||||||
|
// it can describe a partial region along each side, so
|
||||||
|
// we can support having the struts only affect the
|
||||||
|
// primary monitor. This should be enough as we only have
|
||||||
|
// chrome affecting the struts on the primary monitor so
|
||||||
|
// far.
|
||||||
|
//
|
||||||
|
// Metacity wants to know what side of the screen the
|
||||||
|
// strut is considered to be attached to. If the actor is
|
||||||
|
// only touching one edge, or is touching the entire
|
||||||
|
// border of the primary monitor, then it's obvious which
|
||||||
|
// side to call it. If it's in a corner, we pick a side
|
||||||
|
// arbitrarily. If it doesn't touch any edges, or it spans
|
||||||
|
// the width/height across the middle of the screen, then
|
||||||
|
// we don't create a strut for it at all.
|
||||||
|
let side;
|
||||||
|
let primary = this._primaryMonitor;
|
||||||
|
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
||||||
|
if (y1 <= primary.y)
|
||||||
|
side = Meta.Side.TOP;
|
||||||
|
else if (y2 >= primary.y + primary.height)
|
||||||
|
side = Meta.Side.BOTTOM;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||||
|
if (x1 <= 0)
|
||||||
|
side = Meta.Side.LEFT;
|
||||||
|
else if (x2 >= global.screen_width)
|
||||||
|
side = Meta.Side.RIGHT;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
} else if (x1 <= 0)
|
||||||
|
side = Meta.Side.LEFT;
|
||||||
|
else if (y1 <= 0)
|
||||||
|
side = Meta.Side.TOP;
|
||||||
|
else if (x2 >= global.screen_width)
|
||||||
|
side = Meta.Side.RIGHT;
|
||||||
|
else if (y2 >= global.screen_height)
|
||||||
|
side = Meta.Side.BOTTOM;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Ensure that the strut rects goes all the way to the screen edge,
|
||||||
|
// as this really what mutter expects.
|
||||||
|
switch (side) {
|
||||||
|
case Meta.Side.TOP:
|
||||||
|
y1 = 0;
|
||||||
|
break;
|
||||||
|
case Meta.Side.BOTTOM:
|
||||||
|
y2 = global.screen_height;
|
||||||
|
break;
|
||||||
|
case Meta.Side.LEFT:
|
||||||
|
x1 = 0;
|
||||||
|
break;
|
||||||
|
case Meta.Side.RIGHT:
|
||||||
|
x2 = global.screen_width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||||
|
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||||
|
struts.push(strut);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.set_stage_input_region(rects);
|
||||||
|
|
||||||
|
let screen = global.screen;
|
||||||
|
for (let w = 0; w < screen.n_workspaces; w++) {
|
||||||
|
let workspace = screen.get_workspace_by_index(w);
|
||||||
|
workspace.set_builtin_struts(struts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
@ -5,14 +5,13 @@ const Cogl = imports.gi.Cogl;
|
|||||||
const GConf = imports.gi.GConf;
|
const GConf = imports.gi.GConf;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const History = imports.misc.history;
|
const History = imports.misc.history;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
@ -100,12 +99,19 @@ Notebook.prototype = {
|
|||||||
selectIndex: function(index) {
|
selectIndex: function(index) {
|
||||||
if (index == this._selectedIndex)
|
if (index == this._selectedIndex)
|
||||||
return;
|
return;
|
||||||
this._unselect();
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
|
this._unselect();
|
||||||
this.emit('selection', null);
|
this.emit('selection', null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Focus the new tab before unmapping the old one
|
||||||
let tabData = this._tabs[index];
|
let tabData = this._tabs[index];
|
||||||
|
if (!tabData.scrollView.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||||
|
this.actor.grab_key_focus();
|
||||||
|
|
||||||
|
this._unselect();
|
||||||
|
|
||||||
tabData.labelBox.add_style_pseudo_class('selected');
|
tabData.labelBox.add_style_pseudo_class('selected');
|
||||||
tabData.scrollView.show();
|
tabData.scrollView.show();
|
||||||
this._selectedIndex = index;
|
this._selectedIndex = index;
|
||||||
@ -216,10 +222,9 @@ function WindowList() {
|
|||||||
WindowList.prototype = {
|
WindowList.prototype = {
|
||||||
_init : function () {
|
_init : function () {
|
||||||
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
||||||
let display = global.screen.get_display();
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
||||||
display.connect('window-created', Lang.bind(this, this._updateWindowList));
|
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
|
||||||
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
|
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -242,7 +247,7 @@ WindowList.prototype = {
|
|||||||
box.add(propsBox);
|
box.add(propsBox);
|
||||||
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
|
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
|
||||||
let app = tracker.get_window_app(metaWindow);
|
let app = tracker.get_window_app(metaWindow);
|
||||||
if (app != null && !app.is_transient()) {
|
if (app != null && !app.is_window_backed()) {
|
||||||
let icon = app.create_icon_texture(22);
|
let icon = app.create_icon_texture(22);
|
||||||
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
||||||
propsBox.add(propBox);
|
propsBox.add(propBox);
|
||||||
@ -430,7 +435,7 @@ Inspector.prototype = {
|
|||||||
if (!this._eventHandler)
|
if (!this._eventHandler)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
let [minWidth, minHeight, natWidth, natHeight] =
|
let [minWidth, minHeight, natWidth, natHeight] =
|
||||||
this._eventHandler.get_preferred_size();
|
this._eventHandler.get_preferred_size();
|
||||||
@ -576,6 +581,53 @@ ErrorLog.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function Memory() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ vertical: true });
|
||||||
|
this._glibc_uordblks = new St.Label();
|
||||||
|
this.actor.add(this._glibc_uordblks);
|
||||||
|
|
||||||
|
this._js_bytes = new St.Label();
|
||||||
|
this.actor.add(this._js_bytes);
|
||||||
|
|
||||||
|
this._gjs_boxed = new St.Label();
|
||||||
|
this.actor.add(this._gjs_boxed);
|
||||||
|
|
||||||
|
this._gjs_gobject = new St.Label();
|
||||||
|
this.actor.add(this._gjs_gobject);
|
||||||
|
|
||||||
|
this._gjs_function = new St.Label();
|
||||||
|
this.actor.add(this._gjs_function);
|
||||||
|
|
||||||
|
this._gjs_closure = new St.Label();
|
||||||
|
this.actor.add(this._gjs_closure);
|
||||||
|
|
||||||
|
this._gcbutton = new St.Button({ label: 'Full GC',
|
||||||
|
style_class: 'lg-obj-inspector-button' });
|
||||||
|
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
|
||||||
|
this.actor.add(this._gcbutton, { x_align: St.Align.START,
|
||||||
|
x_fill: false });
|
||||||
|
|
||||||
|
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderText: function() {
|
||||||
|
if (!this.actor.mapped)
|
||||||
|
return;
|
||||||
|
let memInfo = global.get_memory_info();
|
||||||
|
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
|
||||||
|
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
|
||||||
|
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
|
||||||
|
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
|
||||||
|
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
|
||||||
|
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function Extensions() {
|
function Extensions() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
@ -586,23 +638,32 @@ Extensions.prototype = {
|
|||||||
name: 'lookingGlassExtensions' });
|
name: 'lookingGlassExtensions' });
|
||||||
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||||
text: _("No extensions installed") });
|
text: _("No extensions installed") });
|
||||||
|
this._numExtensions = 0;
|
||||||
this._extensionsList = new St.BoxLayout({ vertical: true,
|
this._extensionsList = new St.BoxLayout({ vertical: true,
|
||||||
style_class: 'lg-extensions-list' });
|
style_class: 'lg-extensions-list' });
|
||||||
|
this._extensionsList.add(this._noExtensions);
|
||||||
this.actor.add(this._extensionsList);
|
this.actor.add(this._extensionsList);
|
||||||
this._loadExtensionList();
|
|
||||||
|
for (let uuid in ExtensionSystem.extensionMeta)
|
||||||
|
this._loadExtension(null, uuid);
|
||||||
|
|
||||||
|
ExtensionSystem.connect('extension-loaded',
|
||||||
|
Lang.bind(this, this._loadExtension));
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadExtensionList: function() {
|
_loadExtension: function(o, uuid) {
|
||||||
let extensions = ExtensionSystem.extensionMeta;
|
let extension = ExtensionSystem.extensionMeta[uuid];
|
||||||
let totalExtensions = 0;
|
// There can be cases where we create dummy extension metadata
|
||||||
for (let uuid in extensions) {
|
// that's not really a proper extension. Don't bother with these.
|
||||||
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
|
if (!extension.name)
|
||||||
this._extensionsList.add(extensionDisplay);
|
return;
|
||||||
totalExtensions++;
|
|
||||||
}
|
let extensionDisplay = this._createExtensionDisplay(extension);
|
||||||
if (totalExtensions == 0) {
|
if (this._numExtensions == 0)
|
||||||
this._extensionsList.add(this._noExtensions);
|
this._extensionsList.remove_actor(this._noExtensions);
|
||||||
}
|
|
||||||
|
this._numExtensions ++;
|
||||||
|
this._extensionsList.add(extensionDisplay);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onViewSource: function (actor) {
|
_onViewSource: function (actor) {
|
||||||
@ -629,6 +690,8 @@ Extensions.prototype = {
|
|||||||
return _("Error");
|
return _("Error");
|
||||||
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
|
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
|
||||||
return _("Out of date");
|
return _("Out of date");
|
||||||
|
case ExtensionSystem.ExtensionState.DOWNLOADING:
|
||||||
|
return _("Downloading");
|
||||||
}
|
}
|
||||||
return 'Unknown'; // Not translated, shouldn't appear
|
return 'Unknown'; // Not translated, shouldn't appear
|
||||||
},
|
},
|
||||||
@ -639,7 +702,7 @@ Extensions.prototype = {
|
|||||||
text: meta.name });
|
text: meta.name });
|
||||||
box.add(name, { expand: true });
|
box.add(name, { expand: true });
|
||||||
let description = new St.Label({ style_class: 'lg-extension-description',
|
let description = new St.Label({ style_class: 'lg-extension-description',
|
||||||
text: meta.description });
|
text: meta.description || 'No description' });
|
||||||
box.add(description, { expand: true });
|
box.add(description, { expand: true });
|
||||||
|
|
||||||
let metaBox = new St.BoxLayout();
|
let metaBox = new St.BoxLayout();
|
||||||
@ -698,7 +761,9 @@ LookingGlass.prototype = {
|
|||||||
Lang.bind(this, this._updateFont));
|
Lang.bind(this, this._updateFont));
|
||||||
this._updateFont();
|
this._updateFont();
|
||||||
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
// we add it to the chrome because we want it to appear to slide
|
||||||
|
// out from underneath the panel
|
||||||
|
Main.layoutManager.addChrome(this.actor);
|
||||||
|
|
||||||
this._objInspector = new ObjInspector();
|
this._objInspector = new ObjInspector();
|
||||||
Main.uiGroup.add_actor(this._objInspector.actor);
|
Main.uiGroup.add_actor(this._objInspector.actor);
|
||||||
@ -707,6 +772,7 @@ LookingGlass.prototype = {
|
|||||||
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
||||||
this.actor.add_actor(toolbar);
|
this.actor.add_actor(toolbar);
|
||||||
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
|
||||||
|
icon_type: St.IconType.FULLCOLOR,
|
||||||
icon_size: 24 });
|
icon_size: 24 });
|
||||||
toolbar.add_actor(inspectIcon);
|
toolbar.add_actor(inspectIcon);
|
||||||
inspectIcon.reactive = true;
|
inspectIcon.reactive = true;
|
||||||
@ -744,12 +810,7 @@ LookingGlass.prototype = {
|
|||||||
let label = new St.Label({ text: 'js>>> ' });
|
let label = new St.Label({ text: 'js>>> ' });
|
||||||
entryArea.add(label);
|
entryArea.add(label);
|
||||||
|
|
||||||
this._entry = new St.Entry();
|
this._entry = new St.Entry({ can_focus: true });
|
||||||
/* unmapping the edit box will un-focus it, undo that */
|
|
||||||
notebook.connect('selection', Lang.bind(this, function (nb, child) {
|
|
||||||
if (child == this._evalBox)
|
|
||||||
global.stage.set_key_focus(this._entry);
|
|
||||||
}));
|
|
||||||
entryArea.add(this._entry, { expand: true });
|
entryArea.add(this._entry, { expand: true });
|
||||||
|
|
||||||
this._windowList = new WindowList();
|
this._windowList = new WindowList();
|
||||||
@ -762,6 +823,9 @@ LookingGlass.prototype = {
|
|||||||
this._errorLog = new ErrorLog();
|
this._errorLog = new ErrorLog();
|
||||||
notebook.appendPage('Errors', this._errorLog.actor);
|
notebook.appendPage('Errors', this._errorLog.actor);
|
||||||
|
|
||||||
|
this._memory = new Memory();
|
||||||
|
notebook.appendPage('Memory', this._memory.actor);
|
||||||
|
|
||||||
this._extensions = new Extensions();
|
this._extensions = new Extensions();
|
||||||
notebook.appendPage('Extensions', this._extensions.actor);
|
notebook.appendPage('Extensions', this._extensions.actor);
|
||||||
|
|
||||||
@ -855,7 +919,7 @@ LookingGlass.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_resizeTo: function(actor) {
|
_resizeTo: function(actor) {
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
let myWidth = primary.width * 0.7;
|
let myWidth = primary.width * 0.7;
|
||||||
let myHeight = primary.height * 0.7;
|
let myHeight = primary.height * 0.7;
|
||||||
let [srcX, srcY] = actor.get_transformed_position();
|
let [srcX, srcY] = actor.get_transformed_position();
|
||||||
@ -908,8 +972,9 @@ LookingGlass.prototype = {
|
|||||||
if (!Main.pushModal(this._entry))
|
if (!Main.pushModal(this._entry))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this._notebook.selectIndex(0);
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
this.actor.lower(Main.chrome.actor);
|
this.actor.lower_bottom();
|
||||||
this._open = true;
|
this._open = true;
|
||||||
this._history.lastItem();
|
this._history.lastItem();
|
||||||
|
|
||||||
@ -940,6 +1005,7 @@ LookingGlass.prototype = {
|
|||||||
|
|
||||||
Main.popModal(this._entry);
|
Main.popModal(this._entry);
|
||||||
|
|
||||||
|
this.actor.lower_bottom();
|
||||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
y: this._hiddenY,
|
y: this._hiddenY,
|
||||||
|
323
js/ui/main.js
@ -1,11 +1,5 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
imports.gi.versions.Clutter = '1.0';
|
|
||||||
imports.gi.versions.Gio = '2.0';
|
|
||||||
imports.gi.versions.Gdk = '3.0';
|
|
||||||
imports.gi.versions.GdkPixbuf = '2.0';
|
|
||||||
imports.gi.versions.Gtk = '3.0';
|
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const DBus = imports.dbus;
|
const DBus = imports.dbus;
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
@ -17,21 +11,23 @@ const Mainloop = imports.mainloop;
|
|||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Chrome = imports.ui.chrome;
|
const AutomountManager = imports.ui.automountManager;
|
||||||
|
const AutorunManager = imports.ui.autorunManager;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||||
const Environment = imports.ui.environment;
|
const Environment = imports.ui.environment;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
|
const Keyboard = imports.ui.keyboard;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const PlaceDisplay = imports.ui.placeDisplay;
|
const PlaceDisplay = imports.ui.placeDisplay;
|
||||||
const RunDialog = imports.ui.runDialog;
|
const RunDialog = imports.ui.runDialog;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const LookingGlass = imports.ui.lookingGlass;
|
const LookingGlass = imports.ui.lookingGlass;
|
||||||
|
const NetworkAgent = imports.ui.networkAgent;
|
||||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||||
const Scripting = imports.ui.scripting;
|
const Scripting = imports.ui.scripting;
|
||||||
@ -46,7 +42,8 @@ const Util = imports.misc.util;
|
|||||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||||
|
|
||||||
let chrome = null;
|
let automountManager = null;
|
||||||
|
let autorunManager = null;
|
||||||
let panel = null;
|
let panel = null;
|
||||||
let hotCorners = [];
|
let hotCorners = [];
|
||||||
let placesManager = null;
|
let placesManager = null;
|
||||||
@ -67,20 +64,96 @@ let uiGroup = null;
|
|||||||
let magnifier = null;
|
let magnifier = null;
|
||||||
let xdndHandler = null;
|
let xdndHandler = null;
|
||||||
let statusIconDispatcher = null;
|
let statusIconDispatcher = null;
|
||||||
|
let keyboard = null;
|
||||||
|
let layoutManager = null;
|
||||||
|
let networkAgent = null;
|
||||||
let _errorLogStack = [];
|
let _errorLogStack = [];
|
||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
|
let _gdmCssStylesheet = null;
|
||||||
|
|
||||||
let background = null;
|
let background = null;
|
||||||
|
|
||||||
function start() {
|
function _createUserSession() {
|
||||||
// Add a binding for 'global' in the global JS namespace; (gjs
|
// Load the calendar server. Note that we are careful about
|
||||||
// keeps the web browser convention of having that namespace be
|
// not loading any events until the user presses the clock
|
||||||
// called 'window'.)
|
global.launch_calendar_server();
|
||||||
window.global = Shell.Global.get();
|
|
||||||
|
|
||||||
// Now monkey patch utility functions into the global proxy;
|
placesManager = new PlaceDisplay.PlacesManager();
|
||||||
|
telepathyClient = new TelepathyClient.Client();
|
||||||
|
automountManager = new AutomountManager.AutomountManager();
|
||||||
|
autorunManager = new AutorunManager.AutorunManager();
|
||||||
|
networkAgent = new NetworkAgent.NetworkAgent();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createGDMSession() {
|
||||||
|
// We do this this here instead of at the top to prevent GDM
|
||||||
|
// related code from getting loaded in normal user sessions
|
||||||
|
const LoginDialog = imports.gdm.loginDialog;
|
||||||
|
|
||||||
|
let loginDialog = new LoginDialog.LoginDialog();
|
||||||
|
loginDialog.connect('loaded', function() {
|
||||||
|
loginDialog.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initRecorder() {
|
||||||
|
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||||
|
|
||||||
|
global.screen.connect('toggle-recording', function() {
|
||||||
|
if (recorder == null) {
|
||||||
|
recorder = new Shell.Recorder({ stage: global.stage });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recorder.is_recording()) {
|
||||||
|
recorder.pause();
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
} else {
|
||||||
|
// read the parameters from GSettings always in case they have changed
|
||||||
|
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||||
|
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
|
||||||
|
let pipeline = recorderSettings.get_string('pipeline');
|
||||||
|
|
||||||
|
if (!pipeline.match(/^\s*$/))
|
||||||
|
recorder.set_pipeline(pipeline);
|
||||||
|
else
|
||||||
|
recorder.set_pipeline(null);
|
||||||
|
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
|
recorder.record();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initUserSession() {
|
||||||
|
_initRecorder();
|
||||||
|
|
||||||
|
keyboard.init();
|
||||||
|
|
||||||
|
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||||
|
|
||||||
|
ExtensionSystem.init();
|
||||||
|
ExtensionSystem.loadExtensions();
|
||||||
|
|
||||||
|
let shellwm = global.window_manager;
|
||||||
|
|
||||||
|
shellwm.takeover_keybinding('panel_run_dialog');
|
||||||
|
shellwm.connect('keybinding::panel_run_dialog', function () {
|
||||||
|
getRunDialog().open();
|
||||||
|
});
|
||||||
|
|
||||||
|
shellwm.takeover_keybinding('panel_main_menu');
|
||||||
|
shellwm.connect('keybinding::panel_main_menu', function () {
|
||||||
|
overview.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
// Monkey patch utility functions into the global proxy;
|
||||||
// This is easier and faster than indirecting down into global
|
// This is easier and faster than indirecting down into global
|
||||||
// if we want to call back up into JS.
|
// if we want to call back up into JS.
|
||||||
global.logError = _logError;
|
global.logError = _logError;
|
||||||
@ -98,12 +171,6 @@ function start() {
|
|||||||
// back into sync ones.
|
// back into sync ones.
|
||||||
DBus.session.flush();
|
DBus.session.flush();
|
||||||
|
|
||||||
// Load the calendar server. Note that we are careful about
|
|
||||||
// not loading any events until the user presses the clock
|
|
||||||
global.launch_calendar_server();
|
|
||||||
|
|
||||||
Environment.init();
|
|
||||||
|
|
||||||
// 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
|
||||||
// needs to load all the .desktop files, and ShellWindowTracker
|
// needs to load all the .desktop files, and ShellWindowTracker
|
||||||
@ -122,18 +189,9 @@ function start() {
|
|||||||
global.stage.no_clear_hint = true;
|
global.stage.no_clear_hint = true;
|
||||||
|
|
||||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||||
|
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
|
||||||
loadTheme();
|
loadTheme();
|
||||||
|
|
||||||
let shellwm = global.window_manager;
|
|
||||||
shellwm.takeover_keybinding('panel_main_menu');
|
|
||||||
shellwm.connect('keybinding::panel_main_menu', function () {
|
|
||||||
overview.toggle();
|
|
||||||
});
|
|
||||||
shellwm.takeover_keybinding('panel_run_dialog');
|
|
||||||
shellwm.connect('keybinding::panel_run_dialog', function () {
|
|
||||||
getRunDialog().open();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up stage hierarchy to group all UI actors under one container.
|
// Set up stage hierarchy to group all UI actors under one container.
|
||||||
uiGroup = new Clutter.Group();
|
uiGroup = new Clutter.Group();
|
||||||
St.set_ui_root(global.stage, uiGroup);
|
St.set_ui_root(global.stage, uiGroup);
|
||||||
@ -141,51 +199,34 @@ function start() {
|
|||||||
global.overlay_group.reparent(uiGroup);
|
global.overlay_group.reparent(uiGroup);
|
||||||
global.stage.add_actor(uiGroup);
|
global.stage.add_actor(uiGroup);
|
||||||
|
|
||||||
placesManager = new PlaceDisplay.PlacesManager();
|
layoutManager = new Layout.LayoutManager();
|
||||||
xdndHandler = new XdndHandler.XdndHandler();
|
xdndHandler = new XdndHandler.XdndHandler();
|
||||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||||
overview = new Overview.Overview();
|
// This overview object is just a stub for non-user sessions
|
||||||
chrome = new Chrome.Chrome();
|
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
|
||||||
magnifier = new Magnifier.Magnifier();
|
magnifier = new Magnifier.Magnifier();
|
||||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
||||||
panel = new Panel.Panel();
|
panel = new Panel.Panel();
|
||||||
wm = new WindowManager.WindowManager();
|
wm = new WindowManager.WindowManager();
|
||||||
messageTray = new MessageTray.MessageTray();
|
messageTray = new MessageTray.MessageTray();
|
||||||
|
keyboard = new Keyboard.Keyboard();
|
||||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||||
telepathyClient = new TelepathyClient.Client();
|
|
||||||
|
|
||||||
|
if (global.session_type == Shell.SessionType.USER)
|
||||||
|
_createUserSession();
|
||||||
|
else if (global.session_type == Shell.SessionType.GDM)
|
||||||
|
_createGDMSession();
|
||||||
|
|
||||||
|
panel.startStatusArea();
|
||||||
|
|
||||||
|
keyboard.init();
|
||||||
overview.init();
|
overview.init();
|
||||||
|
|
||||||
|
if (global.session_type == Shell.SessionType.USER)
|
||||||
|
_initUserSession();
|
||||||
statusIconDispatcher.start(messageTray.actor);
|
statusIconDispatcher.start(messageTray.actor);
|
||||||
|
|
||||||
_startDate = new Date();
|
|
||||||
|
|
||||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
|
||||||
|
|
||||||
global.screen.connect('toggle-recording', function() {
|
|
||||||
if (recorder == null) {
|
|
||||||
recorder = new Shell.Recorder({ stage: global.stage });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recorder.is_recording()) {
|
|
||||||
recorder.pause();
|
|
||||||
} else {
|
|
||||||
// read the parameters from GSettings always in case they have changed
|
|
||||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
|
||||||
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
|
|
||||||
let pipeline = recorderSettings.get_string('pipeline');
|
|
||||||
|
|
||||||
if (!pipeline.match(/^\s*$/))
|
|
||||||
recorder.set_pipeline(pipeline);
|
|
||||||
else
|
|
||||||
recorder.set_pipeline(null);
|
|
||||||
|
|
||||||
recorder.record();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
|
||||||
|
|
||||||
// Provide the bus object for gnome-session to
|
// Provide the bus object for gnome-session to
|
||||||
// initiate logouts.
|
// initiate logouts.
|
||||||
EndSessionDialog.init();
|
EndSessionDialog.init();
|
||||||
@ -193,19 +234,7 @@ function start() {
|
|||||||
// Attempt to become a PolicyKit authentication agent
|
// Attempt to become a PolicyKit authentication agent
|
||||||
PolkitAuthenticationAgent.init()
|
PolkitAuthenticationAgent.init()
|
||||||
|
|
||||||
global.screen.connect('monitors-changed', _relayout);
|
_startDate = new Date();
|
||||||
|
|
||||||
ExtensionSystem.init();
|
|
||||||
ExtensionSystem.loadExtensions();
|
|
||||||
|
|
||||||
// Perform initial relayout here
|
|
||||||
_relayout();
|
|
||||||
|
|
||||||
panel.startStatusArea();
|
|
||||||
panel.startupAnimation();
|
|
||||||
|
|
||||||
let display = global.screen.get_display();
|
|
||||||
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
|
||||||
|
|
||||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||||
|
|
||||||
@ -223,6 +252,7 @@ function start() {
|
|||||||
|
|
||||||
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
||||||
global.screen.connect('window-left-monitor', _windowLeftMonitor);
|
global.screen.connect('window-left-monitor', _windowLeftMonitor);
|
||||||
|
global.screen.connect('restacked', _windowsRestacked);
|
||||||
|
|
||||||
_nWorkspacesChanged();
|
_nWorkspacesChanged();
|
||||||
}
|
}
|
||||||
@ -314,17 +344,24 @@ function _windowRemoved(workspace, window) {
|
|||||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
|
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
|
||||||
// If the window left the primary monitor, that
|
// If the window left the primary monitor, that
|
||||||
// might make that workspace empty
|
// might make that workspace empty
|
||||||
if (monitorIndex == global.get_primary_monitor_index())
|
if (monitorIndex == layoutManager.primaryIndex)
|
||||||
_queueCheckWorkspaces();
|
_queueCheckWorkspaces();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
|
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
|
||||||
// If the window entered the primary monitor, that
|
// If the window entered the primary monitor, that
|
||||||
// might make that workspace non-empty
|
// might make that workspace non-empty
|
||||||
if (monitorIndex == global.get_primary_monitor_index())
|
if (monitorIndex == layoutManager.primaryIndex)
|
||||||
_queueCheckWorkspaces();
|
_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() {
|
function _queueCheckWorkspaces() {
|
||||||
if (_checkWorkspacesId == 0)
|
if (_checkWorkspacesId == 0)
|
||||||
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
||||||
@ -408,15 +445,40 @@ function setThemeStylesheet(cssStylesheet)
|
|||||||
*/
|
*/
|
||||||
function loadTheme() {
|
function loadTheme() {
|
||||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||||
|
let previousTheme = themeContext.get_theme();
|
||||||
|
|
||||||
let cssStylesheet = _defaultCssStylesheet;
|
let cssStylesheet = _defaultCssStylesheet;
|
||||||
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 });
|
||||||
|
|
||||||
|
if (global.session_type == Shell.SessionType.GDM)
|
||||||
|
theme.load_stylesheet(_gdmCssStylesheet);
|
||||||
|
|
||||||
|
if (previousTheme) {
|
||||||
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||||
|
|
||||||
|
for (let i = 0; i < customStylesheets.length; i++)
|
||||||
|
theme.load_stylesheet(customStylesheets[i]);
|
||||||
|
}
|
||||||
|
|
||||||
themeContext.set_theme (theme);
|
themeContext.set_theme (theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* notify:
|
||||||
|
* @msg: A message
|
||||||
|
* @details: Additional information
|
||||||
|
*/
|
||||||
|
function notify(msg, details) {
|
||||||
|
let source = new MessageTray.SystemNotificationSource();
|
||||||
|
messageTray.add(source);
|
||||||
|
let notification = new MessageTray.Notification(source, msg, details);
|
||||||
|
notification.setTransient(true);
|
||||||
|
source.notify(notification);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* notifyError:
|
* notifyError:
|
||||||
* @msg: An error message
|
* @msg: An error message
|
||||||
@ -431,11 +493,7 @@ function notifyError(msg, details) {
|
|||||||
else
|
else
|
||||||
log("error: " + msg)
|
log("error: " + msg)
|
||||||
|
|
||||||
let source = new MessageTray.SystemNotificationSource();
|
notify(msg, details);
|
||||||
messageTray.add(source);
|
|
||||||
let notification = new MessageTray.Notification(source, msg, details);
|
|
||||||
notification.setTransient(true);
|
|
||||||
source.notify(notification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -479,80 +537,16 @@ function _getAndClearErrorStack() {
|
|||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _relayout() {
|
function logStackTrace(msg) {
|
||||||
let monitors = global.get_monitors();
|
try {
|
||||||
// destroy old corners
|
throw new Error();
|
||||||
for (let i = 0; i < hotCorners.length; i++)
|
} catch (e) {
|
||||||
hotCorners[i].destroy();
|
// e.stack must have at least two lines, with the first being
|
||||||
hotCorners = [];
|
// logStackTrace() (which we strip off), and the second being
|
||||||
|
// our caller.
|
||||||
|
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
|
||||||
let primary = global.get_primary_monitor();
|
log(msg ? (msg + '\n' + trace) : trace);
|
||||||
for (let i = 0; i < monitors.length; i++) {
|
|
||||||
let monitor = monitors[i];
|
|
||||||
let isPrimary = (monitor.x == primary.x &&
|
|
||||||
monitor.y == primary.y &&
|
|
||||||
monitor.width == primary.width &&
|
|
||||||
monitor.height == primary.height);
|
|
||||||
|
|
||||||
let cornerX = monitor.x;
|
|
||||||
let cornerY = monitor.y;
|
|
||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
||||||
cornerX += monitor.width;
|
|
||||||
|
|
||||||
|
|
||||||
let haveTopLeftCorner = true;
|
|
||||||
|
|
||||||
/* 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) */
|
|
||||||
let besideX;
|
|
||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
||||||
besideX = monitor.x + 1;
|
|
||||||
else
|
|
||||||
besideX = cornerX - 1;
|
|
||||||
let besideY = cornerY;
|
|
||||||
let aboveX = cornerX;
|
|
||||||
let aboveY = cornerY - 1;
|
|
||||||
|
|
||||||
for (let j = 0; j < monitors.length; j++) {
|
|
||||||
if (i == j)
|
|
||||||
continue;
|
|
||||||
let otherMonitor = monitors[j];
|
|
||||||
if (besideX >= otherMonitor.x &&
|
|
||||||
besideX < otherMonitor.x + otherMonitor.width &&
|
|
||||||
besideY >= otherMonitor.y &&
|
|
||||||
besideY < otherMonitor.y + otherMonitor.height) {
|
|
||||||
haveTopLeftCorner = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (aboveX >= otherMonitor.x &&
|
|
||||||
aboveX < otherMonitor.x + otherMonitor.width &&
|
|
||||||
aboveY >= otherMonitor.y &&
|
|
||||||
aboveY < otherMonitor.y + otherMonitor.height) {
|
|
||||||
haveTopLeftCorner = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We only want hot corners where there is a natural top-left
|
|
||||||
* corner, and on the primary monitor */
|
|
||||||
if (!isPrimary && !haveTopLeftCorner)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let corner = new Panel.HotCorner(isPrimary ? panel.button : null);
|
|
||||||
hotCorners.push(corner);
|
|
||||||
corner.actor.set_position(cornerX, cornerY);
|
|
||||||
if (isPrimary)
|
|
||||||
panel.setHotCorner(corner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panel.relayout();
|
|
||||||
overview.relayout();
|
|
||||||
|
|
||||||
// To avoid updating the position and size of the workspaces
|
|
||||||
// in the overview, we just hide the overview. The positions
|
|
||||||
// will be updated when it is next shown.
|
|
||||||
overview.hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||||
@ -581,9 +575,8 @@ function _globalKeyPressHandler(actor, event) {
|
|||||||
let keyCode = event.get_key_code();
|
let keyCode = event.get_key_code();
|
||||||
let modifierState = Shell.get_event_state(event);
|
let modifierState = Shell.get_event_state(event);
|
||||||
|
|
||||||
let display = global.screen.get_display();
|
|
||||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||||
let action = display.get_keybinding_action(keyCode, modifierState);
|
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
||||||
|
|
||||||
// The screenshot action should always be available (even if a
|
// The screenshot action should always be available (even if a
|
||||||
// modal dialog is present)
|
// modal dialog is present)
|
||||||
@ -606,6 +599,15 @@ function _globalKeyPressHandler(actor, event) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
||||||
|
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of the other bindings are relevant outside of the user's session
|
||||||
|
if (global.session_type != Shell.SessionType.USER)
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||||
// but that could be considered confusing; we also disable them in the main view.
|
// but that could be considered confusing; we also disable them in the main view.
|
||||||
@ -629,9 +631,6 @@ function _globalKeyPressHandler(actor, event) {
|
|||||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||||
overview.hide();
|
overview.hide();
|
||||||
return true;
|
return true;
|
||||||
case Meta.KeyBindingAction.SWITCH_PANELS:
|
|
||||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -20,9 +20,6 @@ const Params = imports.misc.params;
|
|||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const ANIMATION_TIME = 0.2;
|
const ANIMATION_TIME = 0.2;
|
||||||
const NOTIFICATION_TIMEOUT = 4;
|
const NOTIFICATION_TIMEOUT = 4;
|
||||||
const SUMMARY_TIMEOUT = 1;
|
const SUMMARY_TIMEOUT = 1;
|
||||||
@ -71,14 +68,19 @@ function _fixMarkup(text, allowMarkup) {
|
|||||||
// Support &, ", ', < and >, escape all other
|
// Support &, ", ', < and >, escape all other
|
||||||
// occurrences of '&'.
|
// occurrences of '&'.
|
||||||
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
||||||
|
|
||||||
// Support <b>, <i>, and <u>, escape anything else
|
// Support <b>, <i>, and <u>, escape anything else
|
||||||
// so it displays as raw markup.
|
// so it displays as raw markup.
|
||||||
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1');
|
_text = _text.replace(/<(?!\/?[biu]>)/g, '<');
|
||||||
} else {
|
|
||||||
// Escape everything
|
try {
|
||||||
let _text = text.replace(/&/g, '&');
|
Pango.parse_markup(_text, -1, '');
|
||||||
return _text.replace(/</g, '<');
|
return _text;
|
||||||
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// !allowMarkup, or invalid markup
|
||||||
|
return GLib.markup_escape_text(text, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function URLHighlighter(text, lineWrap, allowMarkup) {
|
function URLHighlighter(text, lineWrap, allowMarkup) {
|
||||||
@ -232,9 +234,7 @@ FocusGrabber.prototype = {
|
|||||||
|
|
||||||
this.actor = actor;
|
this.actor = actor;
|
||||||
|
|
||||||
let metaDisplay = global.screen.get_display();
|
this._prevFocusedWindow = global.display.focus_window;
|
||||||
|
|
||||||
this._prevFocusedWindow = metaDisplay.focus_window;
|
|
||||||
this._prevKeyFocusActor = global.stage.get_key_focus();
|
this._prevKeyFocusActor = global.stage.get_key_focus();
|
||||||
|
|
||||||
if (!Main.overview.visible)
|
if (!Main.overview.visible)
|
||||||
@ -269,7 +269,7 @@ FocusGrabber.prototype = {
|
|||||||
let source = event.get_source();
|
let source = event.get_source();
|
||||||
switch (event.type()) {
|
switch (event.type()) {
|
||||||
case Clutter.EventType.BUTTON_PRESS:
|
case Clutter.EventType.BUTTON_PRESS:
|
||||||
if (!this.actor.contains(source))
|
if (!this.actor.contains(source) && !Main.keyboard.actor.contains(source))
|
||||||
this.emit('button-pressed', source);
|
this.emit('button-pressed', source);
|
||||||
break;
|
break;
|
||||||
case Clutter.EventType.KEY_PRESS:
|
case Clutter.EventType.KEY_PRESS:
|
||||||
@ -288,8 +288,6 @@ FocusGrabber.prototype = {
|
|||||||
if (!this._hasFocus)
|
if (!this._hasFocus)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
|
|
||||||
if (this._focusActorChangedId > 0) {
|
if (this._focusActorChangedId > 0) {
|
||||||
global.stage.disconnect(this._focusActorChangedId);
|
global.stage.disconnect(this._focusActorChangedId);
|
||||||
this._focusActorChangedId = 0;
|
this._focusActorChangedId = 0;
|
||||||
@ -308,8 +306,8 @@ FocusGrabber.prototype = {
|
|||||||
this._hasFocus = false;
|
this._hasFocus = false;
|
||||||
this.emit('focus-ungrabbed');
|
this.emit('focus-ungrabbed');
|
||||||
|
|
||||||
if (this._prevFocusedWindow && !metaDisplay.focus_window) {
|
if (this._prevFocusedWindow && !global.display.focus_window) {
|
||||||
metaDisplay.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
|
global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
|
||||||
this._prevFocusedWindow = null;
|
this._prevFocusedWindow = null;
|
||||||
}
|
}
|
||||||
if (this._prevKeyFocusActor) {
|
if (this._prevKeyFocusActor) {
|
||||||
@ -398,6 +396,8 @@ function Notification(source, title, banner, params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Notification.prototype = {
|
Notification.prototype = {
|
||||||
|
IMAGE_SIZE: 125,
|
||||||
|
|
||||||
_init: function(source, title, banner, params) {
|
_init: function(source, title, banner, params) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.urgency = Urgency.NORMAL;
|
this.urgency = Urgency.NORMAL;
|
||||||
@ -411,8 +411,10 @@ Notification.prototype = {
|
|||||||
this._bannerBodyText = null;
|
this._bannerBodyText = null;
|
||||||
this._bannerBodyMarkup = false;
|
this._bannerBodyMarkup = false;
|
||||||
this._titleFitsInBannerMode = true;
|
this._titleFitsInBannerMode = true;
|
||||||
|
this._titleDirection = St.TextDirection.NONE;
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
|
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
|
||||||
|
this._imageBin = null;
|
||||||
|
|
||||||
source.connect('destroy', Lang.bind(this,
|
source.connect('destroy', Lang.bind(this,
|
||||||
function (source, reason) {
|
function (source, reason) {
|
||||||
@ -442,9 +444,19 @@ Notification.prototype = {
|
|||||||
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
|
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
|
||||||
this._table.add(this._bannerBox, { row: 0,
|
this._table.add(this._bannerBox, { row: 0,
|
||||||
col: 1,
|
col: 1,
|
||||||
|
col_span: 2,
|
||||||
|
x_expand: false,
|
||||||
y_expand: false,
|
y_expand: false,
|
||||||
y_fill: false });
|
y_fill: false });
|
||||||
|
|
||||||
|
// This is an empty cell that overlaps with this._bannerBox cell to ensure
|
||||||
|
// that this._bannerBox cell expands horizontally, while not forcing the
|
||||||
|
// this._imageBin that is also in col: 2 to expand horizontally.
|
||||||
|
this._table.add(new St.Bin(), { row: 0,
|
||||||
|
col: 2,
|
||||||
|
y_expand: false,
|
||||||
|
y_fill: false });
|
||||||
|
|
||||||
this._titleLabel = new St.Label();
|
this._titleLabel = new St.Label();
|
||||||
this._bannerBox.add_actor(this._titleLabel);
|
this._bannerBox.add_actor(this._titleLabel);
|
||||||
this._bannerUrlHighlighter = new URLHighlighter();
|
this._bannerUrlHighlighter = new URLHighlighter();
|
||||||
@ -496,7 +508,10 @@ Notification.prototype = {
|
|||||||
this._actionArea = null;
|
this._actionArea = null;
|
||||||
this._buttonBox = null;
|
this._buttonBox = null;
|
||||||
}
|
}
|
||||||
if (!this._scrollArea && !this._actionArea)
|
if (this._imageBin && params.clear)
|
||||||
|
this.unsetImage();
|
||||||
|
|
||||||
|
if (!this._scrollArea && !this._actionArea && !this._imageBin)
|
||||||
this._table.remove_style_class_name('multi-line-notification');
|
this._table.remove_style_class_name('multi-line-notification');
|
||||||
|
|
||||||
this._icon = params.icon || this.source.createNotificationIcon();
|
this._icon = params.icon || this.source.createNotificationIcon();
|
||||||
@ -510,6 +525,19 @@ Notification.prototype = {
|
|||||||
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
|
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
|
||||||
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
|
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
|
||||||
|
|
||||||
|
if (Pango.find_base_dir(title, -1) == Pango.Direction.RTL)
|
||||||
|
this._titleDirection = St.TextDirection.RTL;
|
||||||
|
else
|
||||||
|
this._titleDirection = St.TextDirection.LTR;
|
||||||
|
|
||||||
|
// Let the title's text direction control the overall direction
|
||||||
|
// of the notification - in case where different scripts are used
|
||||||
|
// in the notification, this is the right thing for the icon, and
|
||||||
|
// arguably for action buttons as well. Labels other than the title
|
||||||
|
// will be allocated at the available width, so that their alignment
|
||||||
|
// is done correctly automatically.
|
||||||
|
this._table.set_direction(this._titleDirection);
|
||||||
|
|
||||||
// Unless the notification has custom content, we save this._bannerBodyText
|
// Unless the notification has custom content, we save this._bannerBodyText
|
||||||
// to add it to the content of the notification if the notification is
|
// to add it to the content of the notification if the notification is
|
||||||
// expandable due to other elements in its content area or due to the banner
|
// expandable due to other elements in its content area or due to the banner
|
||||||
@ -546,8 +574,10 @@ Notification.prototype = {
|
|||||||
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
|
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
|
||||||
vscrollbar_policy: this._scrollPolicy,
|
vscrollbar_policy: this._scrollPolicy,
|
||||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||||
vfade: true });
|
style_class: 'vfade' });
|
||||||
this._table.add(this._scrollArea, { row: 1, col: 1 });
|
this._table.add(this._scrollArea, { row: 1,
|
||||||
|
col: 2 });
|
||||||
|
this._updateLastColumnSettings();
|
||||||
this._contentArea = new St.BoxLayout({ name: 'notification-body',
|
this._contentArea = new St.BoxLayout({ name: 'notification-body',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this._scrollArea.add_actor(this._contentArea);
|
this._scrollArea.add_actor(this._contentArea);
|
||||||
@ -624,13 +654,52 @@ Notification.prototype = {
|
|||||||
if (!props)
|
if (!props)
|
||||||
props = {};
|
props = {};
|
||||||
props.row = 2;
|
props.row = 2;
|
||||||
props.col = 1;
|
props.col = 2;
|
||||||
|
|
||||||
this._table.add_style_class_name('multi-line-notification');
|
this._table.add_style_class_name('multi-line-notification');
|
||||||
this._table.add(this._actionArea, props);
|
this._table.add(this._actionArea, props);
|
||||||
|
this._updateLastColumnSettings();
|
||||||
this._updated();
|
this._updated();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateLastColumnSettings: function() {
|
||||||
|
if (this._scrollArea)
|
||||||
|
this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
|
||||||
|
col_span: this._imageBin ? 1 : 2 });
|
||||||
|
if (this._actionArea)
|
||||||
|
this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
|
||||||
|
col_span: this._imageBin ? 1 : 2 });
|
||||||
|
},
|
||||||
|
|
||||||
|
setImage: function(image) {
|
||||||
|
if (this._imageBin)
|
||||||
|
this.unsetImage();
|
||||||
|
this._imageBin = new St.Bin();
|
||||||
|
this._imageBin.child = image;
|
||||||
|
this._imageBin.opacity = 230;
|
||||||
|
this._table.add_style_class_name('multi-line-notification');
|
||||||
|
this._table.add_style_class_name('notification-with-image');
|
||||||
|
this._updateLastColumnSettings();
|
||||||
|
this._table.add(this._imageBin, { row: 1,
|
||||||
|
col: 1,
|
||||||
|
row_span: 2,
|
||||||
|
x_expand: false,
|
||||||
|
y_expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
unsetImage: function() {
|
||||||
|
if (this._imageBin) {
|
||||||
|
this._table.remove_style_class_name('notification-with-image');
|
||||||
|
this._table.remove_actor(this._imageBin);
|
||||||
|
this._imageBin = null;
|
||||||
|
this._updateLastColumnSettings();
|
||||||
|
if (!this._scrollArea && !this._actionArea)
|
||||||
|
this._table.remove_style_class_name('multi-line-notification');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// addButton:
|
// addButton:
|
||||||
// @id: the action ID
|
// @id: the action ID
|
||||||
// @label: the label for the action's button
|
// @label: the label for the action's button
|
||||||
@ -646,7 +715,9 @@ Notification.prototype = {
|
|||||||
|
|
||||||
let box = new St.BoxLayout({ name: 'notification-actions' });
|
let box = new St.BoxLayout({ name: 'notification-actions' });
|
||||||
this.setActionArea(box, { x_expand: false,
|
this.setActionArea(box, { x_expand: false,
|
||||||
|
y_expand: false,
|
||||||
x_fill: false,
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
x_align: St.Align.END });
|
x_align: St.Align.END });
|
||||||
this._buttonBox = box;
|
this._buttonBox = box;
|
||||||
}
|
}
|
||||||
@ -711,23 +782,39 @@ Notification.prototype = {
|
|||||||
let availWidth = box.x2 - box.x1;
|
let availWidth = box.x2 - box.x1;
|
||||||
|
|
||||||
let titleBox = new Clutter.ActorBox();
|
let titleBox = new Clutter.ActorBox();
|
||||||
titleBox.x1 = titleBox.y1 = 0;
|
let titleBoxW = Math.min(titleNatW, availWidth);
|
||||||
titleBox.x2 = Math.min(titleNatW, availWidth);
|
if (this._titleDirection == St.TextDirection.RTL) {
|
||||||
|
titleBox.x1 = availWidth - titleBoxW;
|
||||||
|
titleBox.x2 = availWidth;
|
||||||
|
} else {
|
||||||
|
titleBox.x1 = 0;
|
||||||
|
titleBox.x2 = titleBoxW;
|
||||||
|
}
|
||||||
|
titleBox.y1 = 0;
|
||||||
titleBox.y2 = titleNatH;
|
titleBox.y2 = titleNatH;
|
||||||
this._titleLabel.allocate(titleBox, flags);
|
this._titleLabel.allocate(titleBox, flags);
|
||||||
this._titleFitsInBannerMode = (titleNatW <= availWidth);
|
this._titleFitsInBannerMode = (titleNatW <= availWidth);
|
||||||
|
|
||||||
let bannerFits = true;
|
let bannerFits = true;
|
||||||
if (titleBox.x2 + this._spacing > availWidth) {
|
if (titleBoxW + this._spacing > availWidth) {
|
||||||
this._bannerLabel.opacity = 0;
|
this._bannerLabel.opacity = 0;
|
||||||
bannerFits = false;
|
bannerFits = false;
|
||||||
} else {
|
} else {
|
||||||
let bannerBox = new Clutter.ActorBox();
|
let bannerBox = new Clutter.ActorBox();
|
||||||
bannerBox.x1 = titleBox.x2 + this._spacing;
|
|
||||||
|
if (this._titleDirection == St.TextDirection.RTL) {
|
||||||
|
bannerBox.x1 = 0;
|
||||||
|
bannerBox.x2 = titleBox.x1 - this._spacing;
|
||||||
|
|
||||||
|
bannerFits = (bannerBox.x2 - bannerNatW >= 0);
|
||||||
|
} else {
|
||||||
|
bannerBox.x1 = titleBox.x2 + this._spacing;
|
||||||
|
bannerBox.x2 = availWidth;
|
||||||
|
|
||||||
|
bannerFits = (bannerBox.x1 + bannerNatW <= availWidth);
|
||||||
|
}
|
||||||
bannerBox.y1 = 0;
|
bannerBox.y1 = 0;
|
||||||
bannerBox.x2 = Math.min(bannerBox.x1 + bannerNatW, availWidth);
|
|
||||||
bannerBox.y2 = titleNatH;
|
bannerBox.y2 = titleNatH;
|
||||||
bannerFits = (bannerBox.x1 + bannerNatW <= availWidth);
|
|
||||||
this._bannerLabel.allocate(bannerBox, flags);
|
this._bannerLabel.allocate(bannerBox, flags);
|
||||||
|
|
||||||
// Make _bannerLabel visible if the entire notification
|
// Make _bannerLabel visible if the entire notification
|
||||||
@ -802,6 +889,7 @@ Notification.prototype = {
|
|||||||
// Restore banner opacity in case the notification is shown in the
|
// Restore banner opacity in case the notification is shown in the
|
||||||
// banner mode again on update.
|
// banner mode again on update.
|
||||||
this._bannerLabel.opacity = 255;
|
this._bannerLabel.opacity = 255;
|
||||||
|
this.emit('collapsed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onActionInvoked: function(actor, mouseButtonClicked, id) {
|
_onActionInvoked: function(actor, mouseButtonClicked, id) {
|
||||||
@ -852,20 +940,96 @@ Source.prototype = {
|
|||||||
|
|
||||||
_init: function(title) {
|
_init: function(title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
|
this.actor = new Shell.GenericContainer();
|
||||||
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
this.actor.connect('destroy', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._actorDestroyed = true;
|
||||||
|
}));
|
||||||
|
this._actorDestroyed = false;
|
||||||
|
|
||||||
|
this._counterLabel = new St.Label();
|
||||||
|
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
|
||||||
|
child: this._counterLabel });
|
||||||
|
this._counterBin.hide();
|
||||||
|
|
||||||
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
|
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
|
||||||
height: this.ICON_SIZE,
|
height: this.ICON_SIZE,
|
||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
|
|
||||||
|
this.actor.add_actor(this._iconBin);
|
||||||
|
this.actor.add_actor(this._counterBin);
|
||||||
|
|
||||||
this.isTransient = false;
|
this.isTransient = false;
|
||||||
this.isChat = false;
|
this.isChat = false;
|
||||||
|
|
||||||
this.notifications = [];
|
this.notifications = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||||
|
let [min, nat] = this._iconBin.get_preferred_width(forHeight);
|
||||||
|
alloc.min_size = min; alloc.nat_size = nat;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||||
|
let [min, nat] = this._iconBin.get_preferred_height(forWidth);
|
||||||
|
alloc.min_size = min; alloc.nat_size = nat;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function(actor, box, flags) {
|
||||||
|
// the iconBin should fill our entire box
|
||||||
|
this._iconBin.allocate(box, flags);
|
||||||
|
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
|
||||||
|
let [minWidth, minHeight, naturalWidth, naturalHeight] = this._counterBin.get_preferred_size();
|
||||||
|
let direction = this.actor.get_direction();
|
||||||
|
|
||||||
|
if (direction == St.TextDirection.LTR) {
|
||||||
|
// allocate on the right in LTR
|
||||||
|
childBox.x1 = box.x2 - naturalWidth;
|
||||||
|
childBox.x2 = box.x2;
|
||||||
|
} else {
|
||||||
|
// allocate on the left in RTL
|
||||||
|
childBox.x1 = 0;
|
||||||
|
childBox.x2 = naturalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
childBox.y1 = box.y2 - naturalHeight;
|
||||||
|
childBox.y2 = box.y2;
|
||||||
|
|
||||||
|
this._counterBin.allocate(childBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setCount: function(count, visible) {
|
||||||
|
if (isNaN(parseInt(count)))
|
||||||
|
throw new Error("Invalid notification count: " + count);
|
||||||
|
|
||||||
|
if (this._actorDestroyed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._counterBin.visible = visible;
|
||||||
|
this._counterLabel.set_text(count.toString());
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCount: function() {
|
||||||
|
let count = this.notifications.length;
|
||||||
|
this._setCount(count, count > 1);
|
||||||
|
},
|
||||||
|
|
||||||
setTransient: function(isTransient) {
|
setTransient: function(isTransient) {
|
||||||
this.isTransient = isTransient;
|
this.isTransient = isTransient;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setTitle: function(newTitle) {
|
||||||
|
this.title = newTitle;
|
||||||
|
this.emit('title-changed');
|
||||||
|
},
|
||||||
|
|
||||||
// Called to create a new icon actor (of size this.ICON_SIZE).
|
// Called to create a new icon actor (of size this.ICON_SIZE).
|
||||||
// Must be overridden by the subclass if you do not pass icons
|
// Must be overridden by the subclass if you do not pass icons
|
||||||
// explicitly to the Notification() constructor.
|
// explicitly to the Notification() constructor.
|
||||||
@ -876,7 +1040,7 @@ Source.prototype = {
|
|||||||
// Unlike createNotificationIcon, this always returns the same actor;
|
// Unlike createNotificationIcon, this always returns the same actor;
|
||||||
// there is only one summary icon actor for a Source.
|
// there is only one summary icon actor for a Source.
|
||||||
getSummaryIcon: function() {
|
getSummaryIcon: function() {
|
||||||
return this._iconBin;
|
return this.actor;
|
||||||
},
|
},
|
||||||
|
|
||||||
pushNotification: function(notification) {
|
pushNotification: function(notification) {
|
||||||
@ -895,7 +1059,11 @@ Source.prototype = {
|
|||||||
this.notifications.splice(index, 1);
|
this.notifications.splice(index, 1);
|
||||||
if (this.notifications.length == 0)
|
if (this.notifications.length == 0)
|
||||||
this._lastNotificationRemoved();
|
this._lastNotificationRemoved();
|
||||||
|
|
||||||
|
this._updateCount();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._updateCount();
|
||||||
},
|
},
|
||||||
|
|
||||||
notify: function(notification) {
|
notify: function(notification) {
|
||||||
@ -932,6 +1100,8 @@ Source.prototype = {
|
|||||||
for (let i = this.notifications.length - 1; i >= 0; i--)
|
for (let i = this.notifications.length - 1; i >= 0; i--)
|
||||||
if (!this.notifications[i].resident)
|
if (!this.notifications[i].resident)
|
||||||
this.notifications[i].destroy();
|
this.notifications[i].destroy();
|
||||||
|
|
||||||
|
this._updateCount();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Default implementation is to destroy this source, but subclasses can override
|
// Default implementation is to destroy this source, but subclasses can override
|
||||||
@ -968,6 +1138,11 @@ SummaryItem.prototype = {
|
|||||||
this._sourceTitleBin.child = this._sourceTitle;
|
this._sourceTitleBin.child = this._sourceTitle;
|
||||||
this._sourceTitleBin.width = 0;
|
this._sourceTitleBin.width = 0;
|
||||||
|
|
||||||
|
this.source.connect('title-changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._sourceTitle.text = source.title;
|
||||||
|
}));
|
||||||
|
|
||||||
this._sourceBox.add(this._sourceIcon, { y_fill: false });
|
this._sourceBox.add(this._sourceIcon, { y_fill: false });
|
||||||
this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false });
|
this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false });
|
||||||
this.actor.child = this._sourceBox;
|
this.actor.child = this._sourceBox;
|
||||||
@ -975,7 +1150,7 @@ SummaryItem.prototype = {
|
|||||||
this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview',
|
this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview',
|
||||||
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
|
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
|
||||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||||
vfade: true });
|
style_class: 'vfade' });
|
||||||
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
|
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this.notificationStackView.add_actor(this.notificationStack);
|
this.notificationStackView.add_actor(this.notificationStack);
|
||||||
@ -1159,12 +1334,11 @@ MessageTray.prototype = {
|
|||||||
this._summaryMotionId = 0;
|
this._summaryMotionId = 0;
|
||||||
|
|
||||||
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||||
{ reactive: true,
|
{ reactive: true,
|
||||||
track_hover: true });
|
track_hover: true });
|
||||||
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
|
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
|
||||||
this.actor.add_actor(this._summaryBoxPointer.actor);
|
|
||||||
this._summaryBoxPointer.actor.lower_bottom();
|
|
||||||
this._summaryBoxPointer.actor.hide();
|
this._summaryBoxPointer.actor.hide();
|
||||||
|
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
|
||||||
|
|
||||||
this._summaryBoxPointerItem = null;
|
this._summaryBoxPointerItem = null;
|
||||||
this._summaryBoxPointerContentUpdatedId = 0;
|
this._summaryBoxPointerContentUpdatedId = 0;
|
||||||
@ -1199,6 +1373,7 @@ MessageTray.prototype = {
|
|||||||
|
|
||||||
this._trayState = State.HIDDEN;
|
this._trayState = State.HIDDEN;
|
||||||
this._locked = false;
|
this._locked = false;
|
||||||
|
this._traySummoned = false;
|
||||||
this._useLongerTrayLeftTimeout = false;
|
this._useLongerTrayLeftTimeout = false;
|
||||||
this._trayLeftTimeoutId = 0;
|
this._trayLeftTimeoutId = 0;
|
||||||
this._pointerInTray = false;
|
this._pointerInTray = false;
|
||||||
@ -1214,12 +1389,12 @@ MessageTray.prototype = {
|
|||||||
this._notificationRemoved = false;
|
this._notificationRemoved = false;
|
||||||
this._reNotifyAfterHideNotification = null;
|
this._reNotifyAfterHideNotification = null;
|
||||||
|
|
||||||
Main.chrome.addActor(this.actor, { affectsStruts: false,
|
Main.layoutManager.trayBox.add_actor(this.actor);
|
||||||
visibleInOverview: true });
|
this.actor.y = -1;
|
||||||
Main.chrome.trackActor(this._notificationBin);
|
Main.layoutManager.trackChrome(this.actor);
|
||||||
Main.chrome.trackActor(this._summaryBoxPointer.actor);
|
Main.layoutManager.trackChrome(this._notificationBin);
|
||||||
|
|
||||||
global.screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
|
||||||
|
|
||||||
this._setSizePosition();
|
this._setSizePosition();
|
||||||
|
|
||||||
@ -1255,23 +1430,11 @@ MessageTray.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_setSizePosition: function() {
|
_setSizePosition: function() {
|
||||||
let primary = global.get_primary_monitor();
|
let monitor = Main.layoutManager.bottomMonitor;
|
||||||
this.actor.x = primary.x;
|
|
||||||
this.actor.y = primary.y + primary.height - 1;
|
|
||||||
this.actor.width = primary.width;
|
|
||||||
this._notificationBin.x = 0;
|
this._notificationBin.x = 0;
|
||||||
this._notificationBin.width = primary.width;
|
this._notificationBin.width = monitor.width;
|
||||||
this._summaryBin.x = 0;
|
this._summaryBin.x = 0;
|
||||||
this._summaryBin.width = primary.width;
|
this._summaryBin.width = monitor.width;
|
||||||
|
|
||||||
if (this._pointerBarrier)
|
|
||||||
global.destroy_pointer_barrier(this._pointerBarrier);
|
|
||||||
this._pointerBarrier =
|
|
||||||
global.create_pointer_barrier(primary.x + primary.width, primary.y + primary.height - this.actor.height,
|
|
||||||
primary.x + primary.width, primary.y + primary.height,
|
|
||||||
4 /* BarrierNegativeX */);
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
contains: function(source) {
|
contains: function(source) {
|
||||||
@ -1417,7 +1580,19 @@ MessageTray.prototype = {
|
|||||||
if (!this._locked)
|
if (!this._locked)
|
||||||
return;
|
return;
|
||||||
this._locked = false;
|
this._locked = false;
|
||||||
this._pointerInTray = this.actor.hover && !this._summaryBoxPointer.bin.hover;
|
this._pointerInTray = this.actor.hover;
|
||||||
|
this._updateState();
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle: function() {
|
||||||
|
this._traySummoned = !this._traySummoned;
|
||||||
|
this._updateState();
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this._traySummoned = false;
|
||||||
|
this.actor.set_hover(false);
|
||||||
|
this._summary.set_hover(false);
|
||||||
this._updateState();
|
this._updateState();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1578,6 +1753,8 @@ MessageTray.prototype = {
|
|||||||
this._clickedSummaryItemMouseButton != button) {
|
this._clickedSummaryItemMouseButton != button) {
|
||||||
this._clickedSummaryItem = summaryItem;
|
this._clickedSummaryItem = summaryItem;
|
||||||
this._clickedSummaryItemMouseButton = button;
|
this._clickedSummaryItemMouseButton = button;
|
||||||
|
|
||||||
|
summaryItem.source.emit('summary-item-clicked', button);
|
||||||
} else {
|
} else {
|
||||||
this._unsetClickedSummaryItem();
|
this._unsetClickedSummaryItem();
|
||||||
}
|
}
|
||||||
@ -1603,13 +1780,6 @@ MessageTray.prototype = {
|
|||||||
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
|
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Don't do anything if the mouse is over the summary notification as this should be considered as
|
|
||||||
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we
|
|
||||||
// should treat the mouse being over the summary notification as the tray being left for collapsing
|
|
||||||
// any expanded summary item other than the one related to the notification.
|
|
||||||
if (this._summaryBoxPointer.bin.hover)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._useLongerTrayLeftTimeout = false;
|
this._useLongerTrayLeftTimeout = false;
|
||||||
if (this._trayLeftTimeoutId) {
|
if (this._trayLeftTimeoutId) {
|
||||||
Mainloop.source_remove(this._trayLeftTimeoutId);
|
Mainloop.source_remove(this._trayLeftTimeoutId);
|
||||||
@ -1733,7 +1903,7 @@ MessageTray.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Summary
|
// Summary
|
||||||
let summarySummoned = this._pointerInSummary || this._overviewVisible;
|
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
|
||||||
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked;
|
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked;
|
||||||
let summaryHovered = this._pointerInTray || this._pointerInSummary;
|
let summaryHovered = this._pointerInTray || this._pointerInSummary;
|
||||||
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered;
|
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered;
|
||||||
@ -1827,18 +1997,16 @@ MessageTray.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_showTray: function() {
|
_showTray: function() {
|
||||||
let primary = global.get_primary_monitor();
|
|
||||||
this._tween(this.actor, '_trayState', State.SHOWN,
|
this._tween(this.actor, '_trayState', State.SHOWN,
|
||||||
{ y: primary.y + primary.height - this.actor.height,
|
{ y: -this.actor.height,
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_hideTray: function() {
|
_hideTray: function() {
|
||||||
let primary = global.get_primary_monitor();
|
|
||||||
this._tween(this.actor, '_trayState', State.HIDDEN,
|
this._tween(this.actor, '_trayState', State.HIDDEN,
|
||||||
{ y: primary.y + primary.height - 1,
|
{ y: -1,
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
@ -1926,7 +2094,7 @@ MessageTray.prototype = {
|
|||||||
|
|
||||||
_notificationTimeout: function() {
|
_notificationTimeout: function() {
|
||||||
let [x, y, mods] = global.get_pointer();
|
let [x, y, mods] = global.get_pointer();
|
||||||
if (y > this._lastSeenMouseY + 10 && y < this.actor.y) {
|
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
|
||||||
// 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
|
||||||
@ -1984,9 +2152,15 @@ MessageTray.prototype = {
|
|||||||
this._notification.expand(!autoExpanding);
|
this._notification.expand(!autoExpanding);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onNotificationExpanded: function() {
|
_onNotificationExpanded: function() {
|
||||||
let expandedY = this.actor.height - this._notificationBin.height;
|
let expandedY = this.actor.height - this._notificationBin.height;
|
||||||
if (this._notificationBin.y != expandedY)
|
|
||||||
|
// Don't animate the notification to its new position if it has shrunk:
|
||||||
|
// there will be a very visible "gap" that breaks the illusion.
|
||||||
|
|
||||||
|
if (this._notificationBin.y < expandedY)
|
||||||
|
this._notificationBin.y = expandedY;
|
||||||
|
else if (this._notification.y != expandedY)
|
||||||
this._tween(this._notificationBin, '_notificationState', State.SHOWN,
|
this._tween(this._notificationBin, '_notificationState', State.SHOWN,
|
||||||
{ y: expandedY,
|
{ y: expandedY,
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
@ -2001,7 +2175,6 @@ MessageTray.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_showSummary: function(timeout) {
|
_showSummary: function(timeout) {
|
||||||
let primary = global.get_primary_monitor();
|
|
||||||
this._summaryBin.opacity = 0;
|
this._summaryBin.opacity = 0;
|
||||||
this._summaryBin.y = this.actor.height;
|
this._summaryBin.y = this.actor.height;
|
||||||
this._tween(this._summaryBin, '_summaryState', State.SHOWN,
|
this._tween(this._summaryBin, '_summaryState', State.SHOWN,
|
||||||
|
@ -10,8 +10,6 @@ const Pango = imports.gi.Pango;
|
|||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
@ -20,6 +18,7 @@ const Main = imports.ui.main;
|
|||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||||
|
const FADE_IN_BUTTONS_TIME = 0.33;
|
||||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||||
|
|
||||||
const State = {
|
const State = {
|
||||||
@ -36,10 +35,12 @@ function ModalDialog() {
|
|||||||
|
|
||||||
ModalDialog.prototype = {
|
ModalDialog.prototype = {
|
||||||
_init: function(params) {
|
_init: function(params) {
|
||||||
params = Params.parse(params, { styleClass: null });
|
params = Params.parse(params, { shellReactive: false,
|
||||||
|
styleClass: null });
|
||||||
|
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this._hasModal = false;
|
this._hasModal = false;
|
||||||
|
this._shellReactive = params.shellReactive;
|
||||||
|
|
||||||
this._group = new St.Group({ visible: false,
|
this._group = new St.Group({ visible: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
@ -50,35 +51,35 @@ ModalDialog.prototype = {
|
|||||||
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
|
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
|
||||||
this._group.add_constraint(constraint);
|
this._group.add_constraint(constraint);
|
||||||
|
|
||||||
global.focus_manager.add_group(this._group);
|
|
||||||
this._initialKeyFocus = this._group;
|
|
||||||
this._savedKeyFocus = null;
|
|
||||||
|
|
||||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||||
|
|
||||||
this._actionKeys = {};
|
this._actionKeys = {};
|
||||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
|
|
||||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
|
||||||
{ inhibitEvents: true });
|
|
||||||
|
|
||||||
this._backgroundBin = new St.Bin();
|
this._backgroundBin = new St.Bin();
|
||||||
|
|
||||||
this._group.add_actor(this._backgroundBin);
|
this._group.add_actor(this._backgroundBin);
|
||||||
this._lightbox.highlight(this._backgroundBin);
|
|
||||||
|
|
||||||
this._backgroundStack = new Shell.Stack();
|
|
||||||
this._backgroundBin.child = this._backgroundStack;
|
|
||||||
|
|
||||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
|
||||||
this._backgroundStack.add_actor(this._eventBlocker);
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
this._backgroundStack.add_actor(this._dialogLayout);
|
|
||||||
|
if (!this._shellReactive) {
|
||||||
|
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||||
|
{ inhibitEvents: true });
|
||||||
|
this._lightbox.highlight(this._backgroundBin);
|
||||||
|
|
||||||
|
let stack = new Shell.Stack();
|
||||||
|
this._backgroundBin.child = stack;
|
||||||
|
|
||||||
|
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||||
|
stack.add_actor(this._eventBlocker);
|
||||||
|
stack.add_actor(this._dialogLayout);
|
||||||
|
} else {
|
||||||
|
this._backgroundBin.child = this._dialogLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||||
this._dialogLayout.add(this.contentLayout,
|
this._dialogLayout.add(this.contentLayout,
|
||||||
@ -88,15 +89,24 @@ ModalDialog.prototype = {
|
|||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||||
opacity: 220,
|
|
||||||
vertical: false });
|
vertical: false });
|
||||||
this._dialogLayout.add(this._buttonLayout,
|
this._dialogLayout.add(this._buttonLayout,
|
||||||
{ expand: true,
|
{ expand: true,
|
||||||
x_align: St.Align.MIDDLE,
|
x_align: St.Align.MIDDLE,
|
||||||
y_align: St.Align.END });
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
global.focus_manager.add_group(this._dialogLayout);
|
||||||
|
this._initialKeyFocus = this._dialogLayout;
|
||||||
|
this._savedKeyFocus = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._group.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
setButtons: function(buttons) {
|
setButtons: function(buttons) {
|
||||||
|
let hadChildren = this._buttonLayout.get_children() > 0;
|
||||||
|
|
||||||
this._buttonLayout.destroy_children();
|
this._buttonLayout.destroy_children();
|
||||||
this._actionKeys = {};
|
this._actionKeys = {};
|
||||||
|
|
||||||
@ -107,10 +117,10 @@ ModalDialog.prototype = {
|
|||||||
let action = buttonInfo['action'];
|
let action = buttonInfo['action'];
|
||||||
let key = buttonInfo['key'];
|
let key = buttonInfo['key'];
|
||||||
|
|
||||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
label: label });
|
label: label });
|
||||||
|
|
||||||
let x_alignment;
|
let x_alignment;
|
||||||
if (buttons.length == 1)
|
if (buttons.length == 1)
|
||||||
@ -122,20 +132,36 @@ ModalDialog.prototype = {
|
|||||||
else
|
else
|
||||||
x_alignment = St.Align.MIDDLE;
|
x_alignment = St.Align.MIDDLE;
|
||||||
|
|
||||||
this._initialKeyFocus = button;
|
this._initialKeyFocus = buttonInfo.button;
|
||||||
this._buttonLayout.add(button,
|
this._buttonLayout.add(buttonInfo.button,
|
||||||
{ expand: true,
|
{ expand: true,
|
||||||
x_fill: false,
|
x_fill: false,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
x_align: x_alignment,
|
x_align: x_alignment,
|
||||||
y_align: St.Align.MIDDLE });
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
button.connect('clicked', action);
|
buttonInfo.button.connect('clicked', action);
|
||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
this._actionKeys[key] = action;
|
this._actionKeys[key] = action;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fade in buttons if there weren't any before
|
||||||
|
if (!hadChildren && i > 0) {
|
||||||
|
this._buttonLayout.opacity = 0;
|
||||||
|
Tweener.addTween(this._buttonLayout,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: FADE_IN_BUTTONS_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this.emit('buttons-set');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.emit('buttons-set');
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
_onKeyPressEvent: function(object, keyPressEvent) {
|
||||||
@ -151,7 +177,7 @@ ModalDialog.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_fadeOpen: function() {
|
_fadeOpen: function() {
|
||||||
let monitor = global.get_focus_monitor();
|
let monitor = Main.layoutManager.focusMonitor;
|
||||||
|
|
||||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
this._backgroundBin.set_position(monitor.x, monitor.y);
|
||||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
this._backgroundBin.set_size(monitor.width, monitor.height);
|
||||||
@ -159,7 +185,8 @@ ModalDialog.prototype = {
|
|||||||
this.state = State.OPENING;
|
this.state = State.OPENING;
|
||||||
|
|
||||||
this._dialogLayout.opacity = 255;
|
this._dialogLayout.opacity = 255;
|
||||||
this._lightbox.show();
|
if (this._lightbox)
|
||||||
|
this._lightbox.show();
|
||||||
this._group.opacity = 0;
|
this._group.opacity = 0;
|
||||||
this._group.show();
|
this._group.show();
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
@ -195,6 +222,7 @@ ModalDialog.prototype = {
|
|||||||
|
|
||||||
this.state = State.CLOSING;
|
this.state = State.CLOSING;
|
||||||
this.popModal(timestamp);
|
this.popModal(timestamp);
|
||||||
|
this._savedKeyFocus = null;
|
||||||
|
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
@ -224,7 +252,8 @@ ModalDialog.prototype = {
|
|||||||
global.gdk_screen.get_display().sync();
|
global.gdk_screen.get_display().sync();
|
||||||
this._hasModal = false;
|
this._hasModal = false;
|
||||||
|
|
||||||
this._eventBlocker.raise_top();
|
if (!this._shellReactive)
|
||||||
|
this._eventBlocker.raise_top();
|
||||||
},
|
},
|
||||||
|
|
||||||
pushModal: function (timestamp) {
|
pushModal: function (timestamp) {
|
||||||
@ -240,7 +269,8 @@ ModalDialog.prototype = {
|
|||||||
} else
|
} else
|
||||||
this._initialKeyFocus.grab_key_focus();
|
this._initialKeyFocus.grab_key_focus();
|
||||||
|
|
||||||
this._eventBlocker.lower_bottom();
|
if (!this._shellReactive)
|
||||||
|
this._eventBlocker.lower_bottom();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
400
js/ui/networkAgent.js
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
|
*
|
||||||
|
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const NetworkManager = imports.gi.NetworkManager;
|
||||||
|
const NMClient = imports.gi.NMClient;
|
||||||
|
const Pango = imports.gi.Pango;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
function NetworkSecretDialog() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSecretDialog.prototype = {
|
||||||
|
__proto__: ModalDialog.ModalDialog.prototype,
|
||||||
|
|
||||||
|
_init: function(agent, requestId, connection, settingName, hints) {
|
||||||
|
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
|
||||||
|
|
||||||
|
this._agent = agent;
|
||||||
|
this._requestId = requestId;
|
||||||
|
this._connection = connection;
|
||||||
|
this._settingName = settingName;
|
||||||
|
this._hints = hints;
|
||||||
|
|
||||||
|
this._content = this._getContent();
|
||||||
|
|
||||||
|
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||||
|
vertical: false });
|
||||||
|
this.contentLayout.add(mainContentBox,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: true });
|
||||||
|
|
||||||
|
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
|
||||||
|
mainContentBox.add(icon,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||||
|
vertical: true });
|
||||||
|
mainContentBox.add(messageBox,
|
||||||
|
{ y_align: St.Align.START });
|
||||||
|
|
||||||
|
let subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||||
|
text: this._content.title });
|
||||||
|
messageBox.add(subjectLabel,
|
||||||
|
{ y_fill: false,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
if (this._content.message != null) {
|
||||||
|
let descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||||
|
text: this._content.message,
|
||||||
|
// HACK: for reasons unknown to me, the label
|
||||||
|
// is not asked the correct height for width,
|
||||||
|
// and thus is underallocated
|
||||||
|
// place a fixed height to avoid overflowing
|
||||||
|
style: 'height: 3em'
|
||||||
|
});
|
||||||
|
descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
|
||||||
|
messageBox.add(descriptionLabel,
|
||||||
|
{ y_fill: true,
|
||||||
|
y_align: St.Align.START,
|
||||||
|
expand: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
|
||||||
|
let pos = 0;
|
||||||
|
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||||
|
let secret = this._content.secrets[i];
|
||||||
|
let label = new St.Label({ style_class: 'polkit-dialog-password-label',
|
||||||
|
text: secret.label });
|
||||||
|
|
||||||
|
let reactive = secret.key != null;
|
||||||
|
|
||||||
|
secret.entry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||||
|
text: secret.value, can_focus: reactive,
|
||||||
|
reactive: reactive });
|
||||||
|
|
||||||
|
if (secret.validate)
|
||||||
|
secret.valid = secret.validate(secret);
|
||||||
|
else // no special validation, just ensure it's not empty
|
||||||
|
secret.valid = secret.value.length > 0;
|
||||||
|
|
||||||
|
if (reactive) {
|
||||||
|
secret.entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
|
||||||
|
secret.value = secret.entry.get_text();
|
||||||
|
if (secret.validate)
|
||||||
|
secret.valid = secret.validate(secret);
|
||||||
|
else
|
||||||
|
secret.valid = secret.value.length > 0;
|
||||||
|
this._updateOkButton();
|
||||||
|
}));
|
||||||
|
} else
|
||||||
|
secret.valid = true;
|
||||||
|
|
||||||
|
secretTable.add(label, { row: pos, col: 0, x_align: St.Align.START, y_align: St.Align.START });
|
||||||
|
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (secret.password) {
|
||||||
|
secret.entry.clutter_text.set_password_char('\u25cf');
|
||||||
|
|
||||||
|
// FIXME: need a real checkbox here
|
||||||
|
let button = new St.Button({ button_mask: St.ButtonMask.ONE,
|
||||||
|
can_focus: true });
|
||||||
|
let checkbox = new St.BoxLayout({ vertical: false,
|
||||||
|
style_class: 'network-dialog-show-password-checkbox'
|
||||||
|
});
|
||||||
|
let _switch = new PopupMenu.Switch(false);
|
||||||
|
checkbox.add(_switch.actor);
|
||||||
|
checkbox.add(new St.Label({ text: _("Show password") }), { expand: true });
|
||||||
|
button.connect('clicked', function() {
|
||||||
|
_switch.toggle();
|
||||||
|
if (_switch.state)
|
||||||
|
secret.entry.clutter_text.set_password_char('');
|
||||||
|
else
|
||||||
|
secret.entry.clutter_text.set_password_char('\u25cf');
|
||||||
|
});
|
||||||
|
button.child = checkbox;
|
||||||
|
secretTable.add(button, { row: pos, col: 1, x_expand: true, x_fill: true, y_fill: true })
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messageBox.add(secretTable);
|
||||||
|
|
||||||
|
this._okButton = { label: _("Connect"),
|
||||||
|
action: Lang.bind(this, this._onOk),
|
||||||
|
key: Clutter.KEY_Return,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setButtons([{ label: _("Cancel"),
|
||||||
|
action: Lang.bind(this, this.cancel),
|
||||||
|
key: Clutter.KEY_Escape,
|
||||||
|
},
|
||||||
|
this._okButton]);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateOkButton: function() {
|
||||||
|
let valid = true;
|
||||||
|
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||||
|
let secret = this._content.secrets[i];
|
||||||
|
valid = valid && secret.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._okButton.button.reactive = valid;
|
||||||
|
this._okButton.button.can_focus = valid;
|
||||||
|
if (valid)
|
||||||
|
this._okButton.button.remove_style_pseudo_class('disabled');
|
||||||
|
else
|
||||||
|
this._okButton.button.add_style_pseudo_class('disabled');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onOk: function() {
|
||||||
|
let valid = true;
|
||||||
|
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||||
|
let secret = this._content.secrets[i];
|
||||||
|
valid = valid && secret.valid;
|
||||||
|
if (secret.key != null)
|
||||||
|
this._agent.set_password(this._requestId, secret.key, secret.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
this._agent.respond(this._requestId, false);
|
||||||
|
this.close(global.get_current_time());
|
||||||
|
}
|
||||||
|
// do nothing if not valid
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this._agent.respond(this._requestId, true);
|
||||||
|
this.close(global.get_current_time());
|
||||||
|
},
|
||||||
|
|
||||||
|
_validateWpaPsk: function(secret) {
|
||||||
|
let value = secret.value;
|
||||||
|
if (value.length == 64) {
|
||||||
|
// must be composed of hexadecimal digits only
|
||||||
|
for (let i = 0; i < 64; i++) {
|
||||||
|
if (!((value[i] >= 'a' && value[i] <= 'f')
|
||||||
|
|| (value[i] >= 'A' && value[i] <= 'F')
|
||||||
|
|| (value[i] >= '0' && value[i] <= '9')))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (value.length >= 8 && value.length <= 63);
|
||||||
|
},
|
||||||
|
|
||||||
|
_validateStaticWep: function(secret) {
|
||||||
|
let value = secret.value;
|
||||||
|
if (secret.wep_key_type == NetworkManager.WepKeyType.KEY) {
|
||||||
|
if (value.length == 10 || value.length == 26) {
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
if (!((value[i] >= 'a' && value[i] <= 'f')
|
||||||
|
|| (value[i] >= 'A' && value[i] <= 'F')
|
||||||
|
|| (value[i] >= '0' && value[i] <= '9')))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (value.length == 5 || value.length == 13) {
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
if (!((value[i] >= 'a' && value[i] <= 'z')
|
||||||
|
|| (value[i] >= 'A' && value[i] <= 'Z')))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
} else if (secret.wep_key_type == NetworkManager.WepKeyType.PASSPHRASE) {
|
||||||
|
if (value.length < 0 || value.length > 64)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getWirelessSecrets: function(secrets, wirelessSetting) {
|
||||||
|
let wirelessSecuritySetting = this._connection.get_setting_wireless_security();
|
||||||
|
switch (wirelessSecuritySetting.key_mgmt) {
|
||||||
|
// First the easy ones
|
||||||
|
case 'wpa-none':
|
||||||
|
case 'wpa-psk':
|
||||||
|
secrets.push({ label: _("Password: "), key: 'psk',
|
||||||
|
value: wirelessSecuritySetting.psk || '',
|
||||||
|
validate: this._validateWpaPsk, password: true });
|
||||||
|
break;
|
||||||
|
case 'none': // static WEP
|
||||||
|
secrets.push({ label: _("Key: "), key: 'wep-key' + wirelessSecuritySetting.wep_tx_keyidx,
|
||||||
|
value: wirelessSecuritySetting.get_wep_key(wirelessSecuritySetting.wep_tx_keyidx) || '',
|
||||||
|
wep_key_type: wirelessSecuritySetting.wep_key_type,
|
||||||
|
validate: this._validateStaticWep, password: true });
|
||||||
|
break;
|
||||||
|
case 'ieee8021x':
|
||||||
|
if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP
|
||||||
|
secrets.push({ label: _("Password: "), key: 'leap-password',
|
||||||
|
value: wirelessSecuritySetting.leap_password || '', password: true });
|
||||||
|
else // Dynamic (IEEE 802.1x) WEP
|
||||||
|
this._get8021xSecrets(secrets);
|
||||||
|
break;
|
||||||
|
case 'wpa-eap':
|
||||||
|
this._get8021xSecrets(secrets);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log('Invalid wireless key management: ' + wirelessSecuritySetting.key_mgmt);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_get8021xSecrets: function(secrets) {
|
||||||
|
let ieee8021xSetting = this._connection.get_setting_802_1x();
|
||||||
|
let phase2method;
|
||||||
|
|
||||||
|
switch (ieee8021xSetting.get_eap_method(0)) {
|
||||||
|
case 'md5':
|
||||||
|
case 'leap':
|
||||||
|
case 'ttls':
|
||||||
|
case 'peap':
|
||||||
|
// TTLS and PEAP are actually much more complicated, but this complication
|
||||||
|
// is not visible here since we only care about phase2 authentication
|
||||||
|
// (and don't even care of which one)
|
||||||
|
secrets.push({ label: _("Username: "), key: null,
|
||||||
|
value: ieee8021xSetting.identity || '', password: false });
|
||||||
|
secrets.push({ label: _("Password: "), key: 'password',
|
||||||
|
value: ieee8021xSetting.password || '', password: true });
|
||||||
|
break;
|
||||||
|
case 'tls':
|
||||||
|
secrets.push({ label: _("Identity: "), key: null,
|
||||||
|
value: ieee8021xSetting.identity || '', password: false });
|
||||||
|
secrets.push({ label: _("Private key password: "), key: 'private-key-password',
|
||||||
|
value: ieee8021xSetting.private_key_password || '', password: true });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log('Invalid EAP/IEEE802.1x method: ' + ieee8021xSetting.get_eap_method(0));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPPPoESecrets: function(secrets) {
|
||||||
|
let pppoeSetting = this._connection.get_setting_pppoe();
|
||||||
|
secrets.push({ label: _("Username: "), key: 'username',
|
||||||
|
value: pppoeSetting.username || '', password: false });
|
||||||
|
secrets.push({ label: _("Service: "), key: 'service',
|
||||||
|
value: pppoeSetting.service || '', password: false });
|
||||||
|
secrets.push({ label: _("Password: "), key: 'password',
|
||||||
|
value: pppoeSetting.password || '', password: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
_getMobileSecrets: function(secrets, connectionType) {
|
||||||
|
let setting;
|
||||||
|
if (connectionType == 'bluetooth')
|
||||||
|
setting = this._connection.get_setting_cdma() || this._connection.get_setting_gsm();
|
||||||
|
else
|
||||||
|
setting = this._connection.get_setting_by_name(connectionType);
|
||||||
|
secrets.push({ label: _("Password: "), key: 'password',
|
||||||
|
value: setting.value || '', password: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
_getContent: function() {
|
||||||
|
let connectionSetting = this._connection.get_setting_connection();
|
||||||
|
let connectionType = connectionSetting.get_connection_type();
|
||||||
|
let wirelessSetting;
|
||||||
|
let ssid;
|
||||||
|
|
||||||
|
let content = { };
|
||||||
|
content.secrets = [ ];
|
||||||
|
|
||||||
|
switch (connectionType) {
|
||||||
|
case '802-11-wireless':
|
||||||
|
wirelessSetting = this._connection.get_setting_wireless();
|
||||||
|
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
|
||||||
|
content.title = _("Authentication required by wireless network");
|
||||||
|
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
|
||||||
|
this._getWirelessSecrets(content.secrets, wirelessSetting);
|
||||||
|
break;
|
||||||
|
case '802-3-ethernet':
|
||||||
|
content.title = _("Wired 802.1X authentication");
|
||||||
|
content.message = null;
|
||||||
|
content.secrets.push({ label: _("Network name: "), key: null,
|
||||||
|
value: connectionSetting.get_id(), password: false });
|
||||||
|
this._get8021xSecrets(content.secrets);
|
||||||
|
break;
|
||||||
|
case 'pppoe':
|
||||||
|
content.title = _("DSL authentication");
|
||||||
|
content.message = null;
|
||||||
|
this._getPPPoESecrets(content.secrets);
|
||||||
|
break;
|
||||||
|
case 'gsm':
|
||||||
|
if (this._hints.indexOf('pin') != -1) {
|
||||||
|
let gsmSetting = this._connection.get_setting_gsm();
|
||||||
|
content.title = _("PIN code required");
|
||||||
|
content.message = _("PIN code is needed for the mobile broadband device");
|
||||||
|
content.secrets.push({ label: _("PIN: "), key: 'pin',
|
||||||
|
value: gsmSetting.pin || '', password: true });
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
case 'cdma':
|
||||||
|
case 'bluetooth':
|
||||||
|
content.title = _("Mobile broadband network password");
|
||||||
|
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
|
||||||
|
this._getMobileSecrets(content.secrets);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log('Invalid connection type: ' + connectionType);
|
||||||
|
};
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function NetworkAgent() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkAgent.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._native = new Shell.NetworkAgent({ auto_register: true,
|
||||||
|
identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||||
|
|
||||||
|
this._dialogs = { };
|
||||||
|
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||||
|
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||||
|
},
|
||||||
|
|
||||||
|
_newRequest: function(agent, requestId, connection, settingName, hints) {
|
||||||
|
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
|
||||||
|
dialog.connect('destroy', Lang.bind(this, function() {
|
||||||
|
delete this._dialogs[requestId];
|
||||||
|
}));
|
||||||
|
this._dialogs[requestId] = dialog;
|
||||||
|
dialog.open(global.get_current_time());
|
||||||
|
},
|
||||||
|
|
||||||
|
_cancelRequest: function(agent, requestId) {
|
||||||
|
this._dialogs[requestId].close(global.get_current_time());
|
||||||
|
this._dialogs[requestId].destroy();
|
||||||
|
}
|
||||||
|
};
|
@ -7,8 +7,6 @@ const Lang = imports.lang;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -121,11 +119,6 @@ NotificationDaemon.prototype = {
|
|||||||
return new St.Icon({ icon_name: icon,
|
return new St.Icon({ icon_name: icon,
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
icon_type: St.IconType.FULLCOLOR,
|
||||||
icon_size: size });
|
icon_size: size });
|
||||||
} else if (hints['image-data']) {
|
|
||||||
let [width, height, rowStride, hasAlpha,
|
|
||||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
|
||||||
return textureCache.load_from_raw(data, data.length, hasAlpha,
|
|
||||||
width, height, rowStride, size);
|
|
||||||
} else {
|
} else {
|
||||||
let stockIcon;
|
let stockIcon;
|
||||||
switch (hints.urgency) {
|
switch (hints.urgency) {
|
||||||
@ -150,7 +143,7 @@ NotificationDaemon.prototype = {
|
|||||||
//
|
//
|
||||||
// Either a pid or ndata.notification is needed to retrieve or
|
// Either a pid or ndata.notification is needed to retrieve or
|
||||||
// create a source.
|
// create a source.
|
||||||
_getSource: function(title, pid, ndata) {
|
_getSource: function(title, pid, ndata, sender) {
|
||||||
if (!pid && !(ndata && ndata.notification))
|
if (!pid && !(ndata && ndata.notification))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -167,10 +160,13 @@ NotificationDaemon.prototype = {
|
|||||||
// with a transient one from the same sender, so we
|
// with a transient one from the same sender, so we
|
||||||
// always create a new source object for new transient notifications
|
// always create a new source object for new transient notifications
|
||||||
// and never add it to this._sources .
|
// and never add it to this._sources .
|
||||||
if (!isForTransientNotification && this._sources[pid])
|
if (!isForTransientNotification && this._sources[pid]) {
|
||||||
return this._sources[pid];
|
let source = this._sources[pid];
|
||||||
|
source.setTitle(title);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
let source = new Source(title, pid);
|
let source = new Source(title, pid, sender);
|
||||||
source.setTransient(isForTransientNotification);
|
source.setTransient(isForTransientNotification);
|
||||||
|
|
||||||
if (!isForTransientNotification) {
|
if (!isForTransientNotification) {
|
||||||
@ -189,9 +185,13 @@ NotificationDaemon.prototype = {
|
|||||||
actions, hints, timeout) {
|
actions, hints, timeout) {
|
||||||
let id;
|
let id;
|
||||||
|
|
||||||
// Filter out chat and presence notifications from Empathy, since we
|
// Filter out chat, presence, calls and invitation notifications from
|
||||||
// handle that information from telepathyClient.js
|
// Empathy, since we handle that information from telepathyClient.js
|
||||||
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
||||||
|
hints['category'] == 'x-empathy.im.room-invitation' ||
|
||||||
|
hints['category'] == 'x-empathy.call.incoming' ||
|
||||||
|
hints['category'] == 'x-empathy.call.incoming"' ||
|
||||||
|
hints['category'] == 'x-empathy.im.subscription-request' ||
|
||||||
hints['category'] == 'presence.online' ||
|
hints['category'] == 'presence.online' ||
|
||||||
hints['category'] == 'presence.offline')) {
|
hints['category'] == 'presence.offline')) {
|
||||||
// Ignore replacesId since we already sent back a
|
// Ignore replacesId since we already sent back a
|
||||||
@ -215,14 +215,18 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||||
|
|
||||||
// Be compatible with the various hints for image data
|
// Be compatible with the various hints for image data and image path
|
||||||
// 'image-data' is the latest name of this hint, introduced in 1.2
|
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
|
||||||
if (!hints['image-data']) {
|
|
||||||
|
if (!hints['image-path'] && hints['image_path'])
|
||||||
|
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
|
||||||
|
|
||||||
|
if (!hints['image-data'])
|
||||||
if (hints['image_data'])
|
if (hints['image_data'])
|
||||||
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
||||||
else if (hints['icon_data'])
|
else if (hints['icon_data'] && !hints['image-path'])
|
||||||
hints['image-data'] = hints['icon_data']; // previous versions of the spec
|
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
|
||||||
}
|
hints['image-data'] = hints['icon_data'];
|
||||||
|
|
||||||
let ndata = { appName: appName,
|
let ndata = { appName: appName,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
@ -243,7 +247,7 @@ NotificationDaemon.prototype = {
|
|||||||
let sender = DBus.getCurrentMessageContext().sender;
|
let sender = DBus.getCurrentMessageContext().sender;
|
||||||
let pid = this._senderToPid[sender];
|
let pid = this._senderToPid[sender];
|
||||||
|
|
||||||
let source = this._getSource(appName, pid, ndata);
|
let source = this._getSource(appName, pid, ndata, sender);
|
||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
this._notifyForSource(source, ndata);
|
this._notifyForSource(source, ndata);
|
||||||
@ -264,7 +268,7 @@ NotificationDaemon.prototype = {
|
|||||||
if (!ndata)
|
if (!ndata)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
source = this._getSource(appName, pid, ndata);
|
source = this._getSource(appName, pid, ndata, sender);
|
||||||
|
|
||||||
// We only store sender-pid entries for persistent sources.
|
// We only store sender-pid entries for persistent sources.
|
||||||
// Removing the entries once the source is destroyed
|
// Removing the entries once the source is destroyed
|
||||||
@ -301,7 +305,7 @@ NotificationDaemon.prototype = {
|
|||||||
ndata.notification = notification;
|
ndata.notification = notification;
|
||||||
notification.connect('destroy', Lang.bind(this,
|
notification.connect('destroy', Lang.bind(this,
|
||||||
function(n, reason) {
|
function(n, reason) {
|
||||||
delete this._notifications[id];
|
delete this._notifications[ndata.id];
|
||||||
let notificationClosedReason;
|
let notificationClosedReason;
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case MessageTray.NotificationDestroyedReason.EXPIRED:
|
case MessageTray.NotificationDestroyedReason.EXPIRED:
|
||||||
@ -314,11 +318,11 @@ NotificationDaemon.prototype = {
|
|||||||
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
|
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this._emitNotificationClosed(id, notificationClosedReason);
|
this._emitNotificationClosed(ndata.id, notificationClosedReason);
|
||||||
}));
|
}));
|
||||||
notification.connect('action-invoked', Lang.bind(this,
|
notification.connect('action-invoked', Lang.bind(this,
|
||||||
function(n, actionId) {
|
function(n, actionId) {
|
||||||
this._emitActionInvoked(id, actionId);
|
this._emitActionInvoked(ndata.id, actionId);
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
notification.update(summary, body, { icon: iconActor,
|
notification.update(summary, body, { icon: iconActor,
|
||||||
@ -326,10 +330,34 @@ NotificationDaemon.prototype = {
|
|||||||
clear: true });
|
clear: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hints['image-data'] || hints['image-path']) {
|
||||||
|
let image = null;
|
||||||
|
if (hints['image-data']) {
|
||||||
|
let [width, height, rowStride, hasAlpha,
|
||||||
|
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||||
|
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
|
||||||
|
width, height, rowStride, notification.IMAGE_SIZE);
|
||||||
|
} else if (hints['image-path']) {
|
||||||
|
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
|
||||||
|
notification.IMAGE_SIZE,
|
||||||
|
notification.IMAGE_SIZE);
|
||||||
|
}
|
||||||
|
notification.setImage(image);
|
||||||
|
} else {
|
||||||
|
notification.unsetImage();
|
||||||
|
}
|
||||||
|
|
||||||
if (actions.length) {
|
if (actions.length) {
|
||||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
notification.setUseActionIcons(hints['action-icons'] == true);
|
||||||
for (let i = 0; i < actions.length - 1; i += 2)
|
for (let i = 0; i < actions.length - 1; i += 2) {
|
||||||
notification.addButton(actions[i], actions[i + 1]);
|
if (actions[i] == 'default')
|
||||||
|
notification.connect('clicked', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._emitActionInvoked(ndata.id, "default");
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
notification.addButton(actions[i], actions[i + 1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch (hints.urgency) {
|
switch (hints.urgency) {
|
||||||
case Urgency.LOW:
|
case Urgency.LOW:
|
||||||
@ -413,7 +441,7 @@ NotificationDaemon.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon) {
|
_onTrayIconAdded: function(o, icon) {
|
||||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
|
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null);
|
||||||
source.setTrayIcon(icon);
|
source.setTrayIcon(icon);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -426,18 +454,28 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
||||||
|
|
||||||
function Source(title, pid) {
|
function Source(title, pid, sender) {
|
||||||
this._init(title, pid);
|
this._init(title, pid, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
Source.prototype = {
|
Source.prototype = {
|
||||||
__proto__: MessageTray.Source.prototype,
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
_init: function(title, pid) {
|
_init: function(title, pid, sender) {
|
||||||
MessageTray.Source.prototype._init.call(this, title);
|
MessageTray.Source.prototype._init.call(this, title);
|
||||||
|
|
||||||
this._pid = pid;
|
this._pid = pid;
|
||||||
this._appStateChangedId = 0;
|
if (sender)
|
||||||
|
// TODO: dbus-glib implementation of watch_name() doesn’t return an id to be used for
|
||||||
|
// unwatch_name() or implement unwatch_name(), however when we move to using GDBus implementation,
|
||||||
|
// we should save the id here and call unwatch_name() with it in destroy().
|
||||||
|
// Moving to GDBus is the work in progress: https://bugzilla.gnome.org/show_bug.cgi?id=648651
|
||||||
|
// and https://bugzilla.gnome.org/show_bug.cgi?id=622921 .
|
||||||
|
DBus.session.watch_name(sender,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
Lang.bind(this, this._onNameVanished));
|
||||||
|
|
||||||
this._setApp();
|
this._setApp();
|
||||||
if (this.app)
|
if (this.app)
|
||||||
this.title = this.app.get_name();
|
this.title = this.app.get_name();
|
||||||
@ -446,6 +484,16 @@ Source.prototype = {
|
|||||||
this._trayIcon = null;
|
this._trayIcon = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onNameVanished: function() {
|
||||||
|
// Destroy the notification source when its sender is removed from DBus.
|
||||||
|
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
|
||||||
|
// of which аre removed from DBus immediately.
|
||||||
|
// Sender being removed from DBus would normally result in a tray icon being removed,
|
||||||
|
// so allow the code path that handles the tray icon being removed to handle that case.
|
||||||
|
if (!this.trayIcon && this.app)
|
||||||
|
this.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
processNotification: function(notification, icon) {
|
processNotification: function(notification, icon) {
|
||||||
if (!this.app)
|
if (!this.app)
|
||||||
this._setApp();
|
this._setApp();
|
||||||
@ -498,10 +546,6 @@ Source.prototype = {
|
|||||||
if (!this.app)
|
if (!this.app)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We only update the app if this.app is null, so we can't disconnect the old this._appStateChangedId
|
|
||||||
// even if it were non-zero for some reason.
|
|
||||||
this._appStateChangedId = this.app.connect('notify::state', Lang.bind(this, this._appStateChanged));
|
|
||||||
|
|
||||||
// Only override the icon if we were previously using
|
// Only override the icon if we were previously using
|
||||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||||
if (!this._trayIcon) {
|
if (!this._trayIcon) {
|
||||||
@ -526,19 +570,6 @@ Source.prototype = {
|
|||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
_appStateChanged: function() {
|
|
||||||
// Destroy notification sources when their apps exit.
|
|
||||||
// The app exiting would normally result in a tray icon being removed,
|
|
||||||
// so the associated source would be destroyed through the code path
|
|
||||||
// that handles the tray icon being removed. We should not destroy
|
|
||||||
// the source associated with a tray icon when the application state
|
|
||||||
// is Shell.AppState.STOPPED because running applications that have
|
|
||||||
// no open windows would also have that state. This is often the case
|
|
||||||
// for applications that use tray icons.
|
|
||||||
if (!this._trayIcon && this.app.get_state() == Shell.AppState.STOPPED)
|
|
||||||
this.destroy();
|
|
||||||
},
|
|
||||||
|
|
||||||
openApp: function() {
|
openApp: function() {
|
||||||
if (this.app == null)
|
if (this.app == null)
|
||||||
return;
|
return;
|
||||||
@ -551,10 +582,6 @@ Source.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
if (this.app && this._appStateChangedId) {
|
|
||||||
this.app.disconnect(this._appStateChangedId);
|
|
||||||
this._appStateChangedId = 0;
|
|
||||||
}
|
|
||||||
MessageTray.Source.prototype.destroy.call(this);
|
MessageTray.Source.prototype.destroy.call(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -8,11 +8,10 @@ const Signals = imports.signals;
|
|||||||
const Lang = imports.lang;
|
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 Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
|
|
||||||
const AppDisplay = imports.ui.appDisplay;
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
|
const ContactDisplay = imports.ui.contactDisplay;
|
||||||
const Dash = imports.ui.dash;
|
const Dash = imports.ui.dash;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const DocDisplay = imports.ui.docDisplay;
|
const DocDisplay = imports.ui.docDisplay;
|
||||||
@ -20,6 +19,7 @@ const Lightbox = imports.ui.lightbox;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
|
const Params = imports.misc.params;
|
||||||
const PlaceDisplay = imports.ui.placeDisplay;
|
const PlaceDisplay = imports.ui.placeDisplay;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const ViewSelector = imports.ui.viewSelector;
|
const ViewSelector = imports.ui.viewSelector;
|
||||||
@ -98,14 +98,30 @@ ShellInfo.prototype = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function Overview() {
|
function Overview() {
|
||||||
this._init();
|
this._init.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
Overview.prototype = {
|
Overview.prototype = {
|
||||||
_init : function() {
|
_init : function(params) {
|
||||||
// The actual global.background_actor is inside global.window_group,
|
params = Params.parse(params, { isDummy: false });
|
||||||
// which is hidden when displaying the overview, so we display a clone.
|
|
||||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
this.isDummy = params.isDummy;
|
||||||
|
|
||||||
|
// We only have an overview in user sessions, so
|
||||||
|
// create a dummy overview in other cases
|
||||||
|
if (this.isDummy) {
|
||||||
|
this.animationInProgress = false;
|
||||||
|
this.visible = false;
|
||||||
|
this.workspaces = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main BackgroundActor is inside global.window_group which is
|
||||||
|
// hidden when displaying the overview, so we create a new
|
||||||
|
// one. Instances of this class share a single CoglTexture behind the
|
||||||
|
// scenes which allows us to show the background with different
|
||||||
|
// rendering options without duplicating the texture data.
|
||||||
|
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||||
this._background.hide();
|
this._background.hide();
|
||||||
global.overlay_group.add_actor(this._background);
|
global.overlay_group.add_actor(this._background);
|
||||||
|
|
||||||
@ -123,7 +139,7 @@ Overview.prototype = {
|
|||||||
let spacing = node.get_length('spacing');
|
let spacing = node.get_length('spacing');
|
||||||
if (spacing != this._spacing) {
|
if (spacing != this._spacing) {
|
||||||
this._spacing = spacing;
|
this._spacing = spacing;
|
||||||
this.relayout();
|
this._relayout();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -177,33 +193,51 @@ Overview.prototype = {
|
|||||||
// signal handlers and so forth. So we create them after
|
// signal handlers and so forth. So we create them after
|
||||||
// construction in this init() method.
|
// construction in this init() method.
|
||||||
init: function() {
|
init: function() {
|
||||||
this.shellInfo = new ShellInfo();
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
this.viewSelector = new ViewSelector.ViewSelector();
|
this._shellInfo = new ShellInfo();
|
||||||
this._group.add_actor(this.viewSelector.actor);
|
|
||||||
|
this._viewSelector = new ViewSelector.ViewSelector();
|
||||||
|
this._group.add_actor(this._viewSelector.actor);
|
||||||
|
|
||||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||||
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||||
|
|
||||||
let appView = new AppDisplay.AllAppDisplay();
|
let appView = new AppDisplay.AllAppDisplay();
|
||||||
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
||||||
|
|
||||||
// Default search providers
|
// Default search providers
|
||||||
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
this._viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||||
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
|
this._viewSelector.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||||
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
this._viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||||
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
|
this._viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
|
||||||
|
this._viewSelector.addSearchProvider(new ContactDisplay.ContactSearchProvider());
|
||||||
|
|
||||||
// TODO - recalculate everything when desktop size changes
|
// TODO - recalculate everything when desktop size changes
|
||||||
this.dash = new Dash.Dash();
|
this._dash = new Dash.Dash();
|
||||||
this._group.add_actor(this.dash.actor);
|
this._group.add_actor(this._dash.actor);
|
||||||
this.dash.actor.add_constraint(this.viewSelector.constrainY);
|
this._dash.actor.add_constraint(this._viewSelector.constrainY);
|
||||||
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
|
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
||||||
|
this.dashIconSize = this._dash.iconSize;
|
||||||
|
this._dash.connect('icon-size-changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.dashIconSize = this._dash.iconSize;
|
||||||
|
}));
|
||||||
|
|
||||||
// Translators: this is the name of the dock/favorites area on
|
// Translators: this is the name of the dock/favorites area on
|
||||||
// the left of the overview
|
// the left of the overview
|
||||||
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
|
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||||
|
this._relayout();
|
||||||
|
},
|
||||||
|
|
||||||
|
setMessage: function(text, undoCallback, undoLabel) {
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._shellInfo.setMessage(text, undoCallback, undoLabel);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragBegin: function() {
|
_onDragBegin: function() {
|
||||||
@ -222,7 +256,7 @@ Overview.prototype = {
|
|||||||
}
|
}
|
||||||
this._resetWindowSwitchTimeout();
|
this._resetWindowSwitchTimeout();
|
||||||
this._lastHoveredWindow = null;
|
this._lastHoveredWindow = null;
|
||||||
DND.removeMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
this.endItemDrag();
|
this.endItemDrag();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -275,6 +309,9 @@ Overview.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setScrollAdjustment: function(adjustment, direction) {
|
setScrollAdjustment: function(adjustment, direction) {
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
this._scrollAdjustment = adjustment;
|
this._scrollAdjustment = adjustment;
|
||||||
if (this._scrollAdjustment == null)
|
if (this._scrollAdjustment == null)
|
||||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||||
@ -283,7 +320,8 @@ Overview.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onButtonPress: function(actor, event) {
|
_onButtonPress: function(actor, event) {
|
||||||
if (this._scrollDirection == SwipeScrollDirection.NONE)
|
if (this._scrollDirection == SwipeScrollDirection.NONE
|
||||||
|
|| event.get_button() != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let [stageX, stageY] = event.get_coords();
|
let [stageX, stageY] = event.get_coords();
|
||||||
@ -392,7 +430,7 @@ Overview.prototype = {
|
|||||||
[stageX, stageY] = event.get_coords();
|
[stageX, stageY] = event.get_coords();
|
||||||
let dx = this._dragX - stageX;
|
let dx = this._dragX - stageX;
|
||||||
let dy = this._dragY - stageY;
|
let dy = this._dragY - stageY;
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
this._dragX = stageX;
|
this._dragX = stageX;
|
||||||
this._dragY = stageY;
|
this._dragY = stageY;
|
||||||
@ -439,8 +477,13 @@ Overview.prototype = {
|
|||||||
return clone;
|
return clone;
|
||||||
},
|
},
|
||||||
|
|
||||||
relayout: function () {
|
_relayout: function () {
|
||||||
let primary = global.get_primary_monitor();
|
// To avoid updating the position and size of the workspaces
|
||||||
|
// we just hide the overview. The positions will be updated
|
||||||
|
// when it is next shown.
|
||||||
|
this.hide();
|
||||||
|
|
||||||
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||||
|
|
||||||
let contentY = Main.panel.actor.height;
|
let contentY = Main.panel.actor.height;
|
||||||
@ -461,15 +504,15 @@ Overview.prototype = {
|
|||||||
// Set the dash's x position - y is handled by a constraint
|
// Set the dash's x position - y is handled by a constraint
|
||||||
let dashX;
|
let dashX;
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
dashX = primary.width;
|
dashX = primary.width;
|
||||||
} else {
|
} else {
|
||||||
dashX = 0;
|
dashX = 0;
|
||||||
}
|
}
|
||||||
this.dash.actor.set_x(dashX);
|
this._dash.actor.set_x(dashX);
|
||||||
|
|
||||||
this.viewSelector.actor.set_position(viewX, viewY);
|
this._viewSelector.actor.set_position(viewX, viewY);
|
||||||
this.viewSelector.actor.set_size(viewWidth, viewHeight);
|
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Public methods ////
|
//// Public methods ////
|
||||||
@ -502,6 +545,8 @@ Overview.prototype = {
|
|||||||
//
|
//
|
||||||
// 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)
|
||||||
|
return;
|
||||||
if (this._shown)
|
if (this._shown)
|
||||||
return;
|
return;
|
||||||
// Do this manually instead of using _syncInputMode, to handle failure
|
// Do this manually instead of using _syncInputMode, to handle failure
|
||||||
@ -529,6 +574,9 @@ Overview.prototype = {
|
|||||||
//
|
//
|
||||||
// If we switched to displaying the actors in the Overview rather than
|
// If we switched to displaying the actors in the Overview rather than
|
||||||
// clones of them, this would obviously no longer be necessary.
|
// clones of them, this would obviously no longer be necessary.
|
||||||
|
//
|
||||||
|
// Disable unredirection while in the overview
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
global.window_group.hide();
|
global.window_group.hide();
|
||||||
this._group.show();
|
this._group.show();
|
||||||
this._background.show();
|
this._background.show();
|
||||||
@ -560,6 +608,12 @@ Overview.prototype = {
|
|||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Tweener.addTween(this._background,
|
||||||
|
{ dim_factor: 0.4,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
|
||||||
this._coverPane.raise_top();
|
this._coverPane.raise_top();
|
||||||
this._coverPane.show();
|
this._coverPane.show();
|
||||||
this.emit('showing');
|
this.emit('showing');
|
||||||
@ -572,6 +626,9 @@ Overview.prototype = {
|
|||||||
// will result in the overview not being hidden until hideTemporarily() is
|
// will result in the overview not being hidden until hideTemporarily() is
|
||||||
// called.
|
// called.
|
||||||
showTemporarily: function() {
|
showTemporarily: function() {
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
if (this._shownTemporarily)
|
if (this._shownTemporarily)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -584,6 +641,9 @@ Overview.prototype = {
|
|||||||
//
|
//
|
||||||
// Reverses the effect of show()
|
// Reverses the effect of show()
|
||||||
hide: function() {
|
hide: function() {
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!this._shown)
|
if (!this._shown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -602,6 +662,9 @@ Overview.prototype = {
|
|||||||
//
|
//
|
||||||
// Reverses the effect of showTemporarily()
|
// Reverses the effect of showTemporarily()
|
||||||
hideTemporarily: function() {
|
hideTemporarily: function() {
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!this._shownTemporarily)
|
if (!this._shownTemporarily)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -613,6 +676,9 @@ Overview.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
|
if (this.isDummy)
|
||||||
|
return;
|
||||||
|
|
||||||
if (this._shown)
|
if (this._shown)
|
||||||
this.hide();
|
this.hide();
|
||||||
else
|
else
|
||||||
@ -678,6 +744,12 @@ Overview.prototype = {
|
|||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Tweener.addTween(this._background,
|
||||||
|
{ dim_factor: 1.0,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
|
||||||
this._coverPane.raise_top();
|
this._coverPane.raise_top();
|
||||||
this._coverPane.show();
|
this._coverPane.show();
|
||||||
this.emit('hiding');
|
this.emit('hiding');
|
||||||
@ -698,6 +770,9 @@ Overview.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_hideDone: function() {
|
_hideDone: function() {
|
||||||
|
// Re-enable unredirection
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
|
||||||
global.window_group.show();
|
global.window_group.show();
|
||||||
|
|
||||||
this.workspaces.destroy();
|
this.workspaces.destroy();
|
||||||
|
829
js/ui/panel.js
@ -9,31 +9,26 @@ const Pango = imports.gi.Pango;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const StatusMenu = imports.ui.statusMenu;
|
const UserMenu = imports.ui.userMenu;
|
||||||
const DateMenu = imports.ui.dateMenu;
|
const DateMenu = imports.ui.dateMenu;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const PANEL_ICON_SIZE = 24;
|
const PANEL_ICON_SIZE = 24;
|
||||||
|
|
||||||
const STARTUP_ANIMATION_TIME = 0.2;
|
|
||||||
|
|
||||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
|
||||||
|
|
||||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||||
|
|
||||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||||
const SPINNER_ANIMATION_TIME = 0.2;
|
const SPINNER_ANIMATION_TIME = 0.2;
|
||||||
|
|
||||||
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
|
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
|
||||||
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
|
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
|
||||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||||
'volume': imports.ui.status.volume.Indicator,
|
'volume': imports.ui.status.volume.Indicator,
|
||||||
@ -50,6 +45,14 @@ try {
|
|||||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GDM_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery'];
|
||||||
|
const GDM_TRAY_ICON_SHELL_IMPLEMENTATION = {
|
||||||
|
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||||
|
'volume': imports.ui.status.volume.Indicator,
|
||||||
|
'battery': imports.ui.status.power.Indicator,
|
||||||
|
'keyboard': imports.ui.status.keyboard.XKBIndicator
|
||||||
|
};
|
||||||
|
|
||||||
// To make sure the panel corners blend nicely with the panel,
|
// To make sure the panel corners blend nicely with the panel,
|
||||||
// we draw background and borders the same way, e.g. drawing
|
// we draw background and borders the same way, e.g. drawing
|
||||||
// them as filled shapes from the outside inwards instead of
|
// them as filled shapes from the outside inwards instead of
|
||||||
@ -240,7 +243,6 @@ AppMenuButton.prototype = {
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||||
this._metaDisplay = global.screen.get_display();
|
|
||||||
this._startingApps = [];
|
this._startingApps = [];
|
||||||
|
|
||||||
this._targetApp = null;
|
this._targetApp = null;
|
||||||
@ -260,10 +262,14 @@ AppMenuButton.prototype = {
|
|||||||
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
||||||
this._iconBox.connect('style-changed',
|
this._iconBox.connect('style-changed',
|
||||||
Lang.bind(this, this._onIconBoxStyleChanged));
|
Lang.bind(this, this._onIconBoxStyleChanged));
|
||||||
|
this._iconBox.connect('notify::allocation',
|
||||||
|
Lang.bind(this, this._updateIconBoxClip));
|
||||||
this._container.add_actor(this._iconBox);
|
this._container.add_actor(this._iconBox);
|
||||||
this._label = new TextShadower();
|
this._label = new TextShadower();
|
||||||
this._container.add_actor(this._label.actor);
|
this._container.add_actor(this._label.actor);
|
||||||
|
|
||||||
|
this._iconBottomClip = 0;
|
||||||
|
|
||||||
this._quitMenu = new PopupMenu.PopupMenuItem('');
|
this._quitMenu = new PopupMenu.PopupMenuItem('');
|
||||||
this.menu.addMenuItem(this._quitMenu);
|
this.menu.addMenuItem(this._quitMenu);
|
||||||
this._quitMenu.connect('activate', Lang.bind(this, this._onQuit));
|
this._quitMenu.connect('activate', Lang.bind(this, this._onQuit));
|
||||||
@ -300,6 +306,7 @@ AppMenuButton.prototype = {
|
|||||||
|
|
||||||
this._visible = true;
|
this._visible = true;
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
|
this.actor.reactive = true;
|
||||||
|
|
||||||
if (!this._targetIsCurrent)
|
if (!this._targetIsCurrent)
|
||||||
return;
|
return;
|
||||||
@ -316,6 +323,7 @@ AppMenuButton.prototype = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._visible = false;
|
this._visible = false;
|
||||||
|
this.actor.reactive = false;
|
||||||
if (!this._targetIsCurrent) {
|
if (!this._targetIsCurrent) {
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
return;
|
return;
|
||||||
@ -334,11 +342,16 @@ AppMenuButton.prototype = {
|
|||||||
|
|
||||||
_onIconBoxStyleChanged: function() {
|
_onIconBoxStyleChanged: function() {
|
||||||
let node = this._iconBox.get_theme_node();
|
let node = this._iconBox.get_theme_node();
|
||||||
let bottomClip = node.get_length('app-icon-bottom-clip');
|
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
|
||||||
if (bottomClip > 0)
|
this._updateIconBoxClip();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateIconBoxClip: function() {
|
||||||
|
let allocation = this._iconBox.allocation;
|
||||||
|
if (this._iconBottomClip > 0)
|
||||||
this._iconBox.set_clip(0, 0,
|
this._iconBox.set_clip(0, 0,
|
||||||
this._iconBox.width,
|
allocation.x2 - allocation.x1,
|
||||||
this._iconBox.height - bottomClip);
|
allocation.y2 - allocation.y1 - this._iconBottomClip);
|
||||||
else
|
else
|
||||||
this._iconBox.remove_clip();
|
this._iconBox.remove_clip();
|
||||||
},
|
},
|
||||||
@ -535,17 +548,249 @@ AppMenuButton.prototype = {
|
|||||||
|
|
||||||
Signals.addSignalMethods(AppMenuButton.prototype);
|
Signals.addSignalMethods(AppMenuButton.prototype);
|
||||||
|
|
||||||
|
// Activities button. Because everything else in the top bar is a
|
||||||
|
// PanelMenu.Button, it simplifies some things to make this be one too.
|
||||||
|
// We just hack it up to not actually have a menu attached to it.
|
||||||
|
function ActivitiesButton() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
function PanelCorner(side) {
|
ActivitiesButton.prototype = {
|
||||||
this._init(side);
|
__proto__: PanelMenu.Button.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||||
|
|
||||||
|
let container = new Shell.GenericContainer();
|
||||||
|
container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
|
container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
|
container.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
this.actor.child = container;
|
||||||
|
this.actor.name = 'panelActivities';
|
||||||
|
|
||||||
|
/* Translators: If there is no suitable word for "Activities"
|
||||||
|
in your language, you can use the word for "Overview". */
|
||||||
|
this._label = new St.Label({ text: _("Activities") });
|
||||||
|
container.add_actor(this._label);
|
||||||
|
|
||||||
|
this._hotCorner = new Layout.HotCorner();
|
||||||
|
container.add_actor(this._hotCorner.actor);
|
||||||
|
|
||||||
|
// Hack up our menu...
|
||||||
|
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
|
||||||
|
this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
|
||||||
|
this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
|
||||||
|
|
||||||
|
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||||
|
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||||
|
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||||
|
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||||
|
this.actor.add_style_pseudo_class('overview');
|
||||||
|
this._escapeMenuGrab();
|
||||||
|
}));
|
||||||
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||||
|
this.actor.remove_style_pseudo_class('overview');
|
||||||
|
this._escapeMenuGrab();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._xdndTimeOut = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
|
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||||
|
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function(actor, box, flags) {
|
||||||
|
this._label.allocate(box, flags);
|
||||||
|
|
||||||
|
// The hot corner needs to be outside any padding/alignment
|
||||||
|
// that has been imposed on us
|
||||||
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
let hotBox = new Clutter.ActorBox();
|
||||||
|
let ok, x, y;
|
||||||
|
if (actor.get_direction() == St.TextDirection.LTR) {
|
||||||
|
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
|
||||||
|
} else {
|
||||||
|
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
|
||||||
|
// hotCorner.actor has northeast gravity, so we don't need
|
||||||
|
// to adjust x for its width
|
||||||
|
}
|
||||||
|
|
||||||
|
hotBox.x1 = Math.round(x);
|
||||||
|
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
|
||||||
|
hotBox.y1 = Math.round(y);
|
||||||
|
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
|
||||||
|
this._hotCorner.actor.allocate(hotBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
|
if (source != Main.xdndHandler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._xdndTimeOut != 0)
|
||||||
|
Mainloop.source_remove(this._xdndTimeOut);
|
||||||
|
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||||
|
Lang.bind(this, this._xdndShowOverview, actor));
|
||||||
|
},
|
||||||
|
|
||||||
|
_escapeMenuGrab: function() {
|
||||||
|
if (this.menu.isOpen)
|
||||||
|
this.menu.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCapturedEvent: function(actor, event) {
|
||||||
|
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||||
|
if (!this._hotCorner.shouldToggleOverviewOnClick())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMenuOpenRequest: function() {
|
||||||
|
this.menu.isOpen = true;
|
||||||
|
this.menu.emit('open-state-changed', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMenuCloseRequest: function() {
|
||||||
|
this.menu.isOpen = false;
|
||||||
|
this.menu.emit('open-state-changed', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMenuToggleRequest: function() {
|
||||||
|
this.menu.isOpen = !this.menu.isOpen;
|
||||||
|
this.menu.emit('open-state-changed', this.menu.isOpen);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onButtonRelease: function() {
|
||||||
|
if (this.menu.isOpen) {
|
||||||
|
this.menu.close();
|
||||||
|
Main.overview.toggle();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyRelease: function(actor, event) {
|
||||||
|
let symbol = event.get_key_symbol();
|
||||||
|
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
||||||
|
if (this.menu.isOpen)
|
||||||
|
this.menu.close();
|
||||||
|
Main.overview.toggle();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_xdndShowOverview: function(actor) {
|
||||||
|
let [x, y, mask] = global.get_pointer();
|
||||||
|
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||||
|
|
||||||
|
if (pickedActor == this.actor) {
|
||||||
|
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||||
|
Main.overview.showTemporarily();
|
||||||
|
Main.overview.beginItemDrag(actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mainloop.source_remove(this._xdndTimeOut);
|
||||||
|
this._xdndTimeOut = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function PanelCorner(panel, side) {
|
||||||
|
this._init(panel, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelCorner.prototype = {
|
PanelCorner.prototype = {
|
||||||
_init: function(side) {
|
_init: function(box, side) {
|
||||||
this._side = side;
|
this._side = side;
|
||||||
|
|
||||||
|
this._box = box;
|
||||||
|
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
|
||||||
|
|
||||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||||
|
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||||
this.actor.connect('style-changed', Lang.bind(this, this.relayout));
|
},
|
||||||
|
|
||||||
|
_findRightmostButton: function(container) {
|
||||||
|
if (!container.get_children)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let children = container.get_children();
|
||||||
|
|
||||||
|
if (!children || children.length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Start at the back and work backward
|
||||||
|
let index = children.length - 1;
|
||||||
|
while (!children[index].visible && index >= 0)
|
||||||
|
index--;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!(children[index].has_style_class_name('panel-menu')) &&
|
||||||
|
!(children[index].has_style_class_name('panel-button')))
|
||||||
|
return this._findRightmostButton(children[index]);
|
||||||
|
|
||||||
|
return children[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
_findLeftmostButton: function(container) {
|
||||||
|
if (!container.get_children)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let children = container.get_children();
|
||||||
|
|
||||||
|
if (!children || children.length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Start at the front and work forward
|
||||||
|
let index = 0;
|
||||||
|
while (!children[index].visible && index < children.length)
|
||||||
|
index++;
|
||||||
|
|
||||||
|
if (index == children.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!(children[index].has_style_class_name('panel-menu')) &&
|
||||||
|
!(children[index].has_style_class_name('panel-button')))
|
||||||
|
return this._findLeftmostButton(children[index]);
|
||||||
|
|
||||||
|
return children[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
_boxStyleChanged: function() {
|
||||||
|
let button;
|
||||||
|
|
||||||
|
if (this._side == St.Side.LEFT)
|
||||||
|
button = this._findLeftmostButton(this._box);
|
||||||
|
else if (this._side == St.Side.RIGHT)
|
||||||
|
button = this._findRightmostButton(this._box);
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
if (this._button && this._buttonStyleChangedSignalId)
|
||||||
|
this._button.disconnect(this._buttonStyleChangedSignalId);
|
||||||
|
|
||||||
|
this._button = button;
|
||||||
|
|
||||||
|
button.connect('destroy', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
if (this._button == button) {
|
||||||
|
this._button = null;
|
||||||
|
this._buttonStyleChangedSignalId = 0;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Synchronize the locate button's pseudo classes with this corner
|
||||||
|
this._buttonStyleChangedSignalId = button.connect('style-changed', Lang.bind(this,
|
||||||
|
function(actor) {
|
||||||
|
let pseudoClass = button.get_style_pseudo_class();
|
||||||
|
this.actor.set_style_pseudo_class(pseudoClass);
|
||||||
|
}));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_repaint: function() {
|
_repaint: function() {
|
||||||
@ -608,194 +853,17 @@ PanelCorner.prototype = {
|
|||||||
cr.restore();
|
cr.restore();
|
||||||
},
|
},
|
||||||
|
|
||||||
relayout: function() {
|
_styleChanged: function() {
|
||||||
let node = this.actor.get_theme_node();
|
let node = this.actor.get_theme_node();
|
||||||
|
|
||||||
let cornerRadius = node.get_length("-panel-corner-radius");
|
let cornerRadius = node.get_length("-panel-corner-radius");
|
||||||
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
|
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
|
||||||
|
|
||||||
this.actor.set_size(cornerRadius,
|
this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius);
|
||||||
innerBorderWidth + cornerRadius);
|
this.actor.set_anchor_point(0, innerBorderWidth);
|
||||||
if (this._side == St.Side.LEFT)
|
|
||||||
this.actor.set_position(Main.panel.actor.x,
|
|
||||||
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth);
|
|
||||||
else
|
|
||||||
this.actor.set_position(Main.panel.actor.x + Main.panel.actor.width - cornerRadius,
|
|
||||||
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* HotCorner:
|
|
||||||
*
|
|
||||||
* This class manages the "hot corner" that can toggle switching to
|
|
||||||
* overview.
|
|
||||||
*/
|
|
||||||
function HotCorner(button) {
|
|
||||||
this._init(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
HotCorner.prototype = {
|
|
||||||
_init : function(button) {
|
|
||||||
// This is the activities button associated with this hot corner,
|
|
||||||
// if this is on the primary monitor (or null with the corner is
|
|
||||||
// on a different monitor)
|
|
||||||
this._button = button;
|
|
||||||
|
|
||||||
// We use this flag to mark the case where the user has entered the
|
|
||||||
// hot corner and has not left both the hot corner and a surrounding
|
|
||||||
// guard area (the "environs"). This avoids triggering the hot corner
|
|
||||||
// multiple times due to an accidental jitter.
|
|
||||||
this._entered = false;
|
|
||||||
|
|
||||||
this.actor = new Clutter.Group({ width: 3,
|
|
||||||
height: 3,
|
|
||||||
reactive: true });
|
|
||||||
|
|
||||||
this._corner = new Clutter.Rectangle({ width: 1,
|
|
||||||
height: 1,
|
|
||||||
opacity: 0,
|
|
||||||
reactive: true });
|
|
||||||
|
|
||||||
this.actor.add_actor(this._corner);
|
|
||||||
|
|
||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
|
||||||
this._corner.set_position(this.actor.width - this._corner.width, 0);
|
|
||||||
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
|
||||||
} else {
|
|
||||||
this._corner.set_position(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._activationTime = 0;
|
|
||||||
|
|
||||||
this.actor.connect('enter-event',
|
|
||||||
Lang.bind(this, this._onEnvironsEntered));
|
|
||||||
this.actor.connect('leave-event',
|
|
||||||
Lang.bind(this, this._onEnvironsLeft));
|
|
||||||
// Clicking on the hot corner environs should result in the same bahavior
|
|
||||||
// as clicking on the hot corner.
|
|
||||||
this.actor.connect('button-release-event',
|
|
||||||
Lang.bind(this, this._onCornerClicked));
|
|
||||||
|
|
||||||
// In addition to being triggered by the mouse enter event, the hot corner
|
|
||||||
// can be triggered by clicking on it. This is useful if the user wants to
|
|
||||||
// undo the effect of triggering the hot corner once in the hot corner.
|
|
||||||
this._corner.connect('enter-event',
|
|
||||||
Lang.bind(this, this._onCornerEntered));
|
|
||||||
this._corner.connect('button-release-event',
|
|
||||||
Lang.bind(this, this._onCornerClicked));
|
|
||||||
this._corner.connect('leave-event',
|
|
||||||
Lang.bind(this, this._onCornerLeft));
|
|
||||||
|
|
||||||
this._corner._delegate = this._corner;
|
|
||||||
this._corner.handleDragOver = Lang.bind(this,
|
|
||||||
function(source, actor, x, y, time) {
|
|
||||||
if (source == Main.xdndHandler) {
|
|
||||||
if(!Main.overview.visible && !Main.overview.animationInProgress) {
|
|
||||||
this.rippleAnimation();
|
|
||||||
Main.overview.showTemporarily();
|
|
||||||
Main.overview.beginItemDrag(actor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Main.chrome.addActor(this.actor, { visibleInOverview: true, affectsStruts: false });
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy: function() {
|
|
||||||
this.actor.destroy();
|
|
||||||
},
|
|
||||||
|
|
||||||
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
|
|
||||||
// We draw a ripple by using a source image and animating it scaling
|
|
||||||
// outwards and fading away. We want the ripples to move linearly
|
|
||||||
// or it looks unrealistic, but if the opacity of the ripple goes
|
|
||||||
// linearly to zero it fades away too quickly, so we use Tweener's
|
|
||||||
// 'onUpdate' to give a non-linear curve to the fade-away and make
|
|
||||||
// it more visible in the middle section.
|
|
||||||
|
|
||||||
let [x, y] = this._corner.get_transformed_position();
|
|
||||||
let ripple = new St.BoxLayout({ style_class: 'ripple-box',
|
|
||||||
opacity: 255 * Math.sqrt(startOpacity),
|
|
||||||
scale_x: startScale,
|
|
||||||
scale_y: startScale,
|
|
||||||
x: x,
|
|
||||||
y: y });
|
|
||||||
ripple._opacity = startOpacity;
|
|
||||||
if (ripple.get_direction() == St.TextDirection.RTL)
|
|
||||||
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
|
||||||
Tweener.addTween(ripple, { _opacity: finalOpacity,
|
|
||||||
scale_x: finalScale,
|
|
||||||
scale_y: finalScale,
|
|
||||||
delay: delay,
|
|
||||||
time: time,
|
|
||||||
transition: 'linear',
|
|
||||||
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
|
|
||||||
onComplete: function() { ripple.destroy(); } });
|
|
||||||
Main.uiGroup.add_actor(ripple);
|
|
||||||
},
|
|
||||||
|
|
||||||
rippleAnimation: function() {
|
|
||||||
// Show three concentric ripples expanding outwards; the exact
|
|
||||||
// parameters were found by trial and error, so don't look
|
|
||||||
// for them to make perfect sense mathematically
|
|
||||||
|
|
||||||
// delay time scale opacity => scale opacity
|
|
||||||
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
|
|
||||||
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
|
|
||||||
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onEnvironsEntered : function() {
|
|
||||||
if (this._button)
|
|
||||||
this._button.hover = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCornerEntered : function() {
|
|
||||||
if (!this._entered) {
|
|
||||||
this._entered = true;
|
|
||||||
if (!Main.overview.animationInProgress) {
|
|
||||||
this._activationTime = Date.now() / 1000;
|
|
||||||
|
|
||||||
this.rippleAnimation();
|
|
||||||
Main.overview.toggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCornerClicked : function() {
|
|
||||||
if (!Main.overview.animationInProgress)
|
|
||||||
this.maybeToggleOverviewOnClick();
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCornerLeft : function(actor, event) {
|
|
||||||
if (event.get_related() != this.actor)
|
|
||||||
this._entered = false;
|
|
||||||
// Consume event, otherwise this will confuse onEnvironsLeft
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onEnvironsLeft : function(actor, event) {
|
|
||||||
if (this._button)
|
|
||||||
this._button.hover = false;
|
|
||||||
|
|
||||||
if (event.get_related() != this._corner)
|
|
||||||
this._entered = false;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
|
|
||||||
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
|
|
||||||
// and clicked the Activities button.
|
|
||||||
maybeToggleOverviewOnClick: function() {
|
|
||||||
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
|
|
||||||
Main.overview.toggle();
|
|
||||||
this._activationTime = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function Panel() {
|
function Panel() {
|
||||||
this._init();
|
this._init();
|
||||||
@ -803,11 +871,12 @@ function Panel() {
|
|||||||
|
|
||||||
Panel.prototype = {
|
Panel.prototype = {
|
||||||
_init : function() {
|
_init : function() {
|
||||||
this.actor = new St.BoxLayout({ style_class: 'menu-bar',
|
this.actor = new Shell.GenericContainer({ name: 'panel',
|
||||||
name: 'panel',
|
reactive: true });
|
||||||
reactive: true });
|
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
|
||||||
|
this._statusArea = {};
|
||||||
|
|
||||||
Main.overview.connect('shown', Lang.bind(this, function () {
|
Main.overview.connect('shown', Lang.bind(this, function () {
|
||||||
this.actor.add_style_class_name('in-overview');
|
this.actor.add_style_class_name('in-overview');
|
||||||
}));
|
}));
|
||||||
@ -815,130 +884,60 @@ Panel.prototype = {
|
|||||||
this.actor.remove_style_class_name('in-overview');
|
this.actor.remove_style_class_name('in-overview');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._leftPointerBarrier = 0;
|
if (global.session_type == Shell.SessionType.GDM) {
|
||||||
this._rightPointerBarrier = 0;
|
this._tray_icon_order = GDM_TRAY_ICON_ORDER;
|
||||||
|
this._tray_icon_shell_implementation = GDM_TRAY_ICON_SHELL_IMPLEMENTATION;
|
||||||
|
} else {
|
||||||
|
this._tray_icon_order = STANDARD_TRAY_ICON_ORDER;
|
||||||
|
this._tray_icon_shell_implementation = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION;
|
||||||
|
}
|
||||||
|
|
||||||
this._menus = new PopupMenu.PopupMenuManager(this);
|
this._menus = new PopupMenu.PopupMenuManager(this);
|
||||||
|
|
||||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||||
|
this.actor.add_actor(this._leftBox);
|
||||||
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
||||||
|
this.actor.add_actor(this._centerBox);
|
||||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||||
|
this.actor.add_actor(this._rightBox);
|
||||||
|
|
||||||
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
if (this.actor.get_direction() == St.TextDirection.RTL)
|
||||||
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
||||||
|
else
|
||||||
|
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
||||||
|
|
||||||
/* This box container ensures that the centerBox is positioned in the *absolute*
|
this.actor.add_actor(this._leftCorner.actor);
|
||||||
* center, but can be pushed aside if necessary. */
|
|
||||||
this._boxContainer = new Shell.GenericContainer();
|
|
||||||
this.actor.add(this._boxContainer, { expand: true });
|
|
||||||
this._boxContainer.add_actor(this._leftBox);
|
|
||||||
this._boxContainer.add_actor(this._centerBox);
|
|
||||||
this._boxContainer.add_actor(this._rightBox);
|
|
||||||
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
|
|
||||||
let children = box.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
|
|
||||||
alloc.min_size += childMin;
|
|
||||||
alloc.natural_size += childNatural;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
|
|
||||||
let children = box.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
|
|
||||||
if (childMin > alloc.min_size)
|
|
||||||
alloc.min_size = childMin;
|
|
||||||
if (childNatural > alloc.natural_size)
|
|
||||||
alloc.natural_size = childNatural;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
|
|
||||||
let allocWidth = box.x2 - box.x1;
|
|
||||||
let allocHeight = box.y2 - box.y1;
|
|
||||||
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
|
|
||||||
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
|
|
||||||
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
|
|
||||||
|
|
||||||
let sideWidth, centerWidth;
|
if (this.actor.get_direction() == St.TextDirection.RTL)
|
||||||
centerWidth = centerNaturalWidth;
|
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
||||||
sideWidth = (allocWidth - centerWidth) / 2;
|
else
|
||||||
|
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
||||||
|
this.actor.add_actor(this._rightCorner.actor);
|
||||||
|
|
||||||
let childBox = new Clutter.ActorBox();
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
childBox.y1 = 0;
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
childBox.y2 = allocHeight;
|
|
||||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
|
||||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
|
||||||
leftNaturalWidth);
|
|
||||||
childBox.x2 = allocWidth;
|
|
||||||
} else {
|
|
||||||
childBox.x1 = 0;
|
|
||||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
|
||||||
leftNaturalWidth);
|
|
||||||
}
|
|
||||||
this._leftBox.allocate(childBox, flags);
|
|
||||||
|
|
||||||
childBox.x1 = Math.ceil(sideWidth);
|
|
||||||
childBox.y1 = 0;
|
|
||||||
childBox.x2 = childBox.x1 + centerWidth;
|
|
||||||
childBox.y2 = allocHeight;
|
|
||||||
this._centerBox.allocate(childBox, flags);
|
|
||||||
|
|
||||||
childBox.y1 = 0;
|
|
||||||
childBox.y2 = allocHeight;
|
|
||||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
|
||||||
childBox.x1 = 0;
|
|
||||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
|
||||||
rightNaturalWidth);
|
|
||||||
} else {
|
|
||||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
|
||||||
rightNaturalWidth);
|
|
||||||
childBox.x2 = allocWidth;
|
|
||||||
}
|
|
||||||
this._rightBox.allocate(childBox, flags);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/* Button on the left side of the panel. */
|
/* Button on the left side of the panel. */
|
||||||
/* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
|
if (global.session_type == Shell.SessionType.USER) {
|
||||||
let label = new St.Label({ text: _("Activities") });
|
this._activitiesButton = new ActivitiesButton();
|
||||||
this.button = new St.Button({ name: 'panelActivities',
|
this._activities = this._activitiesButton.actor;
|
||||||
style_class: 'panel-button',
|
this._leftBox.add(this._activities);
|
||||||
reactive: true,
|
|
||||||
can_focus: true });
|
|
||||||
this.button.set_child(label);
|
|
||||||
this.button._delegate = this.button;
|
|
||||||
this.button._xdndTimeOut = 0;
|
|
||||||
this.button.handleDragOver = Lang.bind(this,
|
|
||||||
function(source, actor, x, y, time) {
|
|
||||||
if (source == Main.xdndHandler) {
|
|
||||||
if (this.button._xdndTimeOut != 0)
|
|
||||||
Mainloop.source_remove(this.button._xdndTimeOut);
|
|
||||||
this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
|
||||||
Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._xdndShowOverview(actor);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._leftBox.add(this.button);
|
|
||||||
|
|
||||||
// Synchronize the buttons pseudo classes with its corner
|
// The activities button has a pretend menu, so as to integrate
|
||||||
this.button.connect('style-changed', Lang.bind(this,
|
// more cleanly with the rest of the panel
|
||||||
function(actor) {
|
this._menus.addMenu(this._activitiesButton.menu);
|
||||||
let rtl = actor.get_direction() == St.TextDirection.RTL;
|
|
||||||
let corner = rtl ? this._rightCorner : this._leftCorner;
|
|
||||||
let pseudoClass = actor.get_style_pseudo_class();
|
|
||||||
corner.actor.set_style_pseudo_class(pseudoClass);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._hotCorner = null;
|
this._appMenu = new AppMenuButton();
|
||||||
|
this._leftBox.add(this._appMenu.actor);
|
||||||
let appMenuButton = new AppMenuButton();
|
this._menus.addMenu(this._appMenu.menu);
|
||||||
this._leftBox.add(appMenuButton.actor);
|
}
|
||||||
|
|
||||||
this._menus.addMenu(appMenuButton.menu);
|
|
||||||
|
|
||||||
/* center */
|
/* center */
|
||||||
this._dateMenu = new DateMenu.DateMenuButton();
|
if (global.session_type == Shell.SessionType.USER)
|
||||||
|
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
|
||||||
|
else
|
||||||
|
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
|
||||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
||||||
this._menus.addMenu(this._dateMenu.menu);
|
this._menus.addMenu(this._dateMenu.menu);
|
||||||
|
|
||||||
@ -954,159 +953,145 @@ Panel.prototype = {
|
|||||||
this._rightBox.add(this._trayBox);
|
this._rightBox.add(this._trayBox);
|
||||||
this._rightBox.add(this._statusBox);
|
this._rightBox.add(this._statusBox);
|
||||||
|
|
||||||
this._statusmenu = new StatusMenu.StatusMenuButton();
|
if (global.session_type == Shell.SessionType.USER) {
|
||||||
this._statusmenu.actor.name = 'panelStatus';
|
this._userMenu = new UserMenu.UserMenuButton();
|
||||||
this._rightBox.add(this._statusmenu.actor);
|
this._userMenu.actor.name = 'panelStatus';
|
||||||
|
this._rightBox.add(this._userMenu.actor);
|
||||||
// Synchronize the buttons pseudo classes with its corner
|
}
|
||||||
this._statusmenu.actor.connect('style-changed', Lang.bind(this,
|
|
||||||
function(actor) {
|
|
||||||
let rtl = actor.get_direction() == St.TextDirection.RTL;
|
|
||||||
let corner = rtl ? this._leftCorner : this._rightCorner;
|
|
||||||
let pseudoClass = actor.get_style_pseudo_class();
|
|
||||||
corner.actor.set_style_pseudo_class(pseudoClass);
|
|
||||||
}));
|
|
||||||
|
|
||||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||||
|
|
||||||
// TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
|
Main.layoutManager.panelBox.add(this.actor);
|
||||||
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
|
|
||||||
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
|
|
||||||
// to switch to.
|
|
||||||
this.button.connect('clicked', Lang.bind(this, function(b) {
|
|
||||||
if (!Main.overview.animationInProgress) {
|
|
||||||
this._hotCorner.maybeToggleOverviewOnClick();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
|
|
||||||
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
|
|
||||||
// and to be released when it is exited regardless of how it was triggered.
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
|
||||||
this.button.checked = true;
|
|
||||||
}));
|
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
|
||||||
this.button.checked = false;
|
|
||||||
}));
|
|
||||||
|
|
||||||
Main.chrome.addActor(this.actor, { visibleInOverview: true });
|
|
||||||
Main.chrome.addActor(this._leftCorner.actor, { visibleInOverview: true,
|
|
||||||
affectsStruts: false,
|
|
||||||
affectsInputRegion: false });
|
|
||||||
Main.chrome.addActor(this._rightCorner.actor, { visibleInOverview: true,
|
|
||||||
affectsStruts: false,
|
|
||||||
affectsInputRegion: false });
|
|
||||||
|
|
||||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
||||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||||
},
|
},
|
||||||
|
|
||||||
_xdndShowOverview: function (actor) {
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
let [x, y, mask] = global.get_pointer();
|
alloc.min_size = -1;
|
||||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
|
||||||
|
|
||||||
if (pickedActor != this.button) {
|
|
||||||
Mainloop.source_remove(this.button._xdndTimeOut);
|
|
||||||
this.button._xdndTimeOut = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Main.overview.visible && !Main.overview.animationInProgress) {
|
|
||||||
Main.overview.showTemporarily();
|
|
||||||
Main.overview.beginItemDrag(actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mainloop.source_remove(this.button._xdndTimeOut);
|
|
||||||
this.button._xdndTimeOut = 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||||
|
// We don't need to implement this; it's forced by the CSS
|
||||||
|
alloc.min_size = -1;
|
||||||
|
alloc.natural_size = -1;
|
||||||
|
},
|
||||||
|
|
||||||
// While there can be multiple hotcorners (one per monitor), the hot corner
|
_allocate: function(actor, box, flags) {
|
||||||
// that is on top of the Activities button is special since it needs special
|
let allocWidth = box.x2 - box.x1;
|
||||||
// coordination with clicking on that button
|
let allocHeight = box.y2 - box.y1;
|
||||||
setHotCorner: function(corner) {
|
|
||||||
this._hotCorner = corner;
|
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
|
||||||
|
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
|
||||||
|
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
|
||||||
|
|
||||||
|
let sideWidth, centerWidth;
|
||||||
|
centerWidth = centerNaturalWidth;
|
||||||
|
sideWidth = (allocWidth - centerWidth) / 2;
|
||||||
|
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
|
||||||
|
childBox.y1 = 0;
|
||||||
|
childBox.y2 = allocHeight;
|
||||||
|
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||||
|
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||||
|
leftNaturalWidth);
|
||||||
|
childBox.x2 = allocWidth;
|
||||||
|
} else {
|
||||||
|
childBox.x1 = 0;
|
||||||
|
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||||
|
leftNaturalWidth);
|
||||||
|
}
|
||||||
|
this._leftBox.allocate(childBox, flags);
|
||||||
|
|
||||||
|
childBox.x1 = Math.ceil(sideWidth);
|
||||||
|
childBox.y1 = 0;
|
||||||
|
childBox.x2 = childBox.x1 + centerWidth;
|
||||||
|
childBox.y2 = allocHeight;
|
||||||
|
this._centerBox.allocate(childBox, flags);
|
||||||
|
|
||||||
|
childBox.y1 = 0;
|
||||||
|
childBox.y2 = allocHeight;
|
||||||
|
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||||
|
childBox.x1 = 0;
|
||||||
|
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||||
|
rightNaturalWidth);
|
||||||
|
} else {
|
||||||
|
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||||
|
rightNaturalWidth);
|
||||||
|
childBox.x2 = allocWidth;
|
||||||
|
}
|
||||||
|
this._rightBox.allocate(childBox, flags);
|
||||||
|
|
||||||
|
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
||||||
|
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
|
||||||
|
childBox.x1 = 0;
|
||||||
|
childBox.x2 = cornerWidth;
|
||||||
|
childBox.y1 = allocHeight;
|
||||||
|
childBox.y2 = allocHeight + cornerHeight;
|
||||||
|
this._leftCorner.actor.allocate(childBox, flags);
|
||||||
|
|
||||||
|
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
||||||
|
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
|
||||||
|
childBox.x1 = allocWidth - cornerWidth;
|
||||||
|
childBox.x2 = allocWidth;
|
||||||
|
childBox.y1 = allocHeight;
|
||||||
|
childBox.y2 = allocHeight + cornerHeight;
|
||||||
|
this._rightCorner.actor.allocate(childBox, flags);
|
||||||
},
|
},
|
||||||
|
|
||||||
startStatusArea: function() {
|
startStatusArea: function() {
|
||||||
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
|
for (let i = 0; i < this._tray_icon_order.length; i++) {
|
||||||
let role = STANDARD_TRAY_ICON_ORDER[i];
|
let role = this._tray_icon_order[i];
|
||||||
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
|
let constructor = this._tray_icon_shell_implementation[role];
|
||||||
if (!constructor) {
|
if (!constructor) {
|
||||||
// This icon is not implemented (this is a bug)
|
// This icon is not implemented (this is a bug)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let indicator = new constructor();
|
let indicator = new constructor();
|
||||||
this._statusBox.add(indicator.actor);
|
this.addToStatusArea(role, indicator, i);
|
||||||
this._menus.addMenu(indicator.menu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopupMenuManager depends on menus being added in order for
|
// PopupMenuManager depends on menus being added in order for
|
||||||
// keyboard navigation
|
// keyboard navigation
|
||||||
this._menus.addMenu(this._statusmenu.menu);
|
if (this._userMenu)
|
||||||
|
this._menus.addMenu(this._userMenu.menu);
|
||||||
},
|
},
|
||||||
|
|
||||||
startupAnimation: function() {
|
addToStatusArea: function(role, indicator, position) {
|
||||||
let oldY = this.actor.y;
|
if (this._statusArea[role])
|
||||||
this.actor.y = oldY - this.actor.height;
|
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ y: oldY,
|
|
||||||
time: STARTUP_ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
|
|
||||||
let oldCornerY = this._leftCorner.actor.y;
|
if (!(indicator instanceof PanelMenu.Button))
|
||||||
this._leftCorner.actor.y = oldCornerY - this.actor.height;
|
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
||||||
this._rightCorner.actor.y = oldCornerY - this.actor.height;
|
|
||||||
Tweener.addTween(this._leftCorner.actor,
|
|
||||||
{ y: oldCornerY,
|
|
||||||
time: STARTUP_ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
Tweener.addTween(this._rightCorner.actor,
|
|
||||||
{ y: oldCornerY,
|
|
||||||
time: STARTUP_ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
relayout: function() {
|
if (!position)
|
||||||
let primary = global.get_primary_monitor();
|
position = 0;
|
||||||
|
|
||||||
this.actor.set_position(primary.x, primary.y);
|
this._statusBox.insert_actor(indicator.actor, position);
|
||||||
this.actor.set_size(primary.width, -1);
|
this._menus.addMenu(indicator.menu);
|
||||||
|
|
||||||
if (this._leftPointerBarrier)
|
this._statusArea[role] = indicator;
|
||||||
global.destroy_pointer_barrier(this._leftPointerBarrier);
|
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||||
if (this._rightPointerBarrier)
|
this._statusArea[role] = null;
|
||||||
global.destroy_pointer_barrier(this._rightPointerBarrier);
|
emitter.disconnect(destroyId);
|
||||||
|
}));
|
||||||
|
|
||||||
this._leftPointerBarrier =
|
return indicator;
|
||||||
global.create_pointer_barrier(primary.x, primary.y,
|
|
||||||
primary.x, primary.y + this.actor.height,
|
|
||||||
1 /* BarrierPositiveX */);
|
|
||||||
this._rightPointerBarrier =
|
|
||||||
global.create_pointer_barrier(primary.x + primary.width, primary.y,
|
|
||||||
primary.x + primary.width, primary.y + this.actor.height,
|
|
||||||
4 /* BarrierNegativeX */);
|
|
||||||
|
|
||||||
this._leftCorner.relayout();
|
|
||||||
this._rightCorner.relayout();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onTrayIconAdded: function(o, icon, role) {
|
_onTrayIconAdded: function(o, icon, role) {
|
||||||
icon.height = PANEL_ICON_SIZE;
|
icon.height = PANEL_ICON_SIZE;
|
||||||
|
|
||||||
if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
|
if (this._tray_icon_shell_implementation[role]) {
|
||||||
// This icon is legacy, and replaced by a Shell version
|
// This icon is legacy, and replaced by a Shell version
|
||||||
// Hide it
|
// Hide it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Figure out the index in our well-known order for this icon
|
// Figure out the index in our well-known order for this icon
|
||||||
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
|
let position = this._tray_icon_order.indexOf(role);
|
||||||
icon._rolePosition = position;
|
icon._rolePosition = position;
|
||||||
let children = this._trayBox.get_children();
|
let children = this._trayBox.get_children();
|
||||||
let i;
|
let i;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
@ -23,11 +24,11 @@ Button.prototype = {
|
|||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
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.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
|
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
|
||||||
|
this.menu.actor.add_style_class_name('panel-menu');
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||||
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
||||||
Main.chrome.addActor(this.menu.actor, { visibleInOverview: true,
|
Main.uiGroup.add_actor(this.menu.actor);
|
||||||
affectsStruts: false });
|
|
||||||
this.menu.actor.hide();
|
this.menu.actor.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ Button.prototype = {
|
|||||||
// Setting the max-height won't do any good if the minimum height of the
|
// Setting the max-height won't do any good if the minimum height of the
|
||||||
// menu is higher then the screen; it's useful if part of the menu is
|
// menu is higher then the screen; it's useful if part of the menu is
|
||||||
// scrollable so the minimum height is smaller than the natural height
|
// scrollable so the minimum height is smaller than the natural height
|
||||||
let monitor = global.get_primary_monitor();
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
this.menu.actor.style = ('max-height: ' +
|
this.menu.actor.style = ('max-height: ' +
|
||||||
Math.round(monitor.height - Main.panel.actor.height) +
|
Math.round(monitor.height - Main.panel.actor.height) +
|
||||||
'px;');
|
'px;');
|
||||||
@ -80,8 +81,18 @@ Button.prototype = {
|
|||||||
this.actor.add_style_pseudo_class('active');
|
this.actor.add_style_pseudo_class('active');
|
||||||
else
|
else
|
||||||
this.actor.remove_style_pseudo_class('active');
|
this.actor.remove_style_pseudo_class('active');
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.actor._delegate = null;
|
||||||
|
|
||||||
|
this.menu.destroy();
|
||||||
|
this.actor.destroy();
|
||||||
|
|
||||||
|
this.emit('destroy');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Signals.addSignalMethods(Button.prototype);
|
||||||
|
|
||||||
/* SystemStatusButton:
|
/* SystemStatusButton:
|
||||||
*
|
*
|
||||||
|
@ -7,8 +7,6 @@ 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 St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -62,13 +60,13 @@ PlaceInfo.prototype = {
|
|||||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
// Helper function to translate launch parameters into a GAppLaunchContext
|
||||||
function _makeLaunchContext(params)
|
function _makeLaunchContext(params)
|
||||||
{
|
{
|
||||||
params = Params.parse(params, { workspace: null,
|
params = Params.parse(params, { workspace: -1,
|
||||||
timestamp: null });
|
timestamp: 0 });
|
||||||
|
|
||||||
let launchContext = global.create_app_launch_context();
|
let launchContext = global.create_app_launch_context();
|
||||||
if (params.workspace != null)
|
if (params.workspace != -1)
|
||||||
launchContext.set_desktop(params.workspace.index());
|
launchContext.set_desktop(params.workspace);
|
||||||
if (params.timestamp != null)
|
if (params.timestamp != 0)
|
||||||
launchContext.set_timestamp(params.timestamp);
|
launchContext.set_timestamp(params.timestamp);
|
||||||
|
|
||||||
return launchContext;
|
return launchContext;
|
||||||
@ -120,9 +118,9 @@ PlaceDeviceInfo.prototype = {
|
|||||||
this._mount.unmount_finish(res);
|
this._mount.unmount_finish(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let message = _("Failed to unmount '%s'").format(o.get_name());
|
let message = _("Failed to unmount '%s'").format(o.get_name());
|
||||||
Main.overview.shellInfo.setMessage(message,
|
Main.overview.setMessage(message,
|
||||||
Lang.bind(this, this.remove),
|
Lang.bind(this, this.remove),
|
||||||
_("Retry"));
|
_("Retry"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -270,10 +268,7 @@ PlacesManager.prototype = {
|
|||||||
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
|
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
|
||||||
|
|
||||||
if (!success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let bookmarks = bookmarksContent.split('\n');
|
let bookmarks = bookmarksContent.split('\n');
|
||||||
|
|
||||||
|
@ -22,13 +22,11 @@
|
|||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const AccountsService = imports.gi.AccountsService;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Gdm = imports.gi.Gdm;
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Polkit = imports.gi.Polkit;
|
const Polkit = imports.gi.Polkit;
|
||||||
@ -94,7 +92,7 @@ AuthenticationDialog.prototype = {
|
|||||||
|
|
||||||
let userName = userNames[0];
|
let userName = userNames[0];
|
||||||
|
|
||||||
this._user = Gdm.UserManager.ref_default().get_user(userName);
|
this._user = AccountsService.UserManager.get_default().get_user(userName);
|
||||||
let userRealName = this._user.get_real_name()
|
let userRealName = this._user.get_real_name()
|
||||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||||
Lang.bind(this, this._onUserChanged));
|
Lang.bind(this, this._onUserChanged));
|
||||||
|
@ -13,11 +13,19 @@ 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 Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||||
|
|
||||||
|
function _ensureStyle(actor) {
|
||||||
|
if (actor.get_children) {
|
||||||
|
let children = actor.get_children();
|
||||||
|
for (let i = 0; i < children.length; i++)
|
||||||
|
_ensureStyle(children[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor instanceof St.Widget)
|
||||||
|
actor.ensure_style();
|
||||||
|
}
|
||||||
|
|
||||||
function PopupBaseMenuItem(params) {
|
function PopupBaseMenuItem(params) {
|
||||||
this._init(params);
|
this._init(params);
|
||||||
}
|
}
|
||||||
@ -498,7 +506,7 @@ PopupSliderMenuItem.prototype = {
|
|||||||
this._slider = new St.DrawingArea({ style_class: 'popup-slider-menu-item', reactive: true });
|
this._slider = new St.DrawingArea({ style_class: 'popup-slider-menu-item', reactive: true });
|
||||||
this.addActor(this._slider, { span: -1, expand: true });
|
this.addActor(this._slider, { span: -1, expand: true });
|
||||||
this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
||||||
this._slider.connect('button-press-event', Lang.bind(this, this._startDragging));
|
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
|
||||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
|
|
||||||
this._releaseId = this._motionId = 0;
|
this._releaseId = this._motionId = 0;
|
||||||
@ -707,11 +715,36 @@ PopupSwitchMenuItem.prototype = {
|
|||||||
this._switch = new Switch(active);
|
this._switch = new Switch(active);
|
||||||
|
|
||||||
this.addActor(this.label);
|
this.addActor(this.label);
|
||||||
this.addActor(this._switch.actor, { align: St.Align.END });
|
|
||||||
|
|
||||||
this.connect('activate', Lang.bind(this,function(from) {
|
this._statusBin = new St.Bin({ x_align: St.Align.END });
|
||||||
|
this.addActor(this._statusBin,
|
||||||
|
{ expand: true, span: -1, align: St.Align.END });
|
||||||
|
|
||||||
|
this._statusLabel = new St.Label({ text: '',
|
||||||
|
style_class: 'popup-inactive-menu-item'
|
||||||
|
});
|
||||||
|
this._statusBin.child = this._switch.actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
setStatus: function(text) {
|
||||||
|
if (text != null) {
|
||||||
|
this._statusLabel.text = text;
|
||||||
|
this._statusBin.child = this._statusLabel;
|
||||||
|
this.actor.reactive = false;
|
||||||
|
this.actor.can_focus = false;
|
||||||
|
} else {
|
||||||
|
this._statusBin.child = this._switch.actor;
|
||||||
|
this.actor.reactive = true;
|
||||||
|
this.actor.can_focus = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function(event) {
|
||||||
|
if (this._switch.actor.mapped) {
|
||||||
this.toggle();
|
this.toggle();
|
||||||
}));
|
}
|
||||||
|
|
||||||
|
PopupBaseMenuItem.prototype.activate.call(this, event);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
@ -766,6 +799,7 @@ PopupMenuBase.prototype = {
|
|||||||
this.box = new St.BoxLayout({ vertical: true });
|
this.box = new St.BoxLayout({ vertical: true });
|
||||||
}
|
}
|
||||||
this.box.connect_after('queue-relayout', Lang.bind(this, this._menuQueueRelayout));
|
this.box.connect_after('queue-relayout', Lang.bind(this, this._menuQueueRelayout));
|
||||||
|
this.length = 0;
|
||||||
|
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
|
|
||||||
@ -778,14 +812,58 @@ PopupMenuBase.prototype = {
|
|||||||
this.passEvents = false;
|
this.passEvents = false;
|
||||||
|
|
||||||
this._activeMenuItem = null;
|
this._activeMenuItem = null;
|
||||||
|
this._childMenus = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
addAction: function(title, callback) {
|
addAction: function(title, callback) {
|
||||||
var menuItem = new PopupMenuItem(title);
|
let menuItem = new PopupMenuItem(title);
|
||||||
this.addMenuItem(menuItem);
|
this.addMenuItem(menuItem);
|
||||||
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
||||||
callback(event);
|
callback(event);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
return menuItem;
|
||||||
|
},
|
||||||
|
|
||||||
|
addSettingsAction: function(title, desktopFile) {
|
||||||
|
// Don't allow user settings to get edited unless we're in a user session
|
||||||
|
if (global.session_type != Shell.SessionType.USER)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let menuItem = this.addAction(title, function() {
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Main.overview.hide();
|
||||||
|
app.activate();
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
},
|
||||||
|
|
||||||
|
isChildMenu: function(menu) {
|
||||||
|
return this._childMenus.indexOf(menu) != -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
addChildMenu: function(menu) {
|
||||||
|
if (this.isChildMenu(menu))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._childMenus.push(menu);
|
||||||
|
this.emit('child-menu-added', menu);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeChildMenu: function(menu) {
|
||||||
|
let index = this._childMenus.indexOf(menu);
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._childMenus.splice(index, 1);
|
||||||
|
this.emit('child-menu-removed', menu);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -838,6 +916,39 @@ PopupMenuBase.prototype = {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateSeparatorVisibility: function(menuItem) {
|
||||||
|
let children = this.box.get_children();
|
||||||
|
|
||||||
|
let index = children.indexOf(menuItem.actor);
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let childBeforeIndex = index - 1;
|
||||||
|
|
||||||
|
while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible)
|
||||||
|
childBeforeIndex--;
|
||||||
|
|
||||||
|
if (childBeforeIndex < 0
|
||||||
|
|| children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) {
|
||||||
|
menuItem.actor.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childAfterIndex = index + 1;
|
||||||
|
|
||||||
|
while (childAfterIndex < children.length && !children[childAfterIndex].visible)
|
||||||
|
childAfterIndex++;
|
||||||
|
|
||||||
|
if (childAfterIndex >= children.length
|
||||||
|
|| children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) {
|
||||||
|
menuItem.actor.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItem.actor.show();
|
||||||
|
},
|
||||||
|
|
||||||
addMenuItem: function(menuItem, position) {
|
addMenuItem: function(menuItem, position) {
|
||||||
let before_item = null;
|
let before_item = null;
|
||||||
if (position == undefined) {
|
if (position == undefined) {
|
||||||
@ -855,6 +966,8 @@ PopupMenuBase.prototype = {
|
|||||||
menuItem.connect('destroy', Lang.bind(this, function() {
|
menuItem.connect('destroy', Lang.bind(this, function() {
|
||||||
menuItem.disconnect(menuItem._subMenuActivateId);
|
menuItem.disconnect(menuItem._subMenuActivateId);
|
||||||
menuItem.disconnect(menuItem._subMenuActiveChangeId);
|
menuItem.disconnect(menuItem._subMenuActiveChangeId);
|
||||||
|
|
||||||
|
this.length--;
|
||||||
}));
|
}));
|
||||||
} else if (menuItem instanceof PopupSubMenuMenuItem) {
|
} else if (menuItem instanceof PopupSubMenuMenuItem) {
|
||||||
if (before_item == null)
|
if (before_item == null)
|
||||||
@ -867,10 +980,20 @@ PopupMenuBase.prototype = {
|
|||||||
if (!open)
|
if (!open)
|
||||||
menuItem.menu.close(false);
|
menuItem.menu.close(false);
|
||||||
});
|
});
|
||||||
|
} else if (menuItem instanceof PopupSeparatorMenuItem) {
|
||||||
|
this._connectItemSignals(menuItem);
|
||||||
|
|
||||||
|
// updateSeparatorVisibility needs to get called any time the
|
||||||
|
// separator's adjacent siblings change visibility or position.
|
||||||
|
// open-state-changed isn't exactly that, but doing it in more
|
||||||
|
// precise ways would require a lot more bookkeeping.
|
||||||
|
this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); }));
|
||||||
} else if (menuItem instanceof PopupBaseMenuItem)
|
} else if (menuItem instanceof PopupBaseMenuItem)
|
||||||
this._connectItemSignals(menuItem);
|
this._connectItemSignals(menuItem);
|
||||||
else
|
else
|
||||||
throw TypeError("Invalid argument to PopupMenuBase.addMenuItem()");
|
throw TypeError("Invalid argument to PopupMenuBase.addMenuItem()");
|
||||||
|
|
||||||
|
this.length++;
|
||||||
},
|
},
|
||||||
|
|
||||||
getColumnWidths: function() {
|
getColumnWidths: function() {
|
||||||
@ -920,6 +1043,18 @@ PopupMenuBase.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get firstMenuItem() {
|
||||||
|
let items = this._getMenuItems();
|
||||||
|
if (items.length)
|
||||||
|
return items[0];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
get numMenuItems() {
|
||||||
|
return this._getMenuItems().length;
|
||||||
|
},
|
||||||
|
|
||||||
removeAll: function() {
|
removeAll: function() {
|
||||||
let children = this._getMenuItems();
|
let children = this._getMenuItems();
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
@ -951,12 +1086,11 @@ function PopupMenu() {
|
|||||||
PopupMenu.prototype = {
|
PopupMenu.prototype = {
|
||||||
__proto__: PopupMenuBase.prototype,
|
__proto__: PopupMenuBase.prototype,
|
||||||
|
|
||||||
_init: function(sourceActor, alignment, arrowSide, gap) {
|
_init: function(sourceActor, alignment, arrowSide) {
|
||||||
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
|
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
|
||||||
|
|
||||||
this._alignment = alignment;
|
this._alignment = alignment;
|
||||||
this._arrowSide = arrowSide;
|
this._arrowSide = arrowSide;
|
||||||
this._gap = gap;
|
|
||||||
|
|
||||||
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
|
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
@ -1014,9 +1148,11 @@ PopupMenu.prototype = {
|
|||||||
|
|
||||||
this.isOpen = true;
|
this.isOpen = true;
|
||||||
|
|
||||||
this._boxPointer.setPosition(this.sourceActor, this._gap, this._alignment);
|
this._boxPointer.setPosition(this.sourceActor, this._alignment);
|
||||||
this._boxPointer.show(animate);
|
this._boxPointer.show(animate);
|
||||||
|
|
||||||
|
this.actor.raise_top();
|
||||||
|
|
||||||
this.emit('open-state-changed', true);
|
this.emit('open-state-changed', true);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1284,6 +1420,193 @@ PopupSubMenuMenuItem.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function PopupComboMenu() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupComboMenu.prototype = {
|
||||||
|
__proto__: PopupMenuBase.prototype,
|
||||||
|
|
||||||
|
_init: function(sourceActor) {
|
||||||
|
PopupMenuBase.prototype._init.call(this,
|
||||||
|
sourceActor, 'popup-combo-menu');
|
||||||
|
this.actor = this.box;
|
||||||
|
this.actor._delegate = this;
|
||||||
|
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
|
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||||
|
this._activeItemPos = -1;
|
||||||
|
global.focus_manager.add_group(this.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyPressEvent: function(actor, event) {
|
||||||
|
if (event.get_key_symbol() == Clutter.Escape) {
|
||||||
|
this.close(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyFocusIn: function(actor) {
|
||||||
|
let items = this._getMenuItems();
|
||||||
|
let activeItem = items[this._activeItemPos];
|
||||||
|
activeItem.actor.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function() {
|
||||||
|
if (this.isOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.isOpen = true;
|
||||||
|
|
||||||
|
let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
|
||||||
|
let items = this._getMenuItems();
|
||||||
|
let activeItem = items[this._activeItemPos];
|
||||||
|
|
||||||
|
this.actor.set_position(sourceX, sourceY - activeItem.actor.y);
|
||||||
|
this.actor.width = Math.max(this.actor.width, this.sourceActor.width);
|
||||||
|
this.actor.raise_top();
|
||||||
|
|
||||||
|
this.actor.opacity = 0;
|
||||||
|
this.actor.show();
|
||||||
|
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
transition: 'linear',
|
||||||
|
time: BoxPointer.POPUP_ANIMATION_TIME });
|
||||||
|
|
||||||
|
this.emit('open-state-changed', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function() {
|
||||||
|
if (!this.isOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.isOpen = false;
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
transition: 'linear',
|
||||||
|
time: BoxPointer.POPUP_ANIMATION_TIME,
|
||||||
|
onComplete: Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.actor.hide();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emit('open-state-changed', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
setActiveItem: function(position) {
|
||||||
|
this._activeItemPos = position;
|
||||||
|
},
|
||||||
|
|
||||||
|
setItemVisible: function(position, visible) {
|
||||||
|
if (!visible && position == this._activeItemPos) {
|
||||||
|
log('Trying to hide the active menu item.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._getMenuItems()[position].actor.visible = visible;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function PopupComboBoxMenuItem() {
|
||||||
|
this._init.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupComboBoxMenuItem.prototype = {
|
||||||
|
__proto__: PopupBaseMenuItem.prototype,
|
||||||
|
|
||||||
|
_init: function (params) {
|
||||||
|
PopupBaseMenuItem.prototype._init.call(this, params);
|
||||||
|
|
||||||
|
this._itemBox = new Shell.Stack();
|
||||||
|
this.addActor(this._itemBox);
|
||||||
|
|
||||||
|
let expander = new St.Label({ text: '\u2304' });
|
||||||
|
this.addActor(expander, { align: St.Align.END });
|
||||||
|
|
||||||
|
this._menu = new PopupComboMenu(this.actor);
|
||||||
|
Main.uiGroup.add_actor(this._menu.actor);
|
||||||
|
this._menu.actor.hide();
|
||||||
|
|
||||||
|
if (params.style_class)
|
||||||
|
this._menu.actor.add_style_class_name(params.style_class);
|
||||||
|
|
||||||
|
this._activeItemPos = -1;
|
||||||
|
this._items = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
_getTopMenu: function() {
|
||||||
|
let actor = this.actor.get_parent();
|
||||||
|
while (actor) {
|
||||||
|
if (actor._delegate &&
|
||||||
|
(actor._delegate instanceof PopupMenu ||
|
||||||
|
actor._delegate instanceof PopupComboMenu))
|
||||||
|
return actor._delegate;
|
||||||
|
|
||||||
|
actor = actor.get_parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function(event) {
|
||||||
|
let topMenu = this._getTopMenu();
|
||||||
|
if (!topMenu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
topMenu.addChildMenu(this._menu);
|
||||||
|
this._menu.toggle();
|
||||||
|
},
|
||||||
|
|
||||||
|
addMenuItem: function(menuItem, position) {
|
||||||
|
if (position === undefined)
|
||||||
|
position = this._menu.numMenuItems;
|
||||||
|
|
||||||
|
this._menu.addMenuItem(menuItem, position);
|
||||||
|
_ensureStyle(this._menu.actor);
|
||||||
|
|
||||||
|
let item = new St.BoxLayout({ style_class: 'popup-combobox-item' });
|
||||||
|
|
||||||
|
let children = menuItem.actor.get_children();
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
let clone = new Clutter.Clone({ source: children[i] });
|
||||||
|
item.add(clone, { y_fill: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldItem = this._items[position];
|
||||||
|
if (oldItem)
|
||||||
|
this._itemBox.remove_actor(oldItem);
|
||||||
|
|
||||||
|
this._items[position] = item;
|
||||||
|
this._itemBox.add_actor(item);
|
||||||
|
|
||||||
|
menuItem.connect('activate',
|
||||||
|
Lang.bind(this, this._itemActivated, position));
|
||||||
|
},
|
||||||
|
|
||||||
|
setActiveItem: function(position) {
|
||||||
|
let item = this._items[position];
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
if (this._activeItemPos == position)
|
||||||
|
return;
|
||||||
|
this._menu.setActiveItem(position);
|
||||||
|
this._activeItemPos = position;
|
||||||
|
for (let i = 0; i < this._items.length; i++)
|
||||||
|
this._items[i].visible = (i == this._activeItemPos);
|
||||||
|
},
|
||||||
|
|
||||||
|
setItemVisible: function(position, visible) {
|
||||||
|
this._menu.setItemVisible(position, visible);
|
||||||
|
},
|
||||||
|
|
||||||
|
_itemActivated: function(menuItem, event, position) {
|
||||||
|
this.setActiveItem(position);
|
||||||
|
this.emit('active-item-changed', position);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* Basic implementation of a menu manager.
|
/* Basic implementation of a menu manager.
|
||||||
* Call addMenu to add menus
|
* Call addMenu to add menus
|
||||||
@ -1303,6 +1626,7 @@ PopupMenuManager.prototype = {
|
|||||||
this._keyFocusNotifyId = 0;
|
this._keyFocusNotifyId = 0;
|
||||||
this._activeMenu = null;
|
this._activeMenu = null;
|
||||||
this._menus = [];
|
this._menus = [];
|
||||||
|
this._menuStack = [];
|
||||||
this._preGrabInputMode = null;
|
this._preGrabInputMode = null;
|
||||||
this._grabbedFromKeynav = false;
|
this._grabbedFromKeynav = false;
|
||||||
},
|
},
|
||||||
@ -1311,6 +1635,8 @@ PopupMenuManager.prototype = {
|
|||||||
let menudata = {
|
let menudata = {
|
||||||
menu: menu,
|
menu: menu,
|
||||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||||
|
childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)),
|
||||||
|
childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)),
|
||||||
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
|
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
|
||||||
enterId: 0,
|
enterId: 0,
|
||||||
focusInId: 0
|
focusInId: 0
|
||||||
@ -1338,6 +1664,8 @@ PopupMenuManager.prototype = {
|
|||||||
|
|
||||||
let menudata = this._menus[position];
|
let menudata = this._menus[position];
|
||||||
menu.disconnect(menudata.openStateChangeId);
|
menu.disconnect(menudata.openStateChangeId);
|
||||||
|
menu.disconnect(menudata.childMenuAddedId);
|
||||||
|
menu.disconnect(menudata.childMenuRemovedId);
|
||||||
menu.disconnect(menudata.destroyId);
|
menu.disconnect(menudata.destroyId);
|
||||||
|
|
||||||
if (menudata.enterId)
|
if (menudata.enterId)
|
||||||
@ -1375,8 +1703,20 @@ PopupMenuManager.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onMenuOpenState: function(menu, open) {
|
_onMenuOpenState: function(menu, open) {
|
||||||
if (open)
|
if (open) {
|
||||||
|
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
|
||||||
|
this._menuStack.push(this._activeMenu);
|
||||||
|
menu.actor.grab_key_focus();
|
||||||
|
}
|
||||||
this._activeMenu = menu;
|
this._activeMenu = menu;
|
||||||
|
} else {
|
||||||
|
if (this._menuStack.length > 0) {
|
||||||
|
this._activeMenu = this._menuStack.pop();
|
||||||
|
if (menu.sourceActor)
|
||||||
|
menu.sourceActor.grab_key_focus();
|
||||||
|
this._didPop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check what the focus was before calling pushModal/popModal
|
// Check what the focus was before calling pushModal/popModal
|
||||||
let focus = global.stage.key_focus;
|
let focus = global.stage.key_focus;
|
||||||
@ -1409,6 +1749,14 @@ PopupMenuManager.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onChildMenuAdded: function(menu, childMenu) {
|
||||||
|
this.addMenu(childMenu);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onChildMenuRemoved: function(menu, childMenu) {
|
||||||
|
this.removeMenu(childMenu);
|
||||||
|
},
|
||||||
|
|
||||||
// change the currently-open menu without dropping grab
|
// change the currently-open menu without dropping grab
|
||||||
_changeMenu: function(newMenu) {
|
_changeMenu: function(newMenu) {
|
||||||
if (this._activeMenu) {
|
if (this._activeMenu) {
|
||||||
@ -1417,6 +1765,8 @@ PopupMenuManager.prototype = {
|
|||||||
// before closing it to keep that from happening
|
// before closing it to keep that from happening
|
||||||
let oldMenu = this._activeMenu;
|
let oldMenu = this._activeMenu;
|
||||||
this._activeMenu = null;
|
this._activeMenu = null;
|
||||||
|
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
||||||
|
this._menuStack[i].close(false);
|
||||||
oldMenu.close(false);
|
oldMenu.close(false);
|
||||||
newMenu.open(false);
|
newMenu.open(false);
|
||||||
} else
|
} else
|
||||||
@ -1427,6 +1777,15 @@ PopupMenuManager.prototype = {
|
|||||||
if (!this.grabbed || menu == this._activeMenu)
|
if (!this.grabbed || menu == this._activeMenu)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._menuStack.indexOf(menu) != -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
|
||||||
|
return false;
|
||||||
|
|
||||||
this._changeMenu(menu);
|
this._changeMenu(menu);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -1439,6 +1798,8 @@ PopupMenuManager.prototype = {
|
|||||||
if (focus) {
|
if (focus) {
|
||||||
if (this._activeMenuContains(focus))
|
if (this._activeMenuContains(focus))
|
||||||
return;
|
return;
|
||||||
|
if (this._menuStack.length > 0)
|
||||||
|
return;
|
||||||
if (focus._delegate && focus._delegate.menu &&
|
if (focus._delegate && focus._delegate.menu &&
|
||||||
this._findMenu(focus._delegate.menu) != -1)
|
this._findMenu(focus._delegate.menu) != -1)
|
||||||
return;
|
return;
|
||||||
@ -1497,6 +1858,11 @@ PopupMenuManager.prototype = {
|
|||||||
if (this._activeMenu != null && this._activeMenu.passEvents)
|
if (this._activeMenu != null && this._activeMenu.passEvents)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this._didPop) {
|
||||||
|
this._didPop = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let activeMenuContains = this._eventIsOnActiveMenu(event);
|
let activeMenuContains = this._eventIsOnActiveMenu(event);
|
||||||
let eventType = event.type();
|
let eventType = event.type();
|
||||||
|
|
||||||
|
@ -8,8 +8,6 @@ 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 Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const FileUtils = imports.misc.fileUtils;
|
const FileUtils = imports.misc.fileUtils;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -25,6 +23,10 @@ const HISTORY_KEY = 'command-history';
|
|||||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||||
const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
|
const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
|
||||||
|
|
||||||
|
const TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
|
||||||
|
const EXEC_KEY = 'exec';
|
||||||
|
const EXEC_ARG_KEY = 'exec-arg';
|
||||||
|
|
||||||
const DIALOG_GROW_TIME = 0.1;
|
const DIALOG_GROW_TIME = 0.1;
|
||||||
|
|
||||||
function CommandCompleter() {
|
function CommandCompleter() {
|
||||||
@ -171,6 +173,7 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
|||||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
|
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
|
||||||
|
|
||||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||||
|
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
|
||||||
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
|
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
|
||||||
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
||||||
}));
|
}));
|
||||||
@ -191,7 +194,7 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
'debugexit': Lang.bind(this, function() {
|
'debugexit': Lang.bind(this, function() {
|
||||||
Meta.exit(Meta.ExitCode.ERROR);
|
Meta.quit(Meta.ExitCode.ERROR);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// rt is short for "reload theme"
|
// rt is short for "reload theme"
|
||||||
@ -311,8 +314,11 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
|||||||
f();
|
f();
|
||||||
} else if (input) {
|
} else if (input) {
|
||||||
try {
|
try {
|
||||||
if (inTerminal)
|
if (inTerminal) {
|
||||||
command = 'gnome-terminal -x ' + input;
|
let exec = this._terminalSettings.get_string(EXEC_KEY);
|
||||||
|
let exec_arg = this._terminalSettings.get_string(EXEC_ARG_KEY);
|
||||||
|
command = exec + ' ' + exec_arg + ' ' + input;
|
||||||
|
}
|
||||||
Util.trySpawnCommandLine(command);
|
Util.trySpawnCommandLine(command);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Mmmh, that failed - see if @input matches an existing file
|
// Mmmh, that failed - see if @input matches an existing file
|
||||||
@ -327,33 +333,46 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
|||||||
|
|
||||||
if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
|
if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
|
||||||
let file = Gio.file_new_for_path(path);
|
let file = Gio.file_new_for_path(path);
|
||||||
Gio.app_info_launch_default_for_uri(file.get_uri(),
|
try {
|
||||||
global.create_app_launch_context());
|
Gio.app_info_launch_default_for_uri(file.get_uri(),
|
||||||
} else {
|
global.create_app_launch_context());
|
||||||
this._commandError = true;
|
} catch (e) {
|
||||||
|
// The exception from gjs contains an error string like:
|
||||||
this._errorMessage.set_text(e.message);
|
// Error invoking Gio.app_info_launch_default_for_uri: No application
|
||||||
|
// is registered as handling this file
|
||||||
if (!this._errorBox.visible) {
|
// We are only interested in the part after the first colon.
|
||||||
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
|
let message = e.message.replace(/[^:]*: *(.+)/, '$1');
|
||||||
|
this._showError(message);
|
||||||
let parentActor = this._errorBox.get_parent();
|
|
||||||
Tweener.addTween(parentActor,
|
|
||||||
{ height: parentActor.height + errorBoxNaturalHeight,
|
|
||||||
time: DIALOG_GROW_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
onComplete: Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
parentActor.set_height(-1);
|
|
||||||
this._errorBox.show();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this._showError(e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_showError : function(message) {
|
||||||
|
this._commandError = true;
|
||||||
|
|
||||||
|
this._errorMessage.set_text(message);
|
||||||
|
|
||||||
|
if (!this._errorBox.visible) {
|
||||||
|
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
|
||||||
|
|
||||||
|
let parentActor = this._errorBox.get_parent();
|
||||||
|
Tweener.addTween(parentActor,
|
||||||
|
{ height: parentActor.height + errorBoxNaturalHeight,
|
||||||
|
time: DIALOG_GROW_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
parentActor.set_height(-1);
|
||||||
|
this._errorBox.show();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
open: function() {
|
open: function() {
|
||||||
this._history.lastItem();
|
this._history.lastItem();
|
||||||
this._errorBox.hide();
|
this._errorBox.hide();
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
const DBus = imports.dbus;
|
const DBus = imports.dbus;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
// This module provides functionality for driving the shell user interface
|
// This module provides functionality for driving the shell user interface
|
||||||
// in an automated fashion. The primary current use case for this is
|
// in an automated fashion. The primary current use case for this is
|
||||||
// automated performance testing (see runPerfScript()), but it could
|
// automated performance testing (see runPerfScript()), but it could
|
||||||
@ -246,18 +247,14 @@ function _collect(scriptModule, outputFile) {
|
|||||||
Shell.write_string_to_stream(out, '"events":\n');
|
Shell.write_string_to_stream(out, '"events":\n');
|
||||||
Shell.PerfLog.get_default().dump_events(out);
|
Shell.PerfLog.get_default().dump_events(out);
|
||||||
|
|
||||||
let monitors = global.get_monitors();
|
let monitors = Main.layoutManager.monitors;
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryIndex;
|
||||||
Shell.write_string_to_stream(out, ',\n"monitors":\n[');
|
Shell.write_string_to_stream(out, ',\n"monitors":\n[');
|
||||||
for (let i = 0; i < monitors.length; i++) {
|
for (let i = 0; i < monitors.length; i++) {
|
||||||
let monitor = monitors[i];
|
let monitor = monitors[i];
|
||||||
let is_primary = (monitor.x == primary.x &&
|
|
||||||
monitor.y == primary.y &&
|
|
||||||
monitor.width == primary.width &&
|
|
||||||
monitor.height == primary.height);
|
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
Shell.write_string_to_stream(out, ', ');
|
Shell.write_string_to_stream(out, ', ');
|
||||||
Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(is_primary ? "*" : "",
|
Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(i == primary ? "*" : "",
|
||||||
monitor.width, monitor.height,
|
monitor.width, monitor.height,
|
||||||
monitor.x, monitor.y));
|
monitor.x, monitor.y));
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,6 @@ const Signals = imports.signals;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const FileUtils = imports.misc.fileUtils;
|
const FileUtils = imports.misc.fileUtils;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
@ -115,6 +112,43 @@ function SearchProvider(title) {
|
|||||||
SearchProvider.prototype = {
|
SearchProvider.prototype = {
|
||||||
_init: function(title) {
|
_init: function(title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
this.searchSystem = null;
|
||||||
|
this.searchAsync = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_asyncCancelled: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
startAsync: function() {
|
||||||
|
this.searchAsync = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
tryCancelAsync: function() {
|
||||||
|
if (!this.searchAsync)
|
||||||
|
return;
|
||||||
|
this._asyncCancelled();
|
||||||
|
this.searchAsync = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addItems:
|
||||||
|
* @items: an array of result identifier strings representing
|
||||||
|
* items which match the last given search terms.
|
||||||
|
*
|
||||||
|
* This should be used for something that requires a bit more
|
||||||
|
* logic; it's designed to be an asyncronous way to add a result
|
||||||
|
* to the current search.
|
||||||
|
*/
|
||||||
|
addItems: function(items) {
|
||||||
|
if (!this.searchSystem)
|
||||||
|
throw new Error('Search provider not registered');
|
||||||
|
|
||||||
|
if (!items.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.tryCancelAsync();
|
||||||
|
|
||||||
|
this.searchSystem.addProviderItems(this, items);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,7 +194,7 @@ SearchProvider.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getResultInfo:
|
* getResultMeta:
|
||||||
* @id: Result identifier string
|
* @id: Result identifier string
|
||||||
*
|
*
|
||||||
* Return an object with 'id', 'name', (both strings) and 'createIcon'
|
* Return an object with 'id', 'name', (both strings) and 'createIcon'
|
||||||
@ -276,7 +310,7 @@ OpenSearchSystem.prototype = {
|
|||||||
_addProvider: function(fileName) {
|
_addProvider: function(fileName) {
|
||||||
let path = global.datadir + '/search_providers/' + fileName;
|
let path = global.datadir + '/search_providers/' + fileName;
|
||||||
let source = Shell.get_file_contents_utf8_sync(path);
|
let source = Shell.get_file_contents_utf8_sync(path);
|
||||||
let [success, name, url, langs, icon_uri] = global.parse_search_provider(source);
|
let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source);
|
||||||
let provider ={ name: name,
|
let provider ={ name: name,
|
||||||
url: url,
|
url: url,
|
||||||
id: this._providers.length,
|
id: this._providers.length,
|
||||||
@ -318,6 +352,7 @@ SearchSystem.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
registerProvider: function (provider) {
|
registerProvider: function (provider) {
|
||||||
|
provider.searchSystem = this;
|
||||||
this._providers.push(provider);
|
this._providers.push(provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -334,12 +369,23 @@ SearchSystem.prototype = {
|
|||||||
this._previousResults = [];
|
this._previousResults = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addProviderItems: function(provider, items) {
|
||||||
|
this.emit('search-updated', provider, items);
|
||||||
|
},
|
||||||
|
|
||||||
updateSearch: function(searchString) {
|
updateSearch: function(searchString) {
|
||||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||||
if (searchString == '')
|
if (searchString == '')
|
||||||
return [];
|
return;
|
||||||
|
|
||||||
let terms = searchString.split(/\s+/);
|
let terms = searchString.split(/\s+/);
|
||||||
|
this.updateSearchResults(terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSearchResults: function(terms) {
|
||||||
|
if (!terms)
|
||||||
|
return;
|
||||||
|
|
||||||
let isSubSearch = terms.length == this._previousTerms.length;
|
let isSubSearch = terms.length == this._previousTerms.length;
|
||||||
if (isSubSearch) {
|
if (isSubSearch) {
|
||||||
for (let i = 0; i < terms.length; i++) {
|
for (let i = 0; i < terms.length; i++) {
|
||||||
@ -352,12 +398,12 @@ SearchSystem.prototype = {
|
|||||||
|
|
||||||
let results = [];
|
let results = [];
|
||||||
if (isSubSearch) {
|
if (isSubSearch) {
|
||||||
for (let i = 0; i < this._previousResults.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
let [provider, previousResults] = this._previousResults[i];
|
let [provider, previousResults] = this._previousResults[i];
|
||||||
|
provider.tryCancelAsync();
|
||||||
try {
|
try {
|
||||||
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
||||||
if (providerResults.length > 0)
|
results.push([provider, providerResults]);
|
||||||
results.push([provider, providerResults]);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||||
}
|
}
|
||||||
@ -365,10 +411,10 @@ SearchSystem.prototype = {
|
|||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
let provider = this._providers[i];
|
let provider = this._providers[i];
|
||||||
|
provider.tryCancelAsync();
|
||||||
try {
|
try {
|
||||||
let providerResults = provider.getInitialResultSet(terms);
|
let providerResults = provider.getInitialResultSet(terms);
|
||||||
if (providerResults.length > 0)
|
results.push([provider, providerResults]);
|
||||||
results.push([provider, providerResults]);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||||
}
|
}
|
||||||
@ -377,8 +423,7 @@ SearchSystem.prototype = {
|
|||||||
|
|
||||||
this._previousTerms = terms;
|
this._previousTerms = terms;
|
||||||
this._previousResults = results;
|
this._previousResults = results;
|
||||||
|
this.emit('search-completed', results);
|
||||||
return results;
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Signals.addSignalMethods(SearchSystem.prototype);
|
Signals.addSignalMethods(SearchSystem.prototype);
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
@ -30,6 +28,7 @@ SearchResult.prototype = {
|
|||||||
x_align: St.Align.START,
|
x_align: St.Align.START,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
this._dragActorSource = null;
|
||||||
|
|
||||||
let content = provider.createResultActor(metaInfo, terms);
|
let content = provider.createResultActor(metaInfo, terms);
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
@ -39,6 +38,11 @@ SearchResult.prototype = {
|
|||||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||||
{ createIcon: this.metaInfo['createIcon'] });
|
{ createIcon: this.metaInfo['createIcon'] });
|
||||||
content.set_child(icon.actor);
|
content.set_child(icon.actor);
|
||||||
|
this._dragActorSource = icon.icon;
|
||||||
|
this.actor.label_actor = icon.label;
|
||||||
|
} else {
|
||||||
|
if (content._delegate && content._delegate.getDragActorSource)
|
||||||
|
this._dragActorSource = content._delegate.getDragActorSource();
|
||||||
}
|
}
|
||||||
this._content = content;
|
this._content = content;
|
||||||
this.actor.set_child(content);
|
this.actor.set_child(content);
|
||||||
@ -77,12 +81,14 @@ SearchResult.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDragActorSource: function() {
|
getDragActorSource: function() {
|
||||||
|
if (this._dragActorSource)
|
||||||
|
return this._dragActorSource;
|
||||||
// not exactly right, but alignment problems are hard to notice
|
// not exactly right, but alignment problems are hard to notice
|
||||||
return this._content;
|
return this._content;
|
||||||
},
|
},
|
||||||
|
|
||||||
getDragActor: function(stageX, stageY) {
|
getDragActor: function(stageX, stageY) {
|
||||||
return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
|
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||||
},
|
},
|
||||||
|
|
||||||
shellWorkspaceLaunch: function(params) {
|
shellWorkspaceLaunch: function(params) {
|
||||||
@ -94,17 +100,17 @@ SearchResult.prototype = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function GridSearchResults(provider) {
|
function GridSearchResults(provider, grid) {
|
||||||
this._init(provider);
|
this._init(provider, grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
GridSearchResults.prototype = {
|
GridSearchResults.prototype = {
|
||||||
__proto__: Search.SearchResultDisplay.prototype,
|
__proto__: Search.SearchResultDisplay.prototype,
|
||||||
|
|
||||||
_init: function(provider) {
|
_init: function(provider, grid) {
|
||||||
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
||||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||||
xAlign: St.Align.START });
|
xAlign: St.Align.START });
|
||||||
this.actor = new St.Bin({ x_align: St.Align.START });
|
this.actor = new St.Bin({ x_align: St.Align.START });
|
||||||
|
|
||||||
this.actor.set_child(this._grid.actor);
|
this.actor.set_child(this._grid.actor);
|
||||||
@ -183,6 +189,8 @@ function SearchResults(searchSystem, openSearchSystem) {
|
|||||||
SearchResults.prototype = {
|
SearchResults.prototype = {
|
||||||
_init: function(searchSystem, openSearchSystem) {
|
_init: function(searchSystem, openSearchSystem) {
|
||||||
this._searchSystem = searchSystem;
|
this._searchSystem = searchSystem;
|
||||||
|
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
|
||||||
|
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
|
||||||
this._openSearchSystem = openSearchSystem;
|
this._openSearchSystem = openSearchSystem;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||||
@ -193,7 +201,7 @@ SearchResults.prototype = {
|
|||||||
|
|
||||||
let scrollView = new St.ScrollView({ x_fill: true,
|
let scrollView = new St.ScrollView({ x_fill: true,
|
||||||
y_fill: false,
|
y_fill: false,
|
||||||
vfade: true });
|
style_class: 'vfade' });
|
||||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||||
scrollView.add_actor(this._content);
|
scrollView.add_actor(this._content);
|
||||||
|
|
||||||
@ -217,9 +225,11 @@ SearchResults.prototype = {
|
|||||||
this._selectedProvider = -1;
|
this._selectedProvider = -1;
|
||||||
this._providers = this._searchSystem.getProviders();
|
this._providers = this._searchSystem.getProviders();
|
||||||
this._providerMeta = [];
|
this._providerMeta = [];
|
||||||
for (let i = 0; i < this._providers.length; i++)
|
this._providerMetaResults = {};
|
||||||
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
this.createProviderMeta(this._providers[i]);
|
this.createProviderMeta(this._providers[i]);
|
||||||
|
this._providerMetaResults[this.providers[i].title] = [];
|
||||||
|
}
|
||||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||||
this.actor.add(this._searchProvidersBox);
|
this.actor.add(this._searchProvidersBox);
|
||||||
|
|
||||||
@ -259,6 +269,7 @@ SearchResults.prototype = {
|
|||||||
let title = new St.Label({ text: provider.name,
|
let title = new St.Label({ text: provider.name,
|
||||||
style_class: 'dash-search-button-label' });
|
style_class: 'dash-search-button-label' });
|
||||||
|
|
||||||
|
button.label_actor = title;
|
||||||
bin.set_child(title);
|
bin.set_child(title);
|
||||||
button.set_child(bin);
|
button.set_child(bin);
|
||||||
provider.actor = button;
|
provider.actor = button;
|
||||||
@ -298,6 +309,12 @@ SearchResults.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_clearDisplayForProvider: function(index) {
|
||||||
|
let meta = this._providerMeta[index];
|
||||||
|
meta.resultDisplay.clear();
|
||||||
|
meta.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this._searchSystem.reset();
|
this._searchSystem.reset();
|
||||||
this._statusText.hide();
|
this._statusText.hide();
|
||||||
@ -312,15 +329,24 @@ SearchResults.prototype = {
|
|||||||
this._statusText.show();
|
this._statusText.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
doSearch: function (searchString) {
|
||||||
|
this._searchSystem.updateSearch(searchString);
|
||||||
|
},
|
||||||
|
|
||||||
_metaForProvider: function(provider) {
|
_metaForProvider: function(provider) {
|
||||||
return this._providerMeta[this._providers.indexOf(provider)];
|
return this._providerMeta[this._providers.indexOf(provider)];
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSearch: function (searchString) {
|
_updateCurrentResults: function(searchSystem, provider, results) {
|
||||||
let results = this._searchSystem.updateSearch(searchString);
|
let terms = searchSystem.getTerms();
|
||||||
|
let meta = this._metaForProvider(provider);
|
||||||
this._clearDisplay();
|
meta.resultDisplay.clear();
|
||||||
|
meta.actor.show();
|
||||||
|
meta.resultDisplay.renderResults(results, terms);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateResults: function(searchSystem, results) {
|
||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
this._statusText.set_text(_("No matching results."));
|
this._statusText.set_text(_("No matching results."));
|
||||||
this._statusText.show();
|
this._statusText.show();
|
||||||
@ -330,19 +356,34 @@ SearchResults.prototype = {
|
|||||||
this._statusText.hide();
|
this._statusText.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
let terms = this._searchSystem.getTerms();
|
let terms = searchSystem.getTerms();
|
||||||
this._openSearchSystem.setSearchTerms(terms);
|
this._openSearchSystem.setSearchTerms(terms);
|
||||||
|
|
||||||
|
// To avoid CSS transitions causing flickering
|
||||||
|
// of the selection when the first search result
|
||||||
|
// stays the same, we hide the content while
|
||||||
|
// filling in the results and setting the initial
|
||||||
|
// selection.
|
||||||
|
this._content.hide();
|
||||||
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
for (let i = 0; i < results.length; i++) {
|
||||||
let [provider, providerResults] = results[i];
|
let [provider, providerResults] = results[i];
|
||||||
let meta = this._metaForProvider(provider);
|
if (providerResults.length == 0) {
|
||||||
meta.actor.show();
|
this._clearDisplayForProvider(i);
|
||||||
meta.resultDisplay.renderResults(providerResults, terms);
|
} else {
|
||||||
|
this._providerMetaResults[provider.title] = providerResults;
|
||||||
|
this._clearDisplayForProvider(i);
|
||||||
|
let meta = this._metaForProvider(provider);
|
||||||
|
meta.actor.show();
|
||||||
|
meta.resultDisplay.renderResults(providerResults, terms);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._selectedOpenSearchButton == -1)
|
if (this._selectedOpenSearchButton == -1)
|
||||||
this.selectDown(false);
|
this.selectDown(false);
|
||||||
|
|
||||||
|
this._content.show();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
const DBus = imports.dbus;
|
const DBus = imports.dbus;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
const Config = imports.misc.config;
|
||||||
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const GnomeShellIface = {
|
const GnomeShellIface = {
|
||||||
@ -9,12 +12,55 @@ const GnomeShellIface = {
|
|||||||
methods: [{ name: 'Eval',
|
methods: [{ name: 'Eval',
|
||||||
inSignature: 's',
|
inSignature: 's',
|
||||||
outSignature: 'bs'
|
outSignature: 'bs'
|
||||||
|
},
|
||||||
|
{ name: 'ListExtensions',
|
||||||
|
inSignature: '',
|
||||||
|
outSignature: 'a{sa{sv}}'
|
||||||
|
},
|
||||||
|
{ name: 'GetExtensionInfo',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: 'a{sv}'
|
||||||
|
},
|
||||||
|
{ name: 'GetExtensionErrors',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: 'as'
|
||||||
|
},
|
||||||
|
{ name: 'ScreenshotArea',
|
||||||
|
inSignature: 'iiiis',
|
||||||
|
outSignature: 'b'
|
||||||
|
},
|
||||||
|
{ name: 'ScreenshotWindow',
|
||||||
|
inSignature: 'bs',
|
||||||
|
outSignature: 'b'
|
||||||
|
},
|
||||||
|
{ name: 'Screenshot',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: 'b'
|
||||||
|
},
|
||||||
|
{ name: 'EnableExtension',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'DisableExtension',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'InstallRemoteExtension',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: ''
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
signals: [],
|
signals: [{ name: 'ExtensionStatusChanged',
|
||||||
|
inSignature: 'sis' }],
|
||||||
properties: [{ name: 'OverviewActive',
|
properties: [{ name: 'OverviewActive',
|
||||||
signature: 'b',
|
signature: 'b',
|
||||||
access: 'readwrite' }]
|
access: 'readwrite' },
|
||||||
|
{ name: 'ApiVersion',
|
||||||
|
signature: 'i',
|
||||||
|
access: 'read' },
|
||||||
|
{ name: 'ShellVersion',
|
||||||
|
signature: 's',
|
||||||
|
access: 'read' }]
|
||||||
};
|
};
|
||||||
|
|
||||||
function GnomeShell() {
|
function GnomeShell() {
|
||||||
@ -24,6 +70,8 @@ function GnomeShell() {
|
|||||||
GnomeShell.prototype = {
|
GnomeShell.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
DBus.session.exportObject('/org/gnome/Shell', this);
|
DBus.session.exportObject('/org/gnome/Shell', this);
|
||||||
|
ExtensionSystem.connect('extension-state-changed',
|
||||||
|
Lang.bind(this, this._extensionStateChanged));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +104,80 @@ GnomeShell.prototype = {
|
|||||||
return [success, returnValue];
|
return [success, returnValue];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScreenshotArea:
|
||||||
|
* @x: The X coordinate of the area
|
||||||
|
* @y: The Y coordinate of the area
|
||||||
|
* @width: The width of the area
|
||||||
|
* @height: The height of the area
|
||||||
|
* @filename: The filename for the screenshot
|
||||||
|
*
|
||||||
|
* Takes a screenshot of the passed in area and saves it
|
||||||
|
* in @filename as png image, it returns a boolean
|
||||||
|
* indicating whether the operation was successful or not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ScreenshotAreaAsync : function (x, y, width, height, filename, callback) {
|
||||||
|
global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScreenshotWindow:
|
||||||
|
* @include_frame: Whether to include the frame or not
|
||||||
|
* @filename: The filename for the screenshot
|
||||||
|
*
|
||||||
|
* Takes a screenshot of the focused window (optionally omitting the frame)
|
||||||
|
* and saves it in @filename as png image, it returns a boolean
|
||||||
|
* indicating whether the operation was successful or not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ScreenshotWindow : function (include_frame, filename) {
|
||||||
|
return global.screenshot_window (include_frame, filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screenshot:
|
||||||
|
* @filename: The filename for the screenshot
|
||||||
|
*
|
||||||
|
* Takes a screenshot of the whole screen and saves it
|
||||||
|
* in @filename as png image, it returns a boolean
|
||||||
|
* indicating whether the operation was successful or not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ScreenshotAsync : function (filename, callback) {
|
||||||
|
global.screenshot(filename, function (obj, result) { callback(result); });
|
||||||
|
},
|
||||||
|
|
||||||
|
ListExtensions: function() {
|
||||||
|
return ExtensionSystem.extensionMeta;
|
||||||
|
},
|
||||||
|
|
||||||
|
GetExtensionInfo: function(uuid) {
|
||||||
|
return ExtensionSystem.extensionMeta[uuid] || {};
|
||||||
|
},
|
||||||
|
|
||||||
|
GetExtensionErrors: function(uuid) {
|
||||||
|
return ExtensionSystem.errors[uuid] || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
EnableExtension: function(uuid) {
|
||||||
|
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||||
|
if (enabledExtensions.indexOf(uuid) == -1)
|
||||||
|
enabledExtensions.push(uuid);
|
||||||
|
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||||
|
},
|
||||||
|
|
||||||
|
DisableExtension: function(uuid) {
|
||||||
|
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||||
|
while (enabledExtensions.indexOf(uuid) != -1)
|
||||||
|
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
|
||||||
|
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||||
|
},
|
||||||
|
|
||||||
|
InstallRemoteExtension: function(uuid, url) {
|
||||||
|
ExtensionSystem.installExtensionFromManifestURL(uuid, url);
|
||||||
|
},
|
||||||
|
|
||||||
get OverviewActive() {
|
get OverviewActive() {
|
||||||
return Main.overview.visible;
|
return Main.overview.visible;
|
||||||
},
|
},
|
||||||
@ -65,6 +187,17 @@ GnomeShell.prototype = {
|
|||||||
Main.overview.show();
|
Main.overview.show();
|
||||||
else
|
else
|
||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
ApiVersion: 1,
|
||||||
|
|
||||||
|
ShellVersion: Config.PACKAGE_VERSION,
|
||||||
|
|
||||||
|
_extensionStateChanged: function(_, newState) {
|
||||||
|
DBus.session.emit_signal('/org/gnome/Shell',
|
||||||
|
'org.gnome.Shell',
|
||||||
|
'ExtensionStatusChanged', 'sis',
|
||||||
|
[newState.uuid, newState.state, newState.error]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
405
js/ui/shellMountOperation.js
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Pango = imports.gi.Pango;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const LIST_ITEM_ICON_SIZE = 48;
|
||||||
|
|
||||||
|
/* ------ Common Utils ------- */
|
||||||
|
function _setLabelText(label, text) {
|
||||||
|
if (text) {
|
||||||
|
label.set_text(text);
|
||||||
|
label.show();
|
||||||
|
} else {
|
||||||
|
label.set_text('');
|
||||||
|
label.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setButtonsForChoices(dialog, choices) {
|
||||||
|
let buttons = [];
|
||||||
|
|
||||||
|
for (let idx = 0; idx < choices.length; idx++) {
|
||||||
|
let button = idx;
|
||||||
|
buttons.unshift({ label: choices[idx],
|
||||||
|
action: Lang.bind(dialog, function() {
|
||||||
|
dialog.emit('response', button);
|
||||||
|
})});
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setButtons(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setLabelsForMessage(dialog, message) {
|
||||||
|
let labels = message.split('\n');
|
||||||
|
|
||||||
|
_setLabelText(dialog.subjectLabel, labels[0]);
|
||||||
|
if (labels.length > 1)
|
||||||
|
_setLabelText(dialog.descriptionLabel, labels[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
function ListItem(app) {
|
||||||
|
this._init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListItem.prototype = {
|
||||||
|
_init: function(app) {
|
||||||
|
this._app = app;
|
||||||
|
|
||||||
|
let layout = new St.BoxLayout({ vertical: false});
|
||||||
|
|
||||||
|
this.actor = new St.Button({ style_class: 'show-processes-dialog-app-list-item',
|
||||||
|
can_focus: true,
|
||||||
|
child: layout,
|
||||||
|
reactive: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
x_fill: true });
|
||||||
|
|
||||||
|
this._icon = this._app.create_icon_texture(LIST_ITEM_ICON_SIZE);
|
||||||
|
|
||||||
|
let iconBin = new St.Bin({ style_class: 'show-processes-dialog-app-list-item-icon',
|
||||||
|
child: this._icon });
|
||||||
|
layout.add(iconBin);
|
||||||
|
|
||||||
|
this._nameLabel = new St.Label({ text: this._app.get_name(),
|
||||||
|
style_class: 'show-processes-dialog-app-list-item-name' });
|
||||||
|
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE,
|
||||||
|
child: this._nameLabel });
|
||||||
|
layout.add(labelBin);
|
||||||
|
|
||||||
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClicked: function() {
|
||||||
|
this.emit('activate');
|
||||||
|
this._app.activate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(ListItem.prototype);
|
||||||
|
|
||||||
|
function ShellMountOperation(source, params) {
|
||||||
|
this._init(source, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellMountOperation.prototype = {
|
||||||
|
_init: function(source, params) {
|
||||||
|
params = Params.parse(params, { reaskPassword: false });
|
||||||
|
|
||||||
|
this._reaskPassword = params.reaskPassword;
|
||||||
|
|
||||||
|
this._dialog = null;
|
||||||
|
this._processesDialog = null;
|
||||||
|
|
||||||
|
this.mountOp = new Shell.MountOperation();
|
||||||
|
|
||||||
|
this.mountOp.connect('ask-question',
|
||||||
|
Lang.bind(this, this._onAskQuestion));
|
||||||
|
this.mountOp.connect('ask-password',
|
||||||
|
Lang.bind(this, this._onAskPassword));
|
||||||
|
this.mountOp.connect('show-processes-2',
|
||||||
|
Lang.bind(this, this._onShowProcesses2));
|
||||||
|
this.mountOp.connect('aborted',
|
||||||
|
Lang.bind(this, this._onAborted));
|
||||||
|
|
||||||
|
this._icon = new St.Icon({ gicon: source.get_icon(),
|
||||||
|
style_class: 'shell-mount-operation-icon' });
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAskQuestion: function(op, message, choices) {
|
||||||
|
this._dialog = new ShellMountQuestionDialog(this._icon);
|
||||||
|
|
||||||
|
this._dialog.connect('response',
|
||||||
|
Lang.bind(this, function(object, choice) {
|
||||||
|
this.mountOp.set_choice(choice);
|
||||||
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
|
|
||||||
|
this._dialog.close(global.get_current_time());
|
||||||
|
this._dialog = null;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dialog.update(message, choices);
|
||||||
|
this._dialog.open(global.get_current_time());
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAskPassword: function(op, message) {
|
||||||
|
this._notificationShowing = true;
|
||||||
|
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
|
||||||
|
|
||||||
|
this._source.connect('password-ready',
|
||||||
|
Lang.bind(this, function(source, password) {
|
||||||
|
this.mountOp.set_password(password);
|
||||||
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
|
|
||||||
|
this._notificationShowing = false;
|
||||||
|
this._source.destroy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._source.connect('destroy',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
if (!this._notificationShowing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._notificationShowing = false;
|
||||||
|
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAborted: function(op) {
|
||||||
|
if (!this._dialog)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._dialog.close(global.get_current_time());
|
||||||
|
this._dialog = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShowProcesses2: function(op) {
|
||||||
|
let processes = op.get_show_processes_pids();
|
||||||
|
let choices = op.get_show_processes_choices();
|
||||||
|
let message = op.get_show_processes_message();
|
||||||
|
|
||||||
|
if (!this._processesDialog) {
|
||||||
|
this._processesDialog = new ShellProcessesDialog(this._icon);
|
||||||
|
this._dialog = this._processesDialog;
|
||||||
|
|
||||||
|
this._processesDialog.connect('response',
|
||||||
|
Lang.bind(this, function(object, choice) {
|
||||||
|
if (choice == -1) {
|
||||||
|
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||||
|
} else {
|
||||||
|
this.mountOp.set_choice(choice);
|
||||||
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._processesDialog.close(global.get_current_time());
|
||||||
|
this._dialog = null;
|
||||||
|
}));
|
||||||
|
this._processesDialog.open(global.get_current_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
this._processesDialog.update(message, processes, choices);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShellMountQuestionDialog(icon) {
|
||||||
|
this._init(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellMountQuestionDialog.prototype = {
|
||||||
|
__proto__: ModalDialog.ModalDialog.prototype,
|
||||||
|
|
||||||
|
_init: function(icon) {
|
||||||
|
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'mount-question-dialog' });
|
||||||
|
|
||||||
|
let mainContentLayout = new St.BoxLayout();
|
||||||
|
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
||||||
|
y_fill: false });
|
||||||
|
|
||||||
|
this._iconBin = new St.Bin({ child: icon });
|
||||||
|
mainContentLayout.add(this._iconBin,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let messageLayout = new St.BoxLayout({ vertical: true });
|
||||||
|
mainContentLayout.add(messageLayout,
|
||||||
|
{ y_align: St.Align.START });
|
||||||
|
|
||||||
|
this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' });
|
||||||
|
|
||||||
|
messageLayout.add(this.subjectLabel,
|
||||||
|
{ y_fill: false,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
this.descriptionLabel = new St.Label({ style_class: 'mount-question-dialog-description' });
|
||||||
|
this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
this.descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
|
||||||
|
messageLayout.add(this.descriptionLabel,
|
||||||
|
{ y_fill: true,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(message, choices) {
|
||||||
|
_setLabelsForMessage(this, message);
|
||||||
|
_setButtonsForChoices(this, choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
|
||||||
|
|
||||||
|
function ShellMountPasswordSource(message, icon, reaskPassword) {
|
||||||
|
this._init(message, icon, reaskPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellMountPasswordSource.prototype = {
|
||||||
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
|
_init: function(message, icon, reaskPassword) {
|
||||||
|
let strings = message.split('\n');
|
||||||
|
MessageTray.Source.prototype._init.call(this, strings[0]);
|
||||||
|
|
||||||
|
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
|
||||||
|
|
||||||
|
// add ourselves as a source, and popup the notification
|
||||||
|
Main.messageTray.add(this);
|
||||||
|
this.notify(this._notification);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
|
||||||
|
|
||||||
|
function ShellMountPasswordNotification(source, strings, icon, reaskPassword) {
|
||||||
|
this._init(source, strings, icon, reaskPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellMountPasswordNotification.prototype = {
|
||||||
|
__proto__: MessageTray.Notification.prototype,
|
||||||
|
|
||||||
|
_init: function(source, strings, icon, reaskPassword) {
|
||||||
|
MessageTray.Notification.prototype._init.call(this, source,
|
||||||
|
strings[0], null,
|
||||||
|
{ customContent: true,
|
||||||
|
icon: icon });
|
||||||
|
|
||||||
|
// set the notification to transient and urgent, so that it
|
||||||
|
// expands out
|
||||||
|
this.setTransient(true);
|
||||||
|
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||||
|
|
||||||
|
if (strings[1])
|
||||||
|
this.addBody(strings[1]);
|
||||||
|
|
||||||
|
if (reaskPassword) {
|
||||||
|
let label = new St.Label({ style_class: 'mount-password-reask',
|
||||||
|
text: _("Wrong password, please try again") });
|
||||||
|
|
||||||
|
this.addActor(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
|
||||||
|
can_focus: true });
|
||||||
|
this.setActionArea(this._responseEntry);
|
||||||
|
|
||||||
|
this._responseEntry.clutter_text.connect('activate',
|
||||||
|
Lang.bind(this, this._onEntryActivated));
|
||||||
|
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||||
|
|
||||||
|
this._responseEntry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEntryActivated: function() {
|
||||||
|
let text = this._responseEntry.get_text();
|
||||||
|
if (text == '')
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.source.emit('password-ready', text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShellProcessesDialog(icon) {
|
||||||
|
this._init(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellProcessesDialog.prototype = {
|
||||||
|
__proto__: ModalDialog.ModalDialog.prototype,
|
||||||
|
|
||||||
|
_init: function(icon) {
|
||||||
|
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'show-processes-dialog' });
|
||||||
|
|
||||||
|
let mainContentLayout = new St.BoxLayout();
|
||||||
|
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
||||||
|
y_fill: false });
|
||||||
|
|
||||||
|
this._iconBin = new St.Bin({ child: icon });
|
||||||
|
mainContentLayout.add(this._iconBin,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let messageLayout = new St.BoxLayout({ vertical: true });
|
||||||
|
mainContentLayout.add(messageLayout,
|
||||||
|
{ y_align: St.Align.START });
|
||||||
|
|
||||||
|
this.subjectLabel = new St.Label({ style_class: 'show-processes-dialog-subject' });
|
||||||
|
|
||||||
|
messageLayout.add(this.subjectLabel,
|
||||||
|
{ y_fill: false,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
this.descriptionLabel = new St.Label({ style_class: 'show-processes-dialog-description' });
|
||||||
|
this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
this.descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
|
||||||
|
messageLayout.add(this.descriptionLabel,
|
||||||
|
{ y_fill: true,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let scrollView = new St.ScrollView({ style_class: 'show-processes-dialog-app-list'});
|
||||||
|
scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||||
|
Gtk.PolicyType.AUTOMATIC);
|
||||||
|
this.contentLayout.add(scrollView,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: true });
|
||||||
|
scrollView.hide();
|
||||||
|
|
||||||
|
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||||
|
scrollView.add_actor(this._applicationList,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
this._applicationList.connect('actor-added',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
if (this._applicationList.get_children().length == 1)
|
||||||
|
scrollView.show();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._applicationList.connect('actor-removed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
if (this._applicationList.get_children().length == 0)
|
||||||
|
scrollView.hide();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_setAppsForPids: function(pids) {
|
||||||
|
// remove all the items
|
||||||
|
this._applicationList.destroy_children();
|
||||||
|
|
||||||
|
pids.forEach(Lang.bind(this, function(pid) {
|
||||||
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
let app = tracker.get_app_from_pid(pid);
|
||||||
|
|
||||||
|
if (!app)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let item = new ListItem(app);
|
||||||
|
this._applicationList.add(item.actor, { x_fill: true });
|
||||||
|
|
||||||
|
item.connect('activate',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
// use -1 to indicate Cancel
|
||||||
|
this.emit('response', -1);
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(message, processes, choices) {
|
||||||
|
this._setAppsForPids(processes);
|
||||||
|
_setLabelsForMessage(this, message);
|
||||||
|
_setButtonsForChoices(this, choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Signals.addSignalMethods(ShellProcessesDialog.prototype);
|
@ -10,13 +10,11 @@ const Shell = imports.gi.Shell;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
|
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
|
||||||
@ -90,10 +88,7 @@ ATIndicator.prototype = {
|
|||||||
this.menu.addMenuItem(mouseKeys);
|
this.menu.addMenuItem(mouseKeys);
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.menu.addAction(_("Universal Access Settings"), function() {
|
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
|
|
||||||
app.activate(-1);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
||||||
|
@ -16,9 +16,6 @@ const MessageTray = imports.ui.messageTray;
|
|||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const ConnectionState = {
|
const ConnectionState = {
|
||||||
DISCONNECTED: 0,
|
DISCONNECTED: 0,
|
||||||
CONNECTED: 1,
|
CONNECTED: 1,
|
||||||
@ -67,10 +64,9 @@ Indicator.prototype = {
|
|||||||
|
|
||||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
||||||
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
|
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
|
||||||
new PopupMenu.PopupMenuItem(_("Setup a New Device...")),
|
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
|
||||||
new PopupMenu.PopupSeparatorMenuItem()];
|
new PopupMenu.PopupSeparatorMenuItem()];
|
||||||
this._hasDevices = false;
|
this._hasDevices = false;
|
||||||
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
|
|
||||||
|
|
||||||
this._fullMenuItems[1].connect('activate', function() {
|
this._fullMenuItems[1].connect('activate', function() {
|
||||||
GLib.spawn_command_line_async('bluetooth-sendto');
|
GLib.spawn_command_line_async('bluetooth-sendto');
|
||||||
@ -92,10 +88,7 @@ Indicator.prototype = {
|
|||||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
||||||
this._updateFullMenu();
|
this._updateFullMenu();
|
||||||
|
|
||||||
this.menu.addAction(_("Bluetooth Settings"), function() {
|
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
|
||||||
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
|
|
||||||
app.activate(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
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));
|
||||||
@ -111,7 +104,11 @@ Indicator.prototype = {
|
|||||||
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
|
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
|
||||||
|
|
||||||
this._killswitch.setToggleState(on);
|
this._killswitch.setToggleState(on);
|
||||||
this._killswitch.actor.reactive = can_toggle;
|
if (can_toggle)
|
||||||
|
this._killswitch.setStatus(null);
|
||||||
|
else
|
||||||
|
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
||||||
|
this._killswitch.setStatus(_("hardware disabled"));
|
||||||
|
|
||||||
if (has_adapter)
|
if (has_adapter)
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
@ -127,13 +124,6 @@ Indicator.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_deviceCompare: function(d1, d2) {
|
|
||||||
return d1.device_path == d2.device_path &&
|
|
||||||
d1.bdaddr == d2.bdaddr &&
|
|
||||||
d1.can_connect == d2.can_connect &&
|
|
||||||
d1.capabilities == d2.capabilities;
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDevices: function() {
|
_updateDevices: function() {
|
||||||
let devices = this._applet.get_devices();
|
let devices = this._applet.get_devices();
|
||||||
|
|
||||||
@ -142,12 +132,8 @@ Indicator.prototype = {
|
|||||||
let item = this._deviceItems[i];
|
let item = this._deviceItems[i];
|
||||||
let destroy = true;
|
let destroy = true;
|
||||||
for (let j = 0; j < devices.length; j++) {
|
for (let j = 0; j < devices.length; j++) {
|
||||||
// we need to deep compare because BluetoothSimpleDevice is a boxed type
|
if (item._device.device_path == devices[j].device_path) {
|
||||||
// (but we take advantage of that, because _skip will disappear the next
|
this._updateDeviceItem(item, devices[j]);
|
||||||
// time get_devices() is called)
|
|
||||||
if (this._deviceCompare(item._device, devices[j])) {
|
|
||||||
item.label.text = devices[j].alias;
|
|
||||||
devices[j]._skip = true;
|
|
||||||
destroy = false;
|
destroy = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -162,7 +148,7 @@ Indicator.prototype = {
|
|||||||
this._hasDevices = newlist.length > 0;
|
this._hasDevices = newlist.length > 0;
|
||||||
for (let i = 0; i < devices.length; i++) {
|
for (let i = 0; i < devices.length; i++) {
|
||||||
let d = devices[i];
|
let d = devices[i];
|
||||||
if (d._skip)
|
if (d._item)
|
||||||
continue;
|
continue;
|
||||||
let item = this._createDeviceItem(d);
|
let item = this._createDeviceItem(d);
|
||||||
if (item) {
|
if (item) {
|
||||||
@ -171,29 +157,64 @@ Indicator.prototype = {
|
|||||||
this._hasDevices = true;
|
this._hasDevices = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._hasDevices)
|
},
|
||||||
this._deviceSep.actor.show();
|
|
||||||
else
|
_updateDeviceItem: function(item, device) {
|
||||||
this._deviceSep.actor.hide();
|
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) {
|
_createDeviceItem: function(device) {
|
||||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
|
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
|
||||||
return null;
|
return null;
|
||||||
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
|
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
|
||||||
item._device = device;
|
|
||||||
|
|
||||||
|
// 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) {
|
if (device.can_connect) {
|
||||||
item._connected = device.connected;
|
item._connected = device.connected;
|
||||||
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
|
item._connectedMenuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
|
||||||
|
item._connectedMenuitem.connect('toggled', Lang.bind(this, function() {
|
||||||
menuitem.connect('toggled', Lang.bind(this, function() {
|
|
||||||
if (item._connected > ConnectionState.CONNECTED) {
|
if (item._connected > ConnectionState.CONNECTED) {
|
||||||
// operation already in progress, revert
|
// operation already in progress, revert
|
||||||
|
// (should not happen anyway)
|
||||||
menuitem.setToggleState(menuitem.state);
|
menuitem.setToggleState(menuitem.state);
|
||||||
}
|
}
|
||||||
if (item._connected) {
|
if (item._connected) {
|
||||||
item._connected = ConnectionState.DISCONNECTING;
|
item._connected = ConnectionState.DISCONNECTING;
|
||||||
|
menuitem.setStatus(_("disconnecting..."));
|
||||||
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
|
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
|
||||||
if (success) { // apply
|
if (success) { // apply
|
||||||
item._connected = ConnectionState.DISCONNECTED;
|
item._connected = ConnectionState.DISCONNECTED;
|
||||||
@ -202,9 +223,11 @@ Indicator.prototype = {
|
|||||||
item._connected = ConnectionState.CONNECTED;
|
item._connected = ConnectionState.CONNECTED;
|
||||||
menuitem.setToggleState(true);
|
menuitem.setToggleState(true);
|
||||||
}
|
}
|
||||||
|
menuitem.setStatus(null);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
item._connected = ConnectionState.CONNECTING;
|
item._connected = ConnectionState.CONNECTING;
|
||||||
|
menuitem.setStatus(_("connecting..."));
|
||||||
this._applet.connect_device(item._device.device_path, function(applet, success) {
|
this._applet.connect_device(item._device.device_path, function(applet, success) {
|
||||||
if (success) { // apply
|
if (success) { // apply
|
||||||
item._connected = ConnectionState.CONNECTED;
|
item._connected = ConnectionState.CONNECTED;
|
||||||
@ -213,11 +236,12 @@ Indicator.prototype = {
|
|||||||
item._connected = ConnectionState.DISCONNECTED;
|
item._connected = ConnectionState.DISCONNECTED;
|
||||||
menuitem.setToggleState(false);
|
menuitem.setToggleState(false);
|
||||||
}
|
}
|
||||||
|
menuitem.setStatus(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
item.menu.addMenuItem(menuitem);
|
item.menu.addMenuItem(item._connectedMenuitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
||||||
@ -244,27 +268,19 @@ Indicator.prototype = {
|
|||||||
|
|
||||||
switch (device.type) {
|
switch (device.type) {
|
||||||
case GnomeBluetoothApplet.Type.KEYBOARD:
|
case GnomeBluetoothApplet.Type.KEYBOARD:
|
||||||
item.menu.addAction(_("Keyboard Settings"), function() {
|
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
|
||||||
GLib.spawn_command_line_async('gnome-control-center keyboard');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case GnomeBluetoothApplet.Type.MOUSE:
|
case GnomeBluetoothApplet.Type.MOUSE:
|
||||||
item.menu.addAction(_("Mouse Settings"), function() {
|
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
|
||||||
GLib.spawn_command_line_async('gnome-control-center mouse');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case GnomeBluetoothApplet.Type.HEADSET:
|
case GnomeBluetoothApplet.Type.HEADSET:
|
||||||
case GnomeBluetoothApplet.Type.HEADPHONES:
|
case GnomeBluetoothApplet.Type.HEADPHONES:
|
||||||
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
||||||
item.menu.addAction(_("Sound Settings"), function() {
|
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||||
GLib.spawn_command_line_async('gnome-control-center sound');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateFullMenu: function() {
|
_updateFullMenu: function() {
|
||||||
@ -272,8 +288,6 @@ Indicator.prototype = {
|
|||||||
this._showAll(this._fullMenuItems);
|
this._showAll(this._fullMenuItems);
|
||||||
if (this._hasDevices)
|
if (this._hasDevices)
|
||||||
this._showAll(this._deviceItems);
|
this._showAll(this._deviceItems);
|
||||||
else
|
|
||||||
this._deviceSep.actor.hide();
|
|
||||||
} else {
|
} else {
|
||||||
this._hideAll(this._fullMenuItems);
|
this._hideAll(this._fullMenuItems);
|
||||||
this._hideAll(this._deviceItems);
|
this._hideAll(this._deviceItems);
|
||||||
|
@ -9,13 +9,11 @@ const Lang = imports.lang;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
function LayoutMenuItem() {
|
function LayoutMenuItem() {
|
||||||
this._init.apply(this, arguments);
|
this._init.apply(this, arguments);
|
||||||
}
|
}
|
||||||
@ -70,13 +68,33 @@ XKBIndicator.prototype = {
|
|||||||
this._sync_config();
|
this._sync_config();
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
|
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
|
||||||
|
Main.overview.hide();
|
||||||
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
|
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
|
||||||
}));
|
}));
|
||||||
this.menu.addAction(_("Localization Settings"), function() {
|
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
|
},
|
||||||
app.activate(-1);
|
|
||||||
});
|
_adjust_group_names: function(names) {
|
||||||
|
// Disambiguate duplicate names with a subscript
|
||||||
|
// This is O(N^2) to avoid sorting names
|
||||||
|
// but N <= 4 so who cares?
|
||||||
|
|
||||||
|
for (let i = 0; i < names.length; i++) {
|
||||||
|
let name = names[i];
|
||||||
|
let cnt = 0;
|
||||||
|
for (let j = i + 1; j < names.length; j++) {
|
||||||
|
if (names[j] == name) {
|
||||||
|
cnt++;
|
||||||
|
// U+2081 SUBSCRIPT ONE
|
||||||
|
names[j] = name + String.fromCharCode(0x2081 + cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cnt != 0)
|
||||||
|
names[i] = name + '\u2081';
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
},
|
},
|
||||||
|
|
||||||
_sync_config: function() {
|
_sync_config: function() {
|
||||||
@ -101,7 +119,7 @@ XKBIndicator.prototype = {
|
|||||||
for (let i = 0; i < this._labelActors.length; i++)
|
for (let i = 0; i < this._labelActors.length; i++)
|
||||||
this._labelActors[i].destroy();
|
this._labelActors[i].destroy();
|
||||||
|
|
||||||
let short_names = this._config.get_short_group_names();
|
let short_names = this._adjust_group_names(this._config.get_short_group_names());
|
||||||
|
|
||||||
this._selectedLayout = null;
|
this._selectedLayout = null;
|
||||||
this._layoutItems = [ ];
|
this._layoutItems = [ ];
|
||||||
|
@ -7,15 +7,13 @@ const Mainloop = imports.mainloop;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
const BUS_NAME = 'org.gnome.SettingsDaemon';
|
||||||
const _ = Gettext.gettext;
|
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||||
|
|
||||||
const BUS_NAME = 'org.gnome.PowerManager';
|
|
||||||
const OBJECT_PATH = '/org/gnome/PowerManager';
|
|
||||||
|
|
||||||
const UPDeviceType = {
|
const UPDeviceType = {
|
||||||
UNKNOWN: 0,
|
UNKNOWN: 0,
|
||||||
@ -43,7 +41,7 @@ const UPDeviceState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const PowerManagerInterface = {
|
const PowerManagerInterface = {
|
||||||
name: 'org.gnome.PowerManager',
|
name: 'org.gnome.SettingsDaemon.Power',
|
||||||
methods: [
|
methods: [
|
||||||
{ name: 'GetDevices', inSignature: '', outSignature: 'a(susbut)' },
|
{ name: 'GetDevices', inSignature: '', outSignature: 'a(susbut)' },
|
||||||
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
|
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
|
||||||
@ -77,15 +75,11 @@ Indicator.prototype = {
|
|||||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||||
this.menu.addMenuItem(this._batteryItem);
|
this.menu.addMenuItem(this._batteryItem);
|
||||||
|
|
||||||
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(this._deviceSep);
|
|
||||||
this._otherDevicePosition = 2;
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
this._otherDevicePosition = 2;
|
||||||
|
|
||||||
this.menu.addAction(_("Power Settings"),function() {
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
|
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||||
app.activate(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
||||||
this._devicesChanged();
|
this._devicesChanged();
|
||||||
@ -94,11 +88,9 @@ Indicator.prototype = {
|
|||||||
_readPrimaryDevice: function() {
|
_readPrimaryDevice: function() {
|
||||||
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) {
|
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
this._checkError(error);
|
|
||||||
this._hasPrimary = false;
|
this._hasPrimary = false;
|
||||||
this._primaryDeviceId = null;
|
this._primaryDeviceId = null;
|
||||||
this._batteryItem.actor.hide();
|
this._batteryItem.actor.hide();
|
||||||
this._deviceSep.actor.hide();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||||
@ -115,25 +107,22 @@ Indicator.prototype = {
|
|||||||
let timestring;
|
let timestring;
|
||||||
if (time > 60) {
|
if (time > 60) {
|
||||||
if (minutes == 0) {
|
if (minutes == 0) {
|
||||||
timestring = Gettext.ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
||||||
} else {
|
} else {
|
||||||
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
||||||
let template = _("%d %s %d %s remaining");
|
let template = _("%d %s %d %s remaining");
|
||||||
|
|
||||||
timestring = template.format (hours, Gettext.ngettext("hour", "hours", hours), minutes, Gettext.ngettext("minute", "minutes", minutes));
|
timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
timestring = Gettext.ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
||||||
this._batteryItem.label.text = timestring;
|
this._batteryItem.label.text = timestring;
|
||||||
}
|
}
|
||||||
this._primaryPercentage.text = Math.round(percentage) + '%';
|
this._primaryPercentage.text = Math.round(percentage) + '%';
|
||||||
this._batteryItem.actor.show();
|
this._batteryItem.actor.show();
|
||||||
if (this._deviceItems.length > 0)
|
|
||||||
this._deviceSep.actor.show();
|
|
||||||
} else {
|
} else {
|
||||||
this._hasPrimary = false;
|
this._hasPrimary = false;
|
||||||
this._batteryItem.actor.hide();
|
this._batteryItem.actor.hide();
|
||||||
this._deviceSep.actor.hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._primaryDeviceId = device_id;
|
this._primaryDeviceId = device_id;
|
||||||
@ -146,8 +135,6 @@ Indicator.prototype = {
|
|||||||
this._deviceItems = [];
|
this._deviceItems = [];
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
this._checkError(error);
|
|
||||||
this._deviceSep.actor.hide();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,11 +149,6 @@ Indicator.prototype = {
|
|||||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
||||||
position++;
|
position++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._hasPrimary && position > 0)
|
|
||||||
this._deviceSep.actor.show();
|
|
||||||
else
|
|
||||||
this._deviceSep.actor.hide();
|
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -177,21 +159,12 @@ Indicator.prototype = {
|
|||||||
this.setGIcon(gicon);
|
this.setGIcon(gicon);
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
} else {
|
} else {
|
||||||
this._checkError(error);
|
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._readPrimaryDevice();
|
this._readPrimaryDevice();
|
||||||
this._readOtherDevices();
|
this._readOtherDevices();
|
||||||
},
|
|
||||||
|
|
||||||
_checkError: function(error) {
|
|
||||||
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
|
|
||||||
Util.killall('gnome-power-manager');
|
|
||||||
Util.spawn(['gnome-power-manager']);
|
|
||||||
this._restarted = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,13 +9,11 @@ const Gvc = imports.gi.Gvc;
|
|||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
|
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
|
||||||
|
|
||||||
const VOLUME_NOTIFY_ID = 1;
|
const VOLUME_NOTIFY_ID = 1;
|
||||||
@ -31,7 +29,7 @@ Indicator.prototype = {
|
|||||||
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
|
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
|
||||||
|
|
||||||
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
|
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
|
||||||
this._control.connect('ready', Lang.bind(this, this._onControlReady));
|
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
|
||||||
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));
|
||||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||||
@ -49,8 +47,7 @@ Indicator.prototype = {
|
|||||||
this.menu.addMenuItem(this._outputTitle);
|
this.menu.addMenuItem(this._outputTitle);
|
||||||
this.menu.addMenuItem(this._outputSlider);
|
this.menu.addMenuItem(this._outputSlider);
|
||||||
|
|
||||||
this._separator = new PopupMenu.PopupSeparatorMenuItem();
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.menu.addMenuItem(this._separator);
|
|
||||||
|
|
||||||
this._input = null;
|
this._input = null;
|
||||||
this._inputVolumeId = 0;
|
this._inputVolumeId = 0;
|
||||||
@ -63,10 +60,7 @@ Indicator.prototype = {
|
|||||||
this.menu.addMenuItem(this._inputSlider);
|
this.menu.addMenuItem(this._inputSlider);
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.menu.addAction(_("Sound Settings"), function() {
|
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
|
|
||||||
app.activate(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
this._control.open();
|
this._control.open();
|
||||||
@ -103,9 +97,14 @@ Indicator.prototype = {
|
|||||||
this._notifyVolumeChange();
|
this._notifyVolumeChange();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onControlReady: function() {
|
_onControlStateChanged: function() {
|
||||||
this._readOutput();
|
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||||
this._readInput();
|
this._readOutput();
|
||||||
|
this._readInput();
|
||||||
|
this.actor.show();
|
||||||
|
} else {
|
||||||
|
this.actor.hide();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_readOutput: function() {
|
_readOutput: function() {
|
||||||
@ -141,7 +140,6 @@ Indicator.prototype = {
|
|||||||
this._mutedChanged (null, null, '_input');
|
this._mutedChanged (null, null, '_input');
|
||||||
this._volumeChanged (null, null, '_input');
|
this._volumeChanged (null, null, '_input');
|
||||||
} else {
|
} else {
|
||||||
this._separator.actor.hide();
|
|
||||||
this._inputTitle.actor.hide();
|
this._inputTitle.actor.hide();
|
||||||
this._inputSlider.actor.hide();
|
this._inputSlider.actor.hide();
|
||||||
}
|
}
|
||||||
@ -164,11 +162,9 @@ Indicator.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showInput) {
|
if (showInput) {
|
||||||
this._separator.actor.show();
|
|
||||||
this._inputTitle.actor.show();
|
this._inputTitle.actor.show();
|
||||||
this._inputSlider.actor.show();
|
this._inputSlider.actor.show();
|
||||||
} else {
|
} else {
|
||||||
this._separator.actor.hide();
|
|
||||||
this._inputTitle.actor.hide();
|
this._inputTitle.actor.hide();
|
||||||
this._inputSlider.actor.hide();
|
this._inputSlider.actor.hide();
|
||||||
}
|
}
|
||||||
|
@ -1,307 +0,0 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
||||||
|
|
||||||
const Gdm = imports.gi.Gdm;
|
|
||||||
const DBus = imports.dbus;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const GLib = imports.gi.GLib;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const St = imports.gi.St;
|
|
||||||
const Tp = imports.gi.TelepathyGLib;
|
|
||||||
const UPowerGlib = imports.gi.UPowerGlib;
|
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
|
||||||
const Util = imports.misc.util;
|
|
||||||
|
|
||||||
const BUS_NAME = 'org.gnome.ScreenSaver';
|
|
||||||
const OBJECT_PATH = '/org/gnome/ScreenSaver';
|
|
||||||
|
|
||||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
|
||||||
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 ScreenSaverInterface = {
|
|
||||||
name: BUS_NAME,
|
|
||||||
methods: [ { name: 'Lock', inSignature: '' } ]
|
|
||||||
};
|
|
||||||
|
|
||||||
let ScreenSaverProxy = DBus.makeProxyClass(ScreenSaverInterface);
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
function StatusMenuButton() {
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusMenuButton.prototype = {
|
|
||||||
__proto__: PanelMenu.Button.prototype,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
|
||||||
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
|
|
||||||
this.actor.set_child(box);
|
|
||||||
|
|
||||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
|
||||||
|
|
||||||
this._gdm = Gdm.UserManager.ref_default();
|
|
||||||
this._gdm.queue_load();
|
|
||||||
|
|
||||||
this._user = this._gdm.get_user(GLib.get_user_name());
|
|
||||||
this._presence = new GnomeSession.Presence();
|
|
||||||
this._presenceItems = {};
|
|
||||||
this._session = new GnomeSession.SessionManager();
|
|
||||||
|
|
||||||
this._account_mgr = Tp.AccountManager.dup()
|
|
||||||
|
|
||||||
this._upClient = new UPowerGlib.Client();
|
|
||||||
this._screenSaverProxy = new ScreenSaverProxy(DBus.session, BUS_NAME, OBJECT_PATH);
|
|
||||||
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._availableIcon = new St.Icon({ icon_name: 'user-available', style_class: 'popup-menu-icon' });
|
|
||||||
this._busyIcon = new St.Icon({ icon_name: 'user-busy', style_class: 'popup-menu-icon' });
|
|
||||||
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', style_class: 'popup-menu-icon' });
|
|
||||||
this._idleIcon = new St.Icon({ icon_name: 'user-idle', style_class: 'popup-menu-icon' });
|
|
||||||
|
|
||||||
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
|
|
||||||
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
|
|
||||||
|
|
||||||
this._name = new St.Label();
|
|
||||||
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._createSubMenu();
|
|
||||||
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
|
|
||||||
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
|
|
||||||
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
|
|
||||||
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));
|
|
||||||
this._updateSwitchUser();
|
|
||||||
this._updateLogout();
|
|
||||||
this._updateLockScreen();
|
|
||||||
|
|
||||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
this._user.disconnect(this._userLoadedId);
|
|
||||||
this._user.disconnect(this._userChangedId);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateUserName: function() {
|
|
||||||
if (this._user.is_loaded)
|
|
||||||
this._name.set_text(this._user.get_real_name());
|
|
||||||
else
|
|
||||||
this._name.set_text("");
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSessionSeparator: function() {
|
|
||||||
let showSeparator = this._loginScreenItem.actor.visible ||
|
|
||||||
this._logoutItem.actor.visible ||
|
|
||||||
this._lockScreenItem.actor.visible;
|
|
||||||
if (showSeparator)
|
|
||||||
this._sessionSeparator.actor.show();
|
|
||||||
else
|
|
||||||
this._sessionSeparator.actor.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSwitchUser: function() {
|
|
||||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
|
||||||
if (allowSwitch && this._gdm.can_switch ())
|
|
||||||
this._loginScreenItem.actor.show();
|
|
||||||
else
|
|
||||||
this._loginScreenItem.actor.hide();
|
|
||||||
this._updateSessionSeparator();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateLogout: function() {
|
|
||||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
|
||||||
if (allowLogout)
|
|
||||||
this._logoutItem.actor.show();
|
|
||||||
else
|
|
||||||
this._logoutItem.actor.hide();
|
|
||||||
this._updateSessionSeparator();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateLockScreen: function() {
|
|
||||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
|
||||||
if (allowLockScreen)
|
|
||||||
this._lockScreenItem.actor.show();
|
|
||||||
else
|
|
||||||
this._lockScreenItem.actor.hide();
|
|
||||||
this._updateSessionSeparator();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSuspendOrPowerOff: function() {
|
|
||||||
this._haveSuspend = this._upClient.get_can_suspend();
|
|
||||||
|
|
||||||
if (!this._suspendOrPowerOffItem)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If we can't suspend show Power Off... instead
|
|
||||||
// and disable the alt key
|
|
||||||
if (!this._haveSuspend) {
|
|
||||||
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
|
||||||
} else {
|
|
||||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updatePresenceIcon: function(presence, status) {
|
|
||||||
if (status == GnomeSession.PresenceStatus.AVAILABLE)
|
|
||||||
this._iconBox.child = this._availableIcon;
|
|
||||||
else if (status == GnomeSession.PresenceStatus.BUSY)
|
|
||||||
this._iconBox.child = this._busyIcon;
|
|
||||||
else if (status == GnomeSession.PresenceStatus.INVISIBLE)
|
|
||||||
this._iconBox.child = this._invisibleIcon;
|
|
||||||
else
|
|
||||||
this._iconBox.child = this._idleIcon;
|
|
||||||
|
|
||||||
for (let itemStatus in this._presenceItems)
|
|
||||||
this._presenceItems[itemStatus].setShowDot(itemStatus == status);
|
|
||||||
},
|
|
||||||
|
|
||||||
_createSubMenu: function() {
|
|
||||||
let item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
|
|
||||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
|
|
||||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("My Account"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._lockScreenItem = 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.PopupSeparatorMenuItem();
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._sessionSeparator = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
|
||||||
_("Power Off..."));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._suspendOrPowerOffItem = item;
|
|
||||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
|
||||||
this._updateSuspendOrPowerOff();
|
|
||||||
},
|
|
||||||
|
|
||||||
_setPresenceStatus: function(item, event, status) {
|
|
||||||
this._presence.setStatus(status);
|
|
||||||
|
|
||||||
this._setIMStatus(status);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMyAccountActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
|
|
||||||
app.activate(-1);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onPreferencesActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
|
|
||||||
app.activate(-1);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onLockScreenActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
this._screenSaverProxy.LockRemote();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onLoginScreenActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
this._gdm.goto_login_session();
|
|
||||||
this._onLockScreenActivate();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onQuitSessionActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
this._session.LogoutRemote(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onSuspendOrPowerOffActivate: function() {
|
|
||||||
Main.overview.hide();
|
|
||||||
|
|
||||||
if (this._haveSuspend &&
|
|
||||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
|
||||||
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
|
|
||||||
this._upClient.suspend_sync(null);
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._session.ShutdownRemote();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_setIMStatus: function(session_status) {
|
|
||||||
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
|
|
||||||
let type, status;
|
|
||||||
|
|
||||||
// We change the IM presence only if there are connected accounts
|
|
||||||
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
|
|
||||||
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
|
|
||||||
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
|
|
||||||
presence_type == Tp.ConnectionPresenceType.ERROR)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
|
|
||||||
type = Tp.ConnectionPresenceType.AVAILABLE;
|
|
||||||
status = "available";
|
|
||||||
}
|
|
||||||
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
|
|
||||||
type = Tp.ConnectionPresenceType.BUSY;
|
|
||||||
status = "busy";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._account_mgr.set_all_requested_presences(type, status, msg);
|
|
||||||
}
|
|
||||||
};
|
|
646
js/ui/userMenu.js
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const AccountsService = imports.gi.AccountsService;
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Tp = imports.gi.TelepathyGLib;
|
||||||
|
const UPowerGlib = imports.gi.UPowerGlib;
|
||||||
|
|
||||||
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
const ScreenSaver = imports.misc.screenSaver;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
|
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||||
|
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 WRAP_WIDTH = 150;
|
||||||
|
const DIALOG_ICON_SIZE = 64;
|
||||||
|
|
||||||
|
const IMStatus = {
|
||||||
|
AVAILABLE: 0,
|
||||||
|
BUSY: 1,
|
||||||
|
HIDDEN: 2,
|
||||||
|
AWAY: 3,
|
||||||
|
IDLE: 4,
|
||||||
|
OFFLINE: 5,
|
||||||
|
LAST: 6
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
function IMStatusItem(label, iconName) {
|
||||||
|
this._init(label, iconName);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMStatusItem.prototype = {
|
||||||
|
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||||
|
|
||||||
|
_init: function(label, iconName) {
|
||||||
|
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
||||||
|
|
||||||
|
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.addActor(this.label);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function IMUserNameItem() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
IMUserNameItem.prototype = {
|
||||||
|
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
PopupMenu.PopupBaseMenuItem.prototype._init.call(this,
|
||||||
|
{ reactive: 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._wrapper.add_actor(this.label);
|
||||||
|
},
|
||||||
|
|
||||||
|
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
|
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_width(-1);
|
||||||
|
if (alloc.natural_size > WRAP_WIDTH)
|
||||||
|
alloc.natural_size = WRAP_WIDTH;
|
||||||
|
},
|
||||||
|
|
||||||
|
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
|
||||||
|
let minWidth, natWidth;
|
||||||
|
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
|
||||||
|
[minWidth, natWidth] = this.label.get_preferred_width(-1);
|
||||||
|
if (natWidth > WRAP_WIDTH) {
|
||||||
|
alloc.min_size *= 2;
|
||||||
|
alloc.natural_size *= 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_wrapperAllocate: function(actor, box, flags) {
|
||||||
|
let availWidth = box.x2 - box.x1;
|
||||||
|
let availHeight = box.y2 - box.y1;
|
||||||
|
this.label.allocate(box, flags);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function IMStatusChooserItem() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
IMStatusChooserItem.prototype = {
|
||||||
|
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
PopupMenu.PopupBaseMenuItem.prototype._init.call (this,
|
||||||
|
{ reactive: false,
|
||||||
|
style_class: 'status-chooser' });
|
||||||
|
|
||||||
|
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
|
||||||
|
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');
|
||||||
|
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
|
||||||
|
|
||||||
|
item = new IMStatusItem(_("Busy"), 'user-busy');
|
||||||
|
this._combo.addMenuItem(item, IMStatus.BUSY);
|
||||||
|
|
||||||
|
item = new IMStatusItem(_("Hidden"), 'user-invisible');
|
||||||
|
this._combo.addMenuItem(item, IMStatus.HIDDEN);
|
||||||
|
|
||||||
|
item = new IMStatusItem(_("Away"), 'user-away');
|
||||||
|
this._combo.addMenuItem(item, IMStatus.AWAY);
|
||||||
|
|
||||||
|
item = new IMStatusItem(_("Idle"), 'user-idle');
|
||||||
|
this._combo.addMenuItem(item, IMStatus.IDLE);
|
||||||
|
|
||||||
|
item = new IMStatusItem(_("Unavailable"), 'user-offline');
|
||||||
|
this._combo.addMenuItem(item, IMStatus.OFFLINE);
|
||||||
|
|
||||||
|
this._combo.connect('active-item-changed',
|
||||||
|
Lang.bind(this, this._changeIMStatus));
|
||||||
|
|
||||||
|
this._presence = new GnomeSession.Presence();
|
||||||
|
this._presence.getStatus(Lang.bind(this, this._sessionStatusChanged));
|
||||||
|
this._presence.connect('StatusChanged',
|
||||||
|
Lang.bind(this, this._sessionStatusChanged));
|
||||||
|
|
||||||
|
this._previousPresence = undefined;
|
||||||
|
|
||||||
|
this._accountMgr = Tp.AccountManager.dup()
|
||||||
|
this._accountMgr.connect('most-available-presence-changed',
|
||||||
|
Lang.bind(this, this._IMStatusChanged));
|
||||||
|
this._accountMgr.prepare_async(null, Lang.bind(this,
|
||||||
|
function(mgr) {
|
||||||
|
let [presence, s, msg] = mgr.get_most_available_presence();
|
||||||
|
|
||||||
|
this._previousPresence = presence;
|
||||||
|
this._IMStatusChanged(mgr, presence, s, msg);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._userManager = AccountsService.UserManager.get_default();
|
||||||
|
|
||||||
|
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||||
|
|
||||||
|
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._updateUser));
|
||||||
|
this._userChangedId = this._user.connect('changed',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._updateUser));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Override getColumnWidths()/setColumnWidths() to make the item
|
||||||
|
// independent from the overall column layout of the menu
|
||||||
|
getColumnWidths: function() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
setColumnWidths: function(widths) {
|
||||||
|
this._columnWidths = PopupMenu.PopupBaseMenuItem.prototype.getColumnWidths.call(this);
|
||||||
|
let sectionWidths = this._section.getColumnWidths();
|
||||||
|
this._section.setColumnWidths(sectionWidths);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateUser: function() {
|
||||||
|
let iconFile = null;
|
||||||
|
if (this._user.is_loaded) {
|
||||||
|
this._name.label.set_text(this._user.get_real_name());
|
||||||
|
iconFile = this._user.get_icon_file();
|
||||||
|
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||||
|
iconFile = null;
|
||||||
|
} else {
|
||||||
|
this._name.label.set_text("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iconFile)
|
||||||
|
this._setIconFromFile(iconFile);
|
||||||
|
else
|
||||||
|
this._setIconFromName('avatar-default');
|
||||||
|
},
|
||||||
|
|
||||||
|
_setIconFromFile: function(iconFile) {
|
||||||
|
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||||
|
this._iconBin.child = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setIconFromName: function(iconName) {
|
||||||
|
this._iconBin.set_style(null);
|
||||||
|
|
||||||
|
if (iconName != null) {
|
||||||
|
let textureCache = St.TextureCache.get_default();
|
||||||
|
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
||||||
|
iconName,
|
||||||
|
St.IconType.SYMBOLIC,
|
||||||
|
DIALOG_ICON_SIZE);
|
||||||
|
|
||||||
|
this._iconBin.child = icon;
|
||||||
|
this._iconBin.show();
|
||||||
|
} else {
|
||||||
|
this._iconBin.child = null;
|
||||||
|
this._iconBin.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_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';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_IMStatusChanged: function(accountMgr, presence, status, message) {
|
||||||
|
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||||
|
this._presence.setStatus(GnomeSession.PresenceStatus.AVAILABLE);
|
||||||
|
|
||||||
|
if (!this._expectedPresence || presence != this._expectedPresence)
|
||||||
|
this._previousPresence = presence;
|
||||||
|
else
|
||||||
|
this._expectedPresence = undefined;
|
||||||
|
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionStatusChanged: function(sessionPresence, sessionStatus) {
|
||||||
|
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||||
|
let newPresence, status;
|
||||||
|
|
||||||
|
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE) {
|
||||||
|
newPresence = this._previousPresence;
|
||||||
|
} else if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
|
||||||
|
// Only change presence if the current one is "more present" than
|
||||||
|
// busy, or if coming back from idle
|
||||||
|
if (presence == Tp.ConnectionPresenceType.AVAILABLE ||
|
||||||
|
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
|
||||||
|
newPresence = Tp.ConnectionPresenceType.BUSY;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
|
||||||
|
// Only change presence if the current one is "more present" than
|
||||||
|
// idle
|
||||||
|
if (presence != Tp.ConnectionPresenceType.OFFLINE)
|
||||||
|
newPresence = Tp.ConnectionPresenceType.EXTENDED_AWAY;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPresence == undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
status = this._statusForPresence(newPresence);
|
||||||
|
msg = msg ? msg : "";
|
||||||
|
|
||||||
|
this._expectedPresence = newPresence;
|
||||||
|
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function UserMenuButton() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserMenuButton.prototype = {
|
||||||
|
__proto__: PanelMenu.Button.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||||
|
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
|
||||||
|
this.actor.set_child(box);
|
||||||
|
|
||||||
|
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_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._account_mgr = Tp.AccountManager.dup()
|
||||||
|
|
||||||
|
this._upClient = new UPowerGlib.Client();
|
||||||
|
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||||
|
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',
|
||||||
|
style_class: 'popup-menu-icon' });
|
||||||
|
this._availableIcon = new St.Icon({ icon_name: 'user-available',
|
||||||
|
style_class: 'popup-menu-icon' });
|
||||||
|
this._busyIcon = new St.Icon({ icon_name: 'user-busy',
|
||||||
|
style_class: 'popup-menu-icon' });
|
||||||
|
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible',
|
||||||
|
style_class: 'popup-menu-icon' });
|
||||||
|
this._awayIcon = new St.Icon({ icon_name: 'user-away',
|
||||||
|
style_class: 'popup-menu-icon' });
|
||||||
|
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
|
||||||
|
style_class: 'popup-menu-icon' });
|
||||||
|
|
||||||
|
this._presence.connect('StatusChanged',
|
||||||
|
Lang.bind(this, this._updateSwitch));
|
||||||
|
this._presence.getStatus(Lang.bind(this, this._updateSwitch));
|
||||||
|
|
||||||
|
this._account_mgr.connect('most-available-presence-changed',
|
||||||
|
Lang.bind(this, this._updatePresenceIcon));
|
||||||
|
this._account_mgr.prepare_async(null, Lang.bind(this,
|
||||||
|
function(mgr) {
|
||||||
|
let [presence, s, msg] = mgr.get_most_available_presence();
|
||||||
|
this._updatePresenceIcon(mgr, presence, s, msg);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._name = new St.Label();
|
||||||
|
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._createSubMenu();
|
||||||
|
this._userManager.connect('notify::is-loaded',
|
||||||
|
Lang.bind(this, this._updateSwitchUser));
|
||||||
|
this._userManager.connect('user-added',
|
||||||
|
Lang.bind(this, this._updateSwitchUser));
|
||||||
|
this._userManager.connect('user-removed',
|
||||||
|
Lang.bind(this, this._updateSwitchUser));
|
||||||
|
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));
|
||||||
|
this._updateSwitchUser();
|
||||||
|
this._updateLogout();
|
||||||
|
this._updateLockScreen();
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
this._updateHaveShutdown();
|
||||||
|
}));
|
||||||
|
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||||
|
Lang.bind(this, this._updateHaveShutdown));
|
||||||
|
|
||||||
|
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this._user.disconnect(this._userLoadedId);
|
||||||
|
this._user.disconnect(this._userChangedId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateUserName: function() {
|
||||||
|
if (this._user.is_loaded)
|
||||||
|
this._name.set_text(this._user.get_real_name());
|
||||||
|
else
|
||||||
|
this._name.set_text("");
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSwitchUser: function() {
|
||||||
|
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||||
|
if (allowSwitch && this._userManager.can_switch ())
|
||||||
|
this._loginScreenItem.actor.show();
|
||||||
|
else
|
||||||
|
this._loginScreenItem.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLogout: function() {
|
||||||
|
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||||
|
if (allowLogout)
|
||||||
|
this._logoutItem.actor.show();
|
||||||
|
else
|
||||||
|
this._logoutItem.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLockScreen: function() {
|
||||||
|
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||||
|
if (allowLockScreen)
|
||||||
|
this._lockScreenItem.actor.show();
|
||||||
|
else
|
||||||
|
this._lockScreenItem.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateHaveShutdown: function() {
|
||||||
|
this._session.CanShutdownRemote(Lang.bind(this,
|
||||||
|
function(result, error) {
|
||||||
|
if (!error) {
|
||||||
|
this._haveShutdown = result;
|
||||||
|
this._updateSuspendOrPowerOff();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSuspendOrPowerOff: function() {
|
||||||
|
this._haveSuspend = this._upClient.get_can_suspend();
|
||||||
|
|
||||||
|
if (!this._suspendOrPowerOffItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._haveShutdown && !this._haveSuspend)
|
||||||
|
this._suspendOrPowerOffItem.actor.hide();
|
||||||
|
else
|
||||||
|
this._suspendOrPowerOffItem.actor.show();
|
||||||
|
|
||||||
|
// If we can't suspend show Power Off... instead
|
||||||
|
// and disable the alt key
|
||||||
|
if (!this._haveSuspend) {
|
||||||
|
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
||||||
|
} else if (!this._haveShutdown) {
|
||||||
|
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
|
||||||
|
} else {
|
||||||
|
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSwitch: function(presence, status) {
|
||||||
|
let active = status == GnomeSession.PresenceStatus.BUSY;
|
||||||
|
this._dontDisturbSwitch.setToggleState(active);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePresenceIcon: function(accountMgr, presence, status, message) {
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createSubMenu: function() {
|
||||||
|
let item;
|
||||||
|
|
||||||
|
item = new IMStatusChooserItem();
|
||||||
|
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupSwitchMenuItem(_("Do Not Disturb"));
|
||||||
|
item.connect('activate', Lang.bind(this, this._updatePresenceStatus));
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
this._dontDisturbSwitch = item;
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
|
||||||
|
item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||||
|
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
|
||||||
|
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
this._lockScreenItem = 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.PopupSeparatorMenuItem();
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
|
||||||
|
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
||||||
|
_("Power Off..."));
|
||||||
|
this.menu.addMenuItem(item);
|
||||||
|
this._suspendOrPowerOffItem = item;
|
||||||
|
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||||
|
this._updateSuspendOrPowerOff();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePresenceStatus: function(item, event) {
|
||||||
|
let status = item.state ? GnomeSession.PresenceStatus.BUSY
|
||||||
|
: GnomeSession.PresenceStatus.AVAILABLE;
|
||||||
|
this._presence.setStatus(status);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMyAccountActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
|
||||||
|
app.activate();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onOnlineAccountsActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
|
||||||
|
app.activate(-1);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPreferencesActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
|
||||||
|
app.activate();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLockScreenActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
this._screenSaverProxy.LockRemote();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLoginScreenActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
// Ensure we only move to GDM after the screensaver has activated; in some
|
||||||
|
// OS configurations, the X server may block event processing on VT switch
|
||||||
|
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||||
|
this._userManager.goto_login_session();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onQuitSessionActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
this._session.LogoutRemote(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSuspendOrPowerOffActivate: function() {
|
||||||
|
Main.overview.hide();
|
||||||
|
|
||||||
|
if (this._haveSuspend &&
|
||||||
|
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||||
|
// Ensure we only suspend after the screensaver has activated
|
||||||
|
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||||
|
this._upClient.suspend_sync(null);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this._session.ShutdownRemote();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -8,8 +8,6 @@ const Signals = imports.signals;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Search = imports.ui.search;
|
const Search = imports.ui.search;
|
||||||
@ -143,19 +141,21 @@ SearchTab.prototype = {
|
|||||||
'edit-find');
|
'edit-find');
|
||||||
|
|
||||||
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
|
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
|
||||||
this._text.connect('activate', Lang.bind(this, function (se) {
|
this._text.connect('key-press-event', Lang.bind(this, function (o, e) {
|
||||||
if (this._searchTimeoutId > 0) {
|
// We can't connect to 'activate' here because search providers
|
||||||
Mainloop.source_remove(this._searchTimeoutId);
|
// might want to do something with the modifiers in activateSelected.
|
||||||
this._doSearch();
|
let symbol = e.get_key_symbol();
|
||||||
|
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||||
|
if (this._searchTimeoutId > 0) {
|
||||||
|
Mainloop.source_remove(this._searchTimeoutId);
|
||||||
|
this._doSearch();
|
||||||
|
}
|
||||||
|
this._searchResults.activateSelected();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
this._searchResults.activateSelected();
|
return false;
|
||||||
return true;
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._entry.connect('secondary-icon-clicked', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._reset();
|
|
||||||
}));
|
|
||||||
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||||
|
|
||||||
global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
|
global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
|
||||||
@ -166,7 +166,14 @@ SearchTab.prototype = {
|
|||||||
hide: function() {
|
hide: function() {
|
||||||
BaseTab.prototype.hide.call(this);
|
BaseTab.prototype.hide.call(this);
|
||||||
|
|
||||||
this._reset();
|
// Leave the entry focused when it doesn't have any text;
|
||||||
|
// when replacing a selected search term, Clutter emits
|
||||||
|
// two 'text-changed' signals, one for deleting the previous
|
||||||
|
// text and one for the new one - the second one is handled
|
||||||
|
// incorrectly when we remove focus
|
||||||
|
// (https://bugzilla.gnome.org/show_bug.cgi?id=636341) */
|
||||||
|
if (this._text.text != '')
|
||||||
|
this._reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
_reset: function () {
|
_reset: function () {
|
||||||
@ -226,7 +233,7 @@ SearchTab.prototype = {
|
|||||||
if (this._iconClickedId == 0) {
|
if (this._iconClickedId == 0) {
|
||||||
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
|
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.reset();
|
this._reset();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
this._activate();
|
this._activate();
|
||||||
@ -290,7 +297,7 @@ SearchTab.prototype = {
|
|||||||
_doSearch: function () {
|
_doSearch: function () {
|
||||||
this._searchTimeoutId = 0;
|
this._searchTimeoutId = 0;
|
||||||
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||||
this._searchResults.updateSearch(text);
|
this._searchResults.doSearch(text);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
@ -18,8 +16,7 @@ WindowAttentionHandler.prototype = {
|
|||||||
this._tracker = Shell.WindowTracker.get_default();
|
this._tracker = Shell.WindowTracker.get_default();
|
||||||
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
||||||
|
|
||||||
let display = global.screen.get_display();
|
global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
||||||
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onStartupSequenceChanged : function(tracker) {
|
_onStartupSequenceChanged : function(tracker) {
|
||||||
|
@ -6,6 +6,7 @@ const Gio = imports.gi.Gio;
|
|||||||
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 AltTab = imports.ui.altTab;
|
const AltTab = imports.ui.altTab;
|
||||||
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
|
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
|
||||||
@ -22,8 +23,7 @@ function getDimShader() {
|
|||||||
if (dimShader === null)
|
if (dimShader === null)
|
||||||
return null;
|
return null;
|
||||||
if (!dimShader) {
|
if (!dimShader) {
|
||||||
let [success, source, length] = GLib.file_get_contents(global.datadir +
|
let source = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
|
||||||
'/shaders/dim-window.glsl');
|
|
||||||
try {
|
try {
|
||||||
let shader = new Clutter.Shader();
|
let shader = new Clutter.Shader();
|
||||||
shader.set_fragment_source(source, -1);
|
shader.set_fragment_source(source, -1);
|
||||||
@ -119,6 +119,8 @@ WindowManager.prototype = {
|
|||||||
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
|
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
|
||||||
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
|
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
|
||||||
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
|
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
|
||||||
|
this.setKeybindingHandler('switch_windows_backward', Lang.bind(this, this._startAppSwitcher));
|
||||||
|
this.setKeybindingHandler('switch_group_backward', Lang.bind(this, this._startAppSwitcher));
|
||||||
this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
|
this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
|
||||||
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||||
@ -180,7 +182,7 @@ WindowManager.prototype = {
|
|||||||
*/
|
*/
|
||||||
this._minimizing.push(actor);
|
this._minimizing.push(actor);
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
let xDest = primary.x;
|
let xDest = primary.x;
|
||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||||
xDest += primary.width;
|
xDest += primary.width;
|
||||||
@ -237,7 +239,7 @@ WindowManager.prototype = {
|
|||||||
_hasAttachedDialogs: function(window, ignoreWindow) {
|
_hasAttachedDialogs: function(window, ignoreWindow) {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
window.foreach_transient(function(win) {
|
window.foreach_transient(function(win) {
|
||||||
if (win != ignoreWindow && win.get_window_type() == Meta.WindowType.MODAL_DIALOG)
|
if (win != ignoreWindow && win.is_attached_dialog())
|
||||||
count++;
|
count++;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -245,7 +247,7 @@ WindowManager.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_checkDimming: function(window, ignoreWindow) {
|
_checkDimming: function(window, ignoreWindow) {
|
||||||
let shouldDim = Meta.prefs_get_attach_modal_dialogs() && this._hasAttachedDialogs(window, ignoreWindow);
|
let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
|
||||||
|
|
||||||
if (shouldDim && !window._dimmed) {
|
if (shouldDim && !window._dimmed) {
|
||||||
window._dimmed = true;
|
window._dimmed = true;
|
||||||
@ -307,9 +309,7 @@ WindowManager.prototype = {
|
|||||||
|
|
||||||
actor._windowType = type;
|
actor._windowType = type;
|
||||||
}));
|
}));
|
||||||
if (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
if (actor.meta_window.is_attached_dialog()) {
|
||||||
&& Meta.prefs_get_attach_modal_dialogs()
|
|
||||||
&& actor.get_meta_window().get_transient_for()) {
|
|
||||||
this._checkDimming(actor.get_meta_window().get_transient_for());
|
this._checkDimming(actor.get_meta_window().get_transient_for());
|
||||||
if (this._shouldAnimate()) {
|
if (this._shouldAnimate()) {
|
||||||
actor.set_scale(1.0, 0.0);
|
actor.set_scale(1.0, 0.0);
|
||||||
@ -371,7 +371,6 @@ WindowManager.prototype = {
|
|||||||
|
|
||||||
_destroyWindow : function(shellwm, actor) {
|
_destroyWindow : function(shellwm, actor) {
|
||||||
let window = actor.meta_window;
|
let window = actor.meta_window;
|
||||||
let parent = window.get_transient_for();
|
|
||||||
if (actor._notifyWindowTypeSignalId) {
|
if (actor._notifyWindowTypeSignalId) {
|
||||||
window.disconnect(actor._notifyWindowTypeSignalId);
|
window.disconnect(actor._notifyWindowTypeSignalId);
|
||||||
actor._notifyWindowTypeSignalId = 0;
|
actor._notifyWindowTypeSignalId = 0;
|
||||||
@ -381,12 +380,14 @@ WindowManager.prototype = {
|
|||||||
return win != window;
|
return win != window;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
while (window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
if (window.is_attached_dialog()) {
|
||||||
&& parent) {
|
let parent = window.get_transient_for();
|
||||||
this._checkDimming(parent, window);
|
this._checkDimming(parent, window);
|
||||||
if (!Meta.prefs_get_attach_modal_dialogs()
|
if (!this._shouldAnimate()) {
|
||||||
|| !this._shouldAnimate())
|
shellwm.completed_destroy(actor);
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
actor.set_scale(1.0, 1.0);
|
actor.set_scale(1.0, 1.0);
|
||||||
actor.show();
|
actor.show();
|
||||||
this._destroying.push(actor);
|
this._destroying.push(actor);
|
||||||
@ -534,7 +535,7 @@ WindowManager.prototype = {
|
|||||||
|
|
||||||
let tabPopup = new AltTab.AltTabPopup();
|
let tabPopup = new AltTab.AltTabPopup();
|
||||||
|
|
||||||
if (!tabPopup.show(backwards, binding == 'switch_group'))
|
if (!tabPopup.show(backwards, binding))
|
||||||
tabPopup.destroy();
|
tabPopup.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GConf = imports.gi.GConf;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
@ -27,6 +28,8 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
|
|||||||
|
|
||||||
const DRAGGING_WINDOW_OPACITY = 100;
|
const DRAGGING_WINDOW_OPACITY = 100;
|
||||||
|
|
||||||
|
const BUTTON_LAYOUT_KEY = '/desktop/gnome/shell/windows/button_layout';
|
||||||
|
|
||||||
// Define a layout scheme for small window counts. For larger
|
// Define a layout scheme for small window counts. For larger
|
||||||
// counts we fall back to an algorithm. We need more schemes here
|
// counts we fall back to an algorithm. We need more schemes here
|
||||||
// unless we have a really good algorithm.
|
// unless we have a really good algorithm.
|
||||||
@ -91,22 +94,38 @@ function WindowClone(realWindow) {
|
|||||||
|
|
||||||
WindowClone.prototype = {
|
WindowClone.prototype = {
|
||||||
_init : function(realWindow) {
|
_init : function(realWindow) {
|
||||||
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
|
|
||||||
reactive: true,
|
|
||||||
x: realWindow.x,
|
|
||||||
y: realWindow.y });
|
|
||||||
this.actor._delegate = this;
|
|
||||||
this.realWindow = realWindow;
|
this.realWindow = realWindow;
|
||||||
this.metaWindow = realWindow.meta_window;
|
this.metaWindow = realWindow.meta_window;
|
||||||
this.metaWindow._delegate = this;
|
this.metaWindow._delegate = this;
|
||||||
this.origX = realWindow.x;
|
|
||||||
this.origY = realWindow.y;
|
let [borderX, borderY] = this._getInvisibleBorderPadding();
|
||||||
|
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
|
||||||
|
x: -borderX,
|
||||||
|
y: -borderY });
|
||||||
|
|
||||||
|
this.origX = realWindow.x + borderX;
|
||||||
|
this.origY = realWindow.y + borderY;
|
||||||
|
|
||||||
|
let outerRect = realWindow.meta_window.get_outer_rect();
|
||||||
|
|
||||||
|
// The MetaShapedTexture that we clone has a size that includes
|
||||||
|
// the invisible border; this is inconvenient; rather than trying
|
||||||
|
// to compensate all over the place we insert a ClutterGroup into
|
||||||
|
// the hierarchy that is sized to only the visible portion.
|
||||||
|
this.actor = new Clutter.Group({ reactive: true,
|
||||||
|
x: this.origX,
|
||||||
|
y: this.origY,
|
||||||
|
width: outerRect.width,
|
||||||
|
height: outerRect.height });
|
||||||
|
|
||||||
|
this.actor.add_actor(this._windowClone);
|
||||||
|
|
||||||
|
this.actor._delegate = this;
|
||||||
|
|
||||||
this._stackAbove = null;
|
this._stackAbove = null;
|
||||||
|
|
||||||
this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
|
this._sizeChangedId = this.realWindow.connect('size-changed',
|
||||||
this.emit('size-changed');
|
Lang.bind(this, this._onRealWindowSizeChanged));
|
||||||
}));
|
|
||||||
this._realWindowDestroyId = this.realWindow.connect('destroy',
|
this._realWindowDestroyId = this.realWindow.connect('destroy',
|
||||||
Lang.bind(this, this._disconnectRealWindowSignals));
|
Lang.bind(this, this._disconnectRealWindowSignals));
|
||||||
|
|
||||||
@ -158,7 +177,7 @@ WindowClone.prototype = {
|
|||||||
// will look funny.
|
// will look funny.
|
||||||
|
|
||||||
if (!this._selected &&
|
if (!this._selected &&
|
||||||
this.metaWindow != global.screen.get_display().focus_window)
|
this.metaWindow != global.display.focus_window)
|
||||||
this._zoomEnd();
|
this._zoomEnd();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -173,6 +192,32 @@ WindowClone.prototype = {
|
|||||||
this._realWindowDestroyId = 0;
|
this._realWindowDestroyId = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getInvisibleBorderPadding: function() {
|
||||||
|
// We need to adjust the position of the actor because of the
|
||||||
|
// consequences of invisible borders -- in reality, the texture
|
||||||
|
// has an extra set of "padding" around it that we need to trim
|
||||||
|
// down.
|
||||||
|
|
||||||
|
// The outer rect paradoxically is the smaller rectangle,
|
||||||
|
// containing the positions of the visible frame. The input
|
||||||
|
// rect contains everything, including the invisible border
|
||||||
|
// padding.
|
||||||
|
let outerRect = this.metaWindow.get_outer_rect();
|
||||||
|
let inputRect = this.metaWindow.get_input_rect();
|
||||||
|
let [borderX, borderY] = [outerRect.x - inputRect.x,
|
||||||
|
outerRect.y - inputRect.y];
|
||||||
|
|
||||||
|
return [borderX, borderY];
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRealWindowSizeChanged: function() {
|
||||||
|
let [borderX, borderY] = this._getInvisibleBorderPadding();
|
||||||
|
let outerRect = this.metaWindow.get_outer_rect();
|
||||||
|
this.actor.set_size(outerRect.width, outerRect.height);
|
||||||
|
this._windowClone.set_position(-borderX, -borderY);
|
||||||
|
this.emit('size-changed');
|
||||||
|
},
|
||||||
|
|
||||||
_onDestroy: function() {
|
_onDestroy: function() {
|
||||||
this._disconnectRealWindowSignals();
|
this._disconnectRealWindowSignals();
|
||||||
|
|
||||||
@ -222,8 +267,12 @@ WindowClone.prototype = {
|
|||||||
let [width, height] = this.actor.get_transformed_size();
|
let [width, height] = this.actor.get_transformed_size();
|
||||||
|
|
||||||
let monitorIndex = this.metaWindow.get_monitor();
|
let monitorIndex = this.metaWindow.get_monitor();
|
||||||
let availArea = global.get_monitors()[monitorIndex];
|
let monitor = Main.layoutManager.monitors[monitorIndex];
|
||||||
if (monitorIndex == global.get_primary_monitor_index()) {
|
let availArea = new Meta.Rectangle({ x: monitor.x,
|
||||||
|
y: monitor.y,
|
||||||
|
width: monitor.width,
|
||||||
|
height: monitor.height });
|
||||||
|
if (monitorIndex == Main.layoutManager.primaryIndex) {
|
||||||
availArea.y += Main.panel.actor.height;
|
availArea.y += Main.panel.actor.height;
|
||||||
availArea.height -= Main.panel.actor.height;
|
availArea.height -= Main.panel.actor.height;
|
||||||
}
|
}
|
||||||
@ -237,7 +286,7 @@ WindowClone.prototype = {
|
|||||||
this.emit('zoom-start');
|
this.emit('zoom-start');
|
||||||
|
|
||||||
if (!this._zoomLightbox)
|
if (!this._zoomLightbox)
|
||||||
this._zoomLightbox = new Lightbox.Lightbox(global.stage,
|
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||||
{ fadeTime: LIGHTBOX_FADE_TIME });
|
{ fadeTime: LIGHTBOX_FADE_TIME });
|
||||||
this._zoomLightbox.show();
|
this._zoomLightbox.show();
|
||||||
|
|
||||||
@ -248,7 +297,7 @@ WindowClone.prototype = {
|
|||||||
this._zoomGlobalOrig.setPosition.apply(this._zoomGlobalOrig, this.actor.get_transformed_position());
|
this._zoomGlobalOrig.setPosition.apply(this._zoomGlobalOrig, this.actor.get_transformed_position());
|
||||||
this._zoomGlobalOrig.setScale(width / this.actor.width, height / this.actor.height);
|
this._zoomGlobalOrig.setScale(width / this.actor.width, height / this.actor.height);
|
||||||
|
|
||||||
this.actor.reparent(global.stage);
|
this.actor.reparent(Main.uiGroup);
|
||||||
this._zoomLightbox.highlight(this.actor);
|
this._zoomLightbox.highlight(this.actor);
|
||||||
|
|
||||||
[this.actor.x, this.actor.y] = this._zoomGlobalOrig.getPosition();
|
[this.actor.x, this.actor.y] = this._zoomGlobalOrig.getPosition();
|
||||||
@ -296,12 +345,30 @@ WindowClone.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onDragBegin : function (draggable, time) {
|
_onDragBegin : function (draggable, time) {
|
||||||
|
if (this._zooming)
|
||||||
|
this._zoomEnd();
|
||||||
|
|
||||||
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
|
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
|
||||||
this.dragOrigScale = this.actor.scale_x;
|
this.dragOrigScale = this.actor.scale_x;
|
||||||
this.inDrag = true;
|
this.inDrag = true;
|
||||||
this.emit('drag-begin');
|
this.emit('drag-begin');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getWorkspaceActor : function() {
|
||||||
|
let index = this.metaWindow.get_workspace().index();
|
||||||
|
return Main.overview.workspaces.getWorkspaceByIndex(index);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDragOver : function(source, actor, x, y, time) {
|
||||||
|
let workspace = this._getWorkspaceActor();
|
||||||
|
return workspace.handleDragOver(source, actor, x, y, time);
|
||||||
|
},
|
||||||
|
|
||||||
|
acceptDrop : function(source, actor, x, y, time) {
|
||||||
|
let workspace = this._getWorkspaceActor();
|
||||||
|
workspace.acceptDrop(source, actor, x, y, time);
|
||||||
|
},
|
||||||
|
|
||||||
_onDragCancelled : function (draggable, time) {
|
_onDragCancelled : function (draggable, time) {
|
||||||
this.emit('drag-cancelled');
|
this.emit('drag-cancelled');
|
||||||
},
|
},
|
||||||
@ -394,6 +461,8 @@ WindowOverlay.prototype = {
|
|||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
this._hidden = false;
|
this._hidden = false;
|
||||||
|
if (this._windowClone.actor.has_pointer)
|
||||||
|
this.closeButton.show();
|
||||||
this.title.show();
|
this.title.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -430,9 +499,20 @@ WindowOverlay.prototype = {
|
|||||||
let button = this.closeButton;
|
let button = this.closeButton;
|
||||||
let title = this.title;
|
let title = this.title;
|
||||||
|
|
||||||
|
let gconf = GConf.Client.get_default();
|
||||||
|
let layout = gconf.get_string(BUTTON_LAYOUT_KEY);
|
||||||
|
let rtl = St.Widget.get_default_direction() == St.TextDirection.RTL;
|
||||||
|
|
||||||
|
let split = layout.split(":");
|
||||||
|
let side;
|
||||||
|
if (split[0].indexOf("close") > -1)
|
||||||
|
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
|
||||||
|
else
|
||||||
|
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
|
||||||
|
|
||||||
let buttonX;
|
let buttonX;
|
||||||
let buttonY = cloneY - (button.height - button._overlap);
|
let buttonY = cloneY - (button.height - button._overlap);
|
||||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
if (side == St.Side.LEFT)
|
||||||
buttonX = cloneX - (button.width - button._overlap);
|
buttonX = cloneX - (button.width - button._overlap);
|
||||||
else
|
else
|
||||||
buttonX = cloneX + (cloneWidth - button._overlap);
|
buttonX = cloneX + (cloneWidth - button._overlap);
|
||||||
@ -559,7 +639,7 @@ Workspace.prototype = {
|
|||||||
this._height = 0;
|
this._height = 0;
|
||||||
|
|
||||||
this.monitorIndex = monitorIndex;
|
this.monitorIndex = monitorIndex;
|
||||||
this._monitor = global.get_monitors()[this.monitorIndex];
|
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
|
||||||
this._windowOverlaysGroup = new Clutter.Group();
|
this._windowOverlaysGroup = new Clutter.Group();
|
||||||
// Without this the drop area will be overlapped.
|
// Without this the drop area will be overlapped.
|
||||||
this._windowOverlaysGroup.set_size(0, 0);
|
this._windowOverlaysGroup.set_size(0, 0);
|
||||||
@ -614,6 +694,7 @@ Workspace.prototype = {
|
|||||||
function () {
|
function () {
|
||||||
this._dropRect.set_position(x, y);
|
this._dropRect.set_position(x, y);
|
||||||
this._dropRect.set_size(width, height);
|
this._dropRect.set_size(width, height);
|
||||||
|
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -636,16 +717,6 @@ Workspace.prototype = {
|
|||||||
return this._windows.length == 0;
|
return this._windows.length == 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* setReactive:
|
|
||||||
* @reactive: %true iff the workspace should be reactive
|
|
||||||
*
|
|
||||||
* Set the workspace (desktop) reactive
|
|
||||||
**/
|
|
||||||
setReactive: function(reactive) {
|
|
||||||
this.actor.reactive = reactive;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Only use this for n <= 20 say
|
// Only use this for n <= 20 say
|
||||||
_factorial: function(n) {
|
_factorial: function(n) {
|
||||||
let result = 1;
|
let result = 1;
|
||||||
@ -1113,7 +1184,9 @@ Workspace.prototype = {
|
|||||||
// the compositor finds out about them...
|
// the compositor finds out about them...
|
||||||
Mainloop.idle_add(Lang.bind(this,
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
function () {
|
function () {
|
||||||
if (this.actor && metaWin.get_compositor_private())
|
if (this.actor &&
|
||||||
|
metaWin.get_compositor_private() &&
|
||||||
|
metaWin.get_workspace() == this.metaWorkspace)
|
||||||
this._doAddWindow(metaWin);
|
this._doAddWindow(metaWin);
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
@ -1409,7 +1482,7 @@ Workspace.prototype = {
|
|||||||
time);
|
time);
|
||||||
return true;
|
return true;
|
||||||
} else if (source.shellWorkspaceLaunch) {
|
} else if (source.shellWorkspaceLaunch) {
|
||||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
|
||||||
timestamp: time });
|
timestamp: time });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ WorkspaceSwitcherPopup.prototype = {
|
|||||||
|
|
||||||
_getPreferredHeight : function (actor, forWidth, alloc) {
|
_getPreferredHeight : function (actor, forWidth, alloc) {
|
||||||
let children = this._list.get_children();
|
let children = this._list.get_children();
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
let availHeight = primary.height;
|
let availHeight = primary.height;
|
||||||
availHeight -= Main.panel.actor.height;
|
availHeight -= Main.panel.actor.height;
|
||||||
@ -82,7 +82,7 @@ WorkspaceSwitcherPopup.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth : function (actor, forHeight, alloc) {
|
_getPreferredWidth : function (actor, forHeight, alloc) {
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
|
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
|
||||||
|
|
||||||
alloc.min_size = this._childWidth;
|
alloc.min_size = this._childWidth;
|
||||||
@ -125,7 +125,7 @@ WorkspaceSwitcherPopup.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_position: function() {
|
_position: function() {
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
|
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
|
||||||
this._container.y = primary.y + Main.panel.actor.height +
|
this._container.y = primary.y + Main.panel.actor.height +
|
||||||
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
|
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
|
||||||
|
@ -146,7 +146,7 @@ function WorkspaceThumbnail(metaWorkspace) {
|
|||||||
WorkspaceThumbnail.prototype = {
|
WorkspaceThumbnail.prototype = {
|
||||||
_init : function(metaWorkspace) {
|
_init : function(metaWorkspace) {
|
||||||
this.metaWorkspace = metaWorkspace;
|
this.metaWorkspace = metaWorkspace;
|
||||||
this.monitorIndex = global.get_primary_monitor_index();
|
this.monitorIndex = Main.layoutManager.primaryIndex;
|
||||||
|
|
||||||
this.actor = new St.Group({ reactive: true,
|
this.actor = new St.Group({ reactive: true,
|
||||||
clip_to_allocation: true,
|
clip_to_allocation: true,
|
||||||
@ -167,10 +167,10 @@ WorkspaceThumbnail.prototype = {
|
|||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||||
this._contents.add_actor(this._background);
|
this._contents.add_actor(this._background);
|
||||||
|
|
||||||
let monitor = global.get_primary_monitor();
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
|
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||||
|
|
||||||
let windows = global.get_window_actors().filter(this._isMyWindow, this);
|
let windows = global.get_window_actors().filter(this._isMyWindow, this);
|
||||||
@ -178,6 +178,11 @@ WorkspaceThumbnail.prototype = {
|
|||||||
// Create clones for windows that should be visible in the Overview
|
// Create clones for windows that should be visible in the Overview
|
||||||
this._windows = [];
|
this._windows = [];
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
|
windows[i].meta_window._minimizedChangedId =
|
||||||
|
windows[i].meta_window.connect('notify::minimized',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._updateMinimized));
|
||||||
|
|
||||||
if (this._isOverviewWindow(windows[i])) {
|
if (this._isOverviewWindow(windows[i])) {
|
||||||
this._addWindowClone(windows[i]);
|
this._addWindowClone(windows[i]);
|
||||||
}
|
}
|
||||||
@ -257,11 +262,18 @@ WorkspaceThumbnail.prototype = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if window still should be here
|
// Check if window still should be here
|
||||||
if (win && this._isMyWindow(win))
|
if (win && this._isMyWindow(win) && this._isOverviewWindow(win))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let clone = this._windows[index];
|
let clone = this._windows[index];
|
||||||
this._windows.splice(index, 1);
|
this._windows.splice(index, 1);
|
||||||
|
|
||||||
|
if (win && this._isOverviewWindow(win)) {
|
||||||
|
if (metaWin._minimizedChangedId) {
|
||||||
|
metaWin.disconnect(metaWin._minimizedChangedId);
|
||||||
|
delete metaWin._minimizedChangedId;
|
||||||
|
}
|
||||||
|
}
|
||||||
clone.destroy();
|
clone.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -276,7 +288,9 @@ WorkspaceThumbnail.prototype = {
|
|||||||
// the compositor finds out about them...
|
// the compositor finds out about them...
|
||||||
Mainloop.idle_add(Lang.bind(this,
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
function () {
|
function () {
|
||||||
if (this.actor && metaWin.get_compositor_private())
|
if (this.actor &&
|
||||||
|
metaWin.get_compositor_private() &&
|
||||||
|
metaWin.get_workspace() == this.metaWorkspace)
|
||||||
this._doAddWindow(metaWin);
|
this._doAddWindow(metaWin);
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
@ -288,6 +302,11 @@ WorkspaceThumbnail.prototype = {
|
|||||||
if (this._lookupIndex (metaWin) != -1)
|
if (this._lookupIndex (metaWin) != -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!metaWin._minimizedChangedId)
|
||||||
|
metaWin._minimizedChangedId = metaWin.connect('notify::minimized',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._updateMinimized));
|
||||||
|
|
||||||
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -314,6 +333,13 @@ WorkspaceThumbnail.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateMinimized: function(metaWin) {
|
||||||
|
if (metaWin.minimized)
|
||||||
|
this._doRemoveWindow(metaWin);
|
||||||
|
else
|
||||||
|
this._doAddWindow(metaWin);
|
||||||
|
},
|
||||||
|
|
||||||
destroy : function() {
|
destroy : function() {
|
||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
},
|
},
|
||||||
@ -324,6 +350,14 @@ WorkspaceThumbnail.prototype = {
|
|||||||
global.screen.disconnect(this._windowEnteredMonitorId);
|
global.screen.disconnect(this._windowEnteredMonitorId);
|
||||||
global.screen.disconnect(this._windowLeftMonitorId);
|
global.screen.disconnect(this._windowLeftMonitorId);
|
||||||
|
|
||||||
|
for (let i = 0; i < this._windows.length; i++) {
|
||||||
|
let metaWin = this._windows[i].metaWindow;
|
||||||
|
if (metaWin._minimizedChangedId) {
|
||||||
|
metaWin.disconnect(metaWin._minimizedChangedId);
|
||||||
|
delete metaWin._minimizedChangedId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._windows = [];
|
this._windows = [];
|
||||||
this.actor = null;
|
this.actor = null;
|
||||||
},
|
},
|
||||||
@ -337,7 +371,8 @@ WorkspaceThumbnail.prototype = {
|
|||||||
// Tests if @win should be shown in the Overview
|
// Tests if @win should be shown in the Overview
|
||||||
_isOverviewWindow : function (win) {
|
_isOverviewWindow : function (win) {
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
return tracker.is_window_interesting(win.get_meta_window());
|
return tracker.is_window_interesting(win.get_meta_window()) &&
|
||||||
|
win.get_meta_window().showing_on_its_workspace();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Create a clone of a (non-desktop) window and add it to the window list
|
// Create a clone of a (non-desktop) window and add it to the window list
|
||||||
@ -417,7 +452,7 @@ WorkspaceThumbnail.prototype = {
|
|||||||
time);
|
time);
|
||||||
return true;
|
return true;
|
||||||
} else if (source.shellWorkspaceLaunch) {
|
} else if (source.shellWorkspaceLaunch) {
|
||||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
|
||||||
timestamp: time });
|
timestamp: time });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -493,7 +528,7 @@ ThumbnailsBox.prototype = {
|
|||||||
|
|
||||||
// The "porthole" is the portion of the screen that we show in the workspaces
|
// The "porthole" is the portion of the screen that we show in the workspaces
|
||||||
let panelHeight = Main.panel.actor.height;
|
let panelHeight = Main.panel.actor.height;
|
||||||
let monitor = global.get_primary_monitor();
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
this._porthole = {
|
this._porthole = {
|
||||||
x: monitor.x,
|
x: monitor.x,
|
||||||
y: monitor.y + panelHeight,
|
y: monitor.y + panelHeight,
|
||||||
|
@ -7,8 +7,6 @@ const Meta = imports.gi.Meta;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
|
||||||
const _ = Gettext.gettext;
|
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@ -51,9 +49,12 @@ WorkspacesView.prototype = {
|
|||||||
this._height = 0;
|
this._height = 0;
|
||||||
this._x = 0;
|
this._x = 0;
|
||||||
this._y = 0;
|
this._y = 0;
|
||||||
|
this._clipX = 0;
|
||||||
|
this._clipY = 0;
|
||||||
|
this._clipWidth = 0;
|
||||||
|
this._clipHeight = 0;
|
||||||
this._workspaceRatioSpacing = 0;
|
this._workspaceRatioSpacing = 0;
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
this._lostWorkspaces = [];
|
|
||||||
this._animating = false; // tweening
|
this._animating = false; // tweening
|
||||||
this._scrolling = false; // swipe-scrolling
|
this._scrolling = false; // swipe-scrolling
|
||||||
this._animatingScroll = false; // programatically updating the adjustment
|
this._animatingScroll = false; // programatically updating the adjustment
|
||||||
@ -69,10 +70,10 @@ WorkspacesView.prototype = {
|
|||||||
this._workspaces[activeWorkspaceIndex].actor.raise_top();
|
this._workspaces[activeWorkspaceIndex].actor.raise_top();
|
||||||
|
|
||||||
this._extraWorkspaces = [];
|
this._extraWorkspaces = [];
|
||||||
let monitors = global.get_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 == global.get_primary_monitor_index())
|
if (i == Main.layoutManager.primaryIndex)
|
||||||
continue;
|
continue;
|
||||||
let ws = new Workspace.Workspace(null, i);
|
let ws = new Workspace.Workspace(null, i);
|
||||||
this._extraWorkspaces[m++] = ws;
|
this._extraWorkspaces[m++] = ws;
|
||||||
@ -95,7 +96,8 @@ WorkspacesView.prototype = {
|
|||||||
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._width, this._height);
|
this.actor.set_clip(this._clipX, this._clipY,
|
||||||
|
this._clipWidth, this._clipHeight);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
||||||
@ -139,6 +141,13 @@ WorkspacesView.prototype = {
|
|||||||
this._workspaces[i].setGeometry(x, y, width, height);
|
this._workspaces[i].setGeometry(x, y, width, height);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setClipRect: function(x, y, width, height) {
|
||||||
|
this._clipX = x;
|
||||||
|
this._clipY = y;
|
||||||
|
this._clipWidth = width;
|
||||||
|
this._clipHeight = height;
|
||||||
|
},
|
||||||
|
|
||||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||||
for (let i = 0; i < this._workspaces.length; i++) {
|
for (let i = 0; i < this._workspaces.length; i++) {
|
||||||
if (this._workspaces[i].containsMetaWindow(metaWindow))
|
if (this._workspaces[i].containsMetaWindow(metaWindow))
|
||||||
@ -152,6 +161,10 @@ WorkspacesView.prototype = {
|
|||||||
return this._workspaces[active];
|
return this._workspaces[active];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getWorkspaceByIndex: function(index) {
|
||||||
|
return this._workspaces[index];
|
||||||
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
||||||
@ -201,12 +214,10 @@ WorkspacesView.prototype = {
|
|||||||
|
|
||||||
Tweener.removeTweens(workspace.actor);
|
Tweener.removeTweens(workspace.actor);
|
||||||
|
|
||||||
let opacity = (this._inDrag && w != active) ? 200 : 255;
|
|
||||||
let y = (w - active) * (this._height + this._spacing + this._workspaceRatioSpacing);
|
let y = (w - active) * (this._height + this._spacing + this._workspaceRatioSpacing);
|
||||||
|
|
||||||
if (showAnimation) {
|
if (showAnimation) {
|
||||||
let params = { y: y,
|
let params = { y: y,
|
||||||
opacity: opacity,
|
|
||||||
time: WORKSPACE_SWITCH_TIME,
|
time: WORKSPACE_SWITCH_TIME,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
};
|
};
|
||||||
@ -224,32 +235,10 @@ WorkspacesView.prototype = {
|
|||||||
Tweener.addTween(workspace.actor, params);
|
Tweener.addTween(workspace.actor, params);
|
||||||
} else {
|
} else {
|
||||||
workspace.actor.set_position(0, y);
|
workspace.actor.set_position(0, y);
|
||||||
workspace.actor.opacity = opacity;
|
|
||||||
if (w == 0)
|
if (w == 0)
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let l = 0; l < this._lostWorkspaces.length; l++) {
|
|
||||||
let workspace = this._lostWorkspaces[l];
|
|
||||||
|
|
||||||
Tweener.removeTweens(workspace.actor);
|
|
||||||
|
|
||||||
workspace.actor.show();
|
|
||||||
workspace.hideWindowsOverlays();
|
|
||||||
|
|
||||||
if (showAnimation) {
|
|
||||||
Tweener.addTween(workspace.actor,
|
|
||||||
{ y: workspace.x,
|
|
||||||
time: WORKSPACE_SWITCH_TIME,
|
|
||||||
transition: 'easeOutQuad',
|
|
||||||
onComplete: Lang.bind(this,
|
|
||||||
this._cleanWorkspaces)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._cleanWorkspaces();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
@ -270,17 +259,6 @@ WorkspacesView.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_cleanWorkspaces: function() {
|
|
||||||
if (this._lostWorkspaces.length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (let l = 0; l < this._lostWorkspaces.length; l++)
|
|
||||||
this._lostWorkspaces[l].destroy();
|
|
||||||
this._lostWorkspaces = [];
|
|
||||||
|
|
||||||
this._updateWorkspaceActors(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateScrollAdjustment: function(index, showAnimation) {
|
_updateScrollAdjustment: function(index, showAnimation) {
|
||||||
if (this._scrolling)
|
if (this._scrolling)
|
||||||
return;
|
return;
|
||||||
@ -303,12 +281,9 @@ WorkspacesView.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces) {
|
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
|
||||||
let active = global.screen.get_active_workspace_index();
|
let active = global.screen.get_active_workspace_index();
|
||||||
|
|
||||||
for (let l = 0; l < lostWorkspaces.length; l++)
|
|
||||||
lostWorkspaces[l].disconnectAll();
|
|
||||||
|
|
||||||
Tweener.addTween(this._scrollAdjustment,
|
Tweener.addTween(this._scrollAdjustment,
|
||||||
{ upper: newNumWorkspaces,
|
{ upper: newNumWorkspaces,
|
||||||
time: WORKSPACE_SWITCH_TIME,
|
time: WORKSPACE_SWITCH_TIME,
|
||||||
@ -316,12 +291,13 @@ WorkspacesView.prototype = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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._width, this._height);
|
||||||
this.actor.add_actor(this._workspaces[w].actor);
|
this.actor.add_actor(this._workspaces[w].actor);
|
||||||
|
}
|
||||||
|
|
||||||
this._updateWorkspaceActors(false);
|
this._updateWorkspaceActors(false);
|
||||||
} else {
|
|
||||||
this._lostWorkspaces = lostWorkspaces;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._scrollToActive(true);
|
this._scrollToActive(true);
|
||||||
@ -407,7 +383,7 @@ WorkspacesView.prototype = {
|
|||||||
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
|
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
let topWorkspace, bottomWorkspace;
|
let topWorkspace, bottomWorkspace;
|
||||||
@ -439,20 +415,14 @@ WorkspacesView.prototype = {
|
|||||||
if (topWorkspace) {
|
if (topWorkspace) {
|
||||||
if (topWorkspace.actor.contains(dragEvent.targetActor)) {
|
if (topWorkspace.actor.contains(dragEvent.targetActor)) {
|
||||||
hoverWorkspace = topWorkspace;
|
hoverWorkspace = topWorkspace;
|
||||||
topWorkspace.opacity = topWorkspace.actor.opacity = 255;
|
|
||||||
result = topWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
|
result = topWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
|
||||||
} else {
|
|
||||||
topWorkspace.opacity = topWorkspace.actor.opacity = 200;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bottomWorkspace) {
|
if (bottomWorkspace) {
|
||||||
if (bottomWorkspace.actor.contains(dragEvent.targetActor)) {
|
if (bottomWorkspace.actor.contains(dragEvent.targetActor)) {
|
||||||
hoverWorkspace = bottomWorkspace;
|
hoverWorkspace = bottomWorkspace;
|
||||||
bottomWorkspace.opacity = bottomWorkspace.actor.opacity = 255;
|
|
||||||
result = bottomWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
|
result = bottomWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
|
||||||
} else {
|
|
||||||
bottomWorkspace.opacity = bottomWorkspace.actor.opacity = 200;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,7 +449,7 @@ WorkspacesView.prototype = {
|
|||||||
Mainloop.source_remove(this._timeoutId);
|
Mainloop.source_remove(this._timeoutId);
|
||||||
this._timeoutId = 0;
|
this._timeoutId = 0;
|
||||||
}
|
}
|
||||||
DND.removeMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
this._inDrag = false;
|
this._inDrag = false;
|
||||||
|
|
||||||
for (let i = 0; i < this._workspaces.length; i++)
|
for (let i = 0; i < this._workspaces.length; i++)
|
||||||
@ -580,8 +550,7 @@ WorkspacesDisplay.prototype = {
|
|||||||
controls.connect('scroll-event',
|
controls.connect('scroll-event',
|
||||||
Lang.bind(this, this._onScrollEvent));
|
Lang.bind(this, this._onScrollEvent));
|
||||||
|
|
||||||
this._monitorIndex = global.get_primary_monitor_index();
|
this._monitorIndex = Main.layoutManager.primaryIndex;
|
||||||
this._monitor = global.get_monitors()[this._monitorIndex];
|
|
||||||
|
|
||||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||||
controls.add_actor(this._thumbnailsBox.actor);
|
controls.add_actor(this._thumbnailsBox.actor);
|
||||||
@ -597,7 +566,10 @@ WorkspacesDisplay.prototype = {
|
|||||||
|
|
||||||
this._updateAlwaysZoom();
|
this._updateAlwaysZoom();
|
||||||
|
|
||||||
global.screen.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
|
||||||
|
global.screen.connect('notify::n-workspaces',
|
||||||
|
Lang.bind(this, this._workspacesChanged));
|
||||||
|
|
||||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
|
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
|
||||||
this._alwaysZoomOut = true;
|
this._alwaysZoomOut = true;
|
||||||
}));
|
}));
|
||||||
@ -607,7 +579,6 @@ WorkspacesDisplay.prototype = {
|
|||||||
this._updateAlwaysZoom();
|
this._updateAlwaysZoom();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._nWorkspacesNotifyId = 0;
|
|
||||||
this._switchWorkspaceNotifyId = 0;
|
this._switchWorkspaceNotifyId = 0;
|
||||||
|
|
||||||
this._itemDragBeginId = 0;
|
this._itemDragBeginId = 0;
|
||||||
@ -637,10 +608,6 @@ WorkspacesDisplay.prototype = {
|
|||||||
this.workspacesView = new WorkspacesView(this._workspaces);
|
this.workspacesView = new WorkspacesView(this._workspaces);
|
||||||
this._updateWorkspacesGeometry();
|
this._updateWorkspacesGeometry();
|
||||||
|
|
||||||
this._nWorkspacesNotifyId =
|
|
||||||
global.screen.connect('notify::n-workspaces',
|
|
||||||
Lang.bind(this, this._workspacesChanged));
|
|
||||||
|
|
||||||
this._restackedNotifyId =
|
this._restackedNotifyId =
|
||||||
global.screen.connect('restacked',
|
global.screen.connect('restacked',
|
||||||
Lang.bind(this, this._onRestacked));
|
Lang.bind(this, this._onRestacked));
|
||||||
@ -671,10 +638,6 @@ WorkspacesDisplay.prototype = {
|
|||||||
this._controls.hide();
|
this._controls.hide();
|
||||||
this._thumbnailsBox.hide();
|
this._thumbnailsBox.hide();
|
||||||
|
|
||||||
if (this._nWorkspacesNotifyId > 0) {
|
|
||||||
global.screen.disconnect(this._nWorkspacesNotifyId);
|
|
||||||
this._nWorkspacesNotifyId = 0;
|
|
||||||
}
|
|
||||||
if (this._restackedNotifyId > 0){
|
if (this._restackedNotifyId > 0){
|
||||||
global.screen.disconnect(this._restackedNotifyId);
|
global.screen.disconnect(this._restackedNotifyId);
|
||||||
this._restackedNotifyId = 0;
|
this._restackedNotifyId = 0;
|
||||||
@ -723,10 +686,15 @@ WorkspacesDisplay.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateAlwaysZoom: function() {
|
_updateAlwaysZoom: function() {
|
||||||
this._alwaysZoomOut = false;
|
// Always show the pager if workspaces are actually used,
|
||||||
|
// e.g. there are windows on more than one
|
||||||
|
this._alwaysZoomOut = global.screen.n_workspaces > 2;
|
||||||
|
|
||||||
let monitors = global.get_monitors();
|
if (this._alwaysZoomOut)
|
||||||
let primary = global.get_primary_monitor();
|
return;
|
||||||
|
|
||||||
|
let monitors = Main.layoutManager.monitors;
|
||||||
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
/* Look for any monitor to the right of the primary, if there is
|
/* Look for any monitor to the right of the primary, if there is
|
||||||
* one, we always keep zoom out, otherwise its hard to reach
|
* one, we always keep zoom out, otherwise its hard to reach
|
||||||
@ -794,6 +762,13 @@ WorkspacesDisplay.prototype = {
|
|||||||
|
|
||||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||||
|
|
||||||
|
let clipWidth = width - controlsVisible;
|
||||||
|
let clipHeight = (fullHeight / fullWidth) * clipWidth;
|
||||||
|
let clipX = rtl ? x + controlsVisible : x;
|
||||||
|
let clipY = y + (fullHeight - clipHeight) / 2;
|
||||||
|
|
||||||
|
this.workspacesView.setClipRect(clipX, clipY, clipWidth, clipHeight);
|
||||||
|
|
||||||
if (this._zoomOut) {
|
if (this._zoomOut) {
|
||||||
width -= controlsNatural;
|
width -= controlsNatural;
|
||||||
if (rtl)
|
if (rtl)
|
||||||
@ -832,6 +807,12 @@ WorkspacesDisplay.prototype = {
|
|||||||
if (oldNumWorkspaces == newNumWorkspaces)
|
if (oldNumWorkspaces == newNumWorkspaces)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this._updateAlwaysZoom();
|
||||||
|
this._updateZoom();
|
||||||
|
|
||||||
|
if (this.workspacesView == null)
|
||||||
|
return;
|
||||||
|
|
||||||
let lostWorkspaces = [];
|
let lostWorkspaces = [];
|
||||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||||
// Assume workspaces are only added at the end
|
// Assume workspaces are only added at the end
|
||||||
@ -857,24 +838,23 @@ WorkspacesDisplay.prototype = {
|
|||||||
lostWorkspaces = this._workspaces.splice(removedIndex,
|
lostWorkspaces = this._workspaces.splice(removedIndex,
|
||||||
removedNum);
|
removedNum);
|
||||||
|
|
||||||
// Don't let the user try to select this workspace as it's
|
for (let l = 0; l < lostWorkspaces.length; l++) {
|
||||||
// making its exit.
|
lostWorkspaces[l].disconnectAll();
|
||||||
for (let l = 0; l < lostWorkspaces.length; l++)
|
lostWorkspaces[l].destroy();
|
||||||
lostWorkspaces[l].setReactive(false);
|
}
|
||||||
|
|
||||||
this._thumbnailsBox.removeThumbmails(removedIndex, removedNum);
|
this._thumbnailsBox.removeThumbmails(removedIndex, removedNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.workspacesView.updateWorkspaces(oldNumWorkspaces,
|
this.workspacesView.updateWorkspaces(oldNumWorkspaces,
|
||||||
newNumWorkspaces,
|
newNumWorkspaces);
|
||||||
lostWorkspaces);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateZoom : function() {
|
_updateZoom : function() {
|
||||||
if (Main.overview.animationInProgress)
|
if (Main.overview.animationInProgress)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let shouldZoom = this._alwaysZoomOut || this._controls.hover || (this._inDrag && !this._cancelledDrag);
|
let shouldZoom = this._alwaysZoomOut || this._controls.hover;
|
||||||
if (shouldZoom != this._zoomOut) {
|
if (shouldZoom != this._zoomOut) {
|
||||||
this._zoomOut = shouldZoom;
|
this._zoomOut = shouldZoom;
|
||||||
this._updateWorkspacesGeometry();
|
this._updateWorkspacesGeometry();
|
||||||
@ -898,12 +878,22 @@ WorkspacesDisplay.prototype = {
|
|||||||
_dragBegin: function() {
|
_dragBegin: function() {
|
||||||
this._inDrag = true;
|
this._inDrag = true;
|
||||||
this._cancelledDrag = false;
|
this._cancelledDrag = false;
|
||||||
this._updateZoom();
|
this._dragMonitor = {
|
||||||
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||||
|
};
|
||||||
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_dragCancelled: function() {
|
_dragCancelled: function() {
|
||||||
this._cancelledDrag = true;
|
this._cancelledDrag = true;
|
||||||
this._updateZoom();
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragMotion: function(dragEvent) {
|
||||||
|
let controlsHovered = this._controls.contains(dragEvent.targetActor);
|
||||||
|
this._controls.set_hover(controlsHovered);
|
||||||
|
|
||||||
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_dragEnd: function() {
|
_dragEnd: function() {
|
||||||
|
@ -36,53 +36,48 @@ visually attractive and easy to use experience.
|
|||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-r, \-\-replace
|
.B \-\-replace
|
||||||
Replace the running metacity/gnome-panel
|
Replace the running window manager
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-v, \-\-verbose
|
.B \-\-sm-disable
|
||||||
Shows details about the results of running `gnome-shell'.
|
Disable connection to the session manager
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-g, \-\-debug
|
.B \-\-sm-client-id=ID
|
||||||
Run under a debugger
|
Specify session management ID
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-debug\-command
|
.B \-\-sm-save-file=FILE
|
||||||
Command to use for debugging (defaults to 'gdb \-\-args')
|
Initialize session from savefile
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-\-screen=SCREEN
|
||||||
|
X screen to use
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-d, \-\-display=DISPLAY
|
||||||
|
X display to use
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-sync
|
.B \-\-sync
|
||||||
.br
|
Make X calls synchronous
|
||||||
Make X calls synchronously, useful when debugging down X errors
|
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-xephyr
|
.B \-\-version
|
||||||
Run a debugging instance inside Xephyr
|
Print version and exit
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-geometry
|
.B \-\-help
|
||||||
Specify Xephyr screen geometry
|
Display help and exit
|
||||||
.br
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.B \-w, \-\-wide
|
|
||||||
Use widescreen (1280x800) with Xephyr
|
|
||||||
.br
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.B \-\-create\-extension
|
|
||||||
Create a new GNOME Shell extension
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.B \-\-eval\-file
|
|
||||||
Evaluate the contents of the given JavaScript file
|
|
||||||
.br
|
.br
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
af
|
af
|
||||||
|
an
|
||||||
ar
|
ar
|
||||||
|
be
|
||||||
bg
|
bg
|
||||||
bn
|
bn
|
||||||
bn_IN
|
bn_IN
|
||||||
ca
|
ca
|
||||||
|
ca@valencia
|
||||||
cs
|
cs
|
||||||
da
|
da
|
||||||
de
|
de
|
||||||
el
|
el
|
||||||
en_GB
|
en_GB
|
||||||
|
eo
|
||||||
es
|
es
|
||||||
et
|
et
|
||||||
eu
|
eu
|
||||||
@ -29,6 +33,7 @@ kn
|
|||||||
lt
|
lt
|
||||||
lv
|
lv
|
||||||
mr
|
mr
|
||||||
|
ms
|
||||||
nb
|
nb
|
||||||
nl
|
nl
|
||||||
nn
|
nn
|
||||||
@ -38,11 +43,13 @@ pt
|
|||||||
pt_BR
|
pt_BR
|
||||||
ro
|
ro
|
||||||
ru
|
ru
|
||||||
|
sk
|
||||||
sl
|
sl
|
||||||
sr
|
sr
|
||||||
sr@latin
|
sr@latin
|
||||||
sv
|
sv
|
||||||
ta
|
ta
|
||||||
|
te
|
||||||
th
|
th
|
||||||
tr
|
tr
|
||||||
ug
|
ug
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
data/gnome-shell.desktop.in.in
|
data/gnome-shell.desktop.in.in
|
||||||
data/org.gnome.shell.gschema.xml.in
|
data/org.gnome.shell.gschema.xml.in
|
||||||
|
js/gdm/loginDialog.js
|
||||||
js/misc/util.js
|
js/misc/util.js
|
||||||
js/ui/appDisplay.js
|
js/ui/appDisplay.js
|
||||||
js/ui/appFavorites.js
|
js/ui/appFavorites.js
|
||||||
|
js/ui/autorunManager.js
|
||||||
js/ui/calendar.js
|
js/ui/calendar.js
|
||||||
|
js/ui/contactDisplay.js
|
||||||
js/ui/dash.js
|
js/ui/dash.js
|
||||||
js/ui/dateMenu.js
|
js/ui/dateMenu.js
|
||||||
js/ui/docDisplay.js
|
js/ui/docDisplay.js
|
||||||
js/ui/endSessionDialog.js
|
js/ui/endSessionDialog.js
|
||||||
|
js/ui/keyboard.js
|
||||||
js/ui/lookingGlass.js
|
js/ui/lookingGlass.js
|
||||||
js/ui/messageTray.js
|
js/ui/messageTray.js
|
||||||
|
js/ui/networkAgent.js
|
||||||
|
js/ui/notificationDaemon.js
|
||||||
js/ui/overview.js
|
js/ui/overview.js
|
||||||
js/ui/panel.js
|
js/ui/panel.js
|
||||||
js/ui/placeDisplay.js
|
js/ui/placeDisplay.js
|
||||||
@ -17,7 +23,7 @@ js/ui/polkitAuthenticationAgent.js
|
|||||||
js/ui/popupMenu.js
|
js/ui/popupMenu.js
|
||||||
js/ui/runDialog.js
|
js/ui/runDialog.js
|
||||||
js/ui/searchDisplay.js
|
js/ui/searchDisplay.js
|
||||||
js/ui/statusMenu.js
|
js/ui/shellMountOperation.js
|
||||||
js/ui/status/accessibility.js
|
js/ui/status/accessibility.js
|
||||||
js/ui/status/bluetooth.js
|
js/ui/status/bluetooth.js
|
||||||
js/ui/status/keyboard.js
|
js/ui/status/keyboard.js
|
||||||
@ -25,11 +31,10 @@ js/ui/status/network.js
|
|||||||
js/ui/status/power.js
|
js/ui/status/power.js
|
||||||
js/ui/status/volume.js
|
js/ui/status/volume.js
|
||||||
js/ui/telepathyClient.js
|
js/ui/telepathyClient.js
|
||||||
|
js/ui/userMenu.js
|
||||||
js/ui/viewSelector.js
|
js/ui/viewSelector.js
|
||||||
js/ui/windowAttentionHandler.js
|
js/ui/windowAttentionHandler.js
|
||||||
js/ui/workspacesView.js
|
|
||||||
src/gvc/gvc-mixer-control.c
|
src/gvc/gvc-mixer-control.c
|
||||||
src/gdmuser/gdm-user.c
|
|
||||||
src/main.c
|
src/main.c
|
||||||
src/shell-app.c
|
src/shell-app.c
|
||||||
src/shell-app-system.c
|
src/shell-app-system.c
|
||||||
|