Compare commits
701 Commits
wip/smartc
...
3.11.1
Author | SHA1 | Date | |
---|---|---|---|
cf7cf45003 | |||
633dd0d9de | |||
b929320d4c | |||
7296bedd8e | |||
e9fbbf4000 | |||
dd44219aa5 | |||
8cc1fe007d | |||
c0b45c9fc4 | |||
41315f45a9 | |||
04d28a0eea | |||
1e9cd3f785 | |||
f462dd6a4c | |||
34e75fc595 | |||
dac513e046 | |||
2c538d247b | |||
f3b7f61e54 | |||
d47ecf19f5 | |||
da19b344b5 | |||
eb66407926 | |||
61c5b8e7d2 | |||
4b09d57ec2 | |||
a16f699dfc | |||
b908a3d70a | |||
d519c7263e | |||
5dedc5d8ba | |||
3ca1784ff4 | |||
b54d512f3f | |||
9d8fb19f55 | |||
1ac4ab7edc | |||
c9b73ac731 | |||
e0b87f1e14 | |||
394743efc8 | |||
27a86a4756 | |||
8ee0ef2cde | |||
43f4682ec4 | |||
5f081b8f8d | |||
5023542882 | |||
88d0731d80 | |||
06cb8c52d7 | |||
4d1358b7ed | |||
0cfa7c1c56 | |||
76928390a3 | |||
af1f9cd76d | |||
2f39f3d146 | |||
f72f39bc26 | |||
2659ba6bb4 | |||
0b8c0c202e | |||
37c8132632 | |||
02f2f694e4 | |||
d175a588f7 | |||
4bb41f2f66 | |||
088c46c7be | |||
4228c40b3d | |||
d581d29198 | |||
5a7e854f9e | |||
6e13823ccc | |||
5c5f2fdf8f | |||
96aa33f4ef | |||
25fd23e703 | |||
99cf4e5787 | |||
66a4cb5875 | |||
da14e2c349 | |||
4cda61a16a | |||
002afda503 | |||
cb7a2e8c6a | |||
d21ae1dad1 | |||
4548859509 | |||
09c6e6427a | |||
8c34398a15 | |||
a65b705080 | |||
7e28c71074 | |||
726f4a6715 | |||
a444b43548 | |||
473bb139d1 | |||
d084770cea | |||
619389ed20 | |||
ad043e009e | |||
89b9d079b1 | |||
58a8845047 | |||
3e6c8e68b4 | |||
40cd92f701 | |||
e216addf7c | |||
9291594330 | |||
f6010864ea | |||
13e6a6def5 | |||
575b373cd5 | |||
0892065649 | |||
766ef367fb | |||
0c7d9958f5 | |||
425b8f6073 | |||
63593e45a6 | |||
a6ee9806de | |||
a6579f4ceb | |||
639622a4fe | |||
e28ec2f5ab | |||
873753c735 | |||
ff14951be4 | |||
b47b445558 | |||
6e9a2fea89 | |||
64d2679b3c | |||
3c3d4dfccb | |||
0ccffed517 | |||
20c18c1fc0 | |||
a21c0097c2 | |||
404d9ef2af | |||
503fa1cbce | |||
63f7991d0f | |||
ae301c1f39 | |||
8e911fb719 | |||
a0d84e44c5 | |||
81fb7ebb31 | |||
3751211590 | |||
6b4cba09be | |||
6a7d184b7b | |||
bde5cfc8bb | |||
777c7a952b | |||
c79bdd9029 | |||
6a154efe65 | |||
627f3ef36b | |||
3d28836f2c | |||
c28bd04958 | |||
a6fb3acb42 | |||
8a8b3bf96e | |||
f7624e5f05 | |||
55edfd2e4a | |||
91878dd52c | |||
c132358ec9 | |||
e426f8ac47 | |||
740dca8afc | |||
6ae06d319c | |||
30bc2b2f9c | |||
7dc8e9d657 | |||
1ab2fa5bf0 | |||
779a1077bf | |||
815bf7a53a | |||
91a382dfb5 | |||
2d8d4cd57d | |||
0c5fe2b3bf | |||
56c487347f | |||
0a572fce1b | |||
e898e29910 | |||
843f076225 | |||
fdb732c8c2 | |||
33896a4e8f | |||
51e016a0d6 | |||
8737b06559 | |||
15ab285174 | |||
3a4782cc64 | |||
0256a6d47b | |||
8b0e846e0e | |||
41acb5d3cc | |||
a2f9b8ea9b | |||
6237a1c505 | |||
7c08db0b0f | |||
df1270ac49 | |||
46edc053d4 | |||
908046c31a | |||
8380c79875 | |||
8a4879a96a | |||
cdf1a77f08 | |||
3f9857ccbd | |||
1d65a31420 | |||
dafdf0838a | |||
f9cf135f68 | |||
a1878e54c9 | |||
95e5d899a9 | |||
ee8321df67 | |||
4918213e68 | |||
ed7f349fc6 | |||
2888f22a24 | |||
fcb217f681 | |||
9ffa9fe1a8 | |||
905020c507 | |||
02f5500641 | |||
465af55d6e | |||
80a3bb85aa | |||
ea26bd3003 | |||
508a511d2a | |||
2d80cb71db | |||
e31693bbee | |||
fb561f10a7 | |||
0c57d53e03 | |||
3b1b9f589b | |||
ac8d39acf4 | |||
664e795217 | |||
82bf323f63 | |||
547ac85113 | |||
46e0e4430d | |||
609a31ea46 | |||
3e99eb10d1 | |||
b9d935af0c | |||
31d3e82aa8 | |||
dfdc17197b | |||
62b965b4b7 | |||
ba221abea5 | |||
aa026c7134 | |||
496cfff97a | |||
ccaa7f5f3e | |||
f492d21c70 | |||
1983541f8c | |||
e4cb3672b9 | |||
a06a78a9c1 | |||
2ba91ad837 | |||
66eb3ea723 | |||
d30e992b20 | |||
c6a342563f | |||
fd584eda05 | |||
4301506590 | |||
88f7c3a970 | |||
f21a9f0cc2 | |||
2233c2e618 | |||
6264419bd4 | |||
b62effb8fa | |||
c8a07dd612 | |||
02c99e4b25 | |||
1242a16265 | |||
a89fd17b8e | |||
3d5e7bd6f1 | |||
cefcb89487 | |||
005272bde9 | |||
491e60e4f2 | |||
e5f72fd302 | |||
5f21b100b8 | |||
1a4c7629c5 | |||
d21734ee47 | |||
e140e2c367 | |||
7ced1f5b54 | |||
54b028ee3e | |||
703336e1ea | |||
9e936252ae | |||
fc71a0f081 | |||
86c72fa15d | |||
6ba5af1e9e | |||
33a4f59cfb | |||
39134f0d9b | |||
30e7440851 | |||
be1a7bac7c | |||
fb52a93a28 | |||
efdf1ff755 | |||
2c00dad211 | |||
c23786c73e | |||
7f1b07b76f | |||
2f35ad6e65 | |||
159c7d34c7 | |||
fe8e990ed7 | |||
1fb9b18cb6 | |||
5c2586127b | |||
661b266b45 | |||
98af044196 | |||
8006c336f5 | |||
bdf07d2ce8 | |||
efcf858e60 | |||
93d9c16672 | |||
7aaf261f5a | |||
5eb4450012 | |||
49c8cdd8f6 | |||
c860b96a86 | |||
69403bda80 | |||
f5456b66ff | |||
28b4c413cb | |||
5b97250bb1 | |||
5d26c29eaa | |||
613944eccd | |||
8d0e8fc021 | |||
41ee70d414 | |||
3691e8ddd7 | |||
be54e94045 | |||
366ca72342 | |||
fc4e392ac1 | |||
507be35d3a | |||
5c0d62cd0e | |||
7b7c4568b2 | |||
f38091d96b | |||
7c78e1fbf5 | |||
72f0a48fac | |||
193f872ebe | |||
c3f96cf0e8 | |||
df09109d81 | |||
662cb9e2a3 | |||
daa54a3798 | |||
f035a1a0e0 | |||
2688bf3333 | |||
4095a58eb9 | |||
c1b1ebe97e | |||
a47b97d443 | |||
df5d5583eb | |||
79e764d5ec | |||
2fcb04e5b2 | |||
da1e264687 | |||
03975287d2 | |||
50a61b38f7 | |||
1fe072471e | |||
93e840295e | |||
6ab7d640f0 | |||
255cb8edb1 | |||
367fb32493 | |||
ef6d1fd6ce | |||
3e8ab0645b | |||
135727c9f7 | |||
c58448817b | |||
8ae0f1a9dc | |||
ba9c1d98f6 | |||
4db6e70f97 | |||
9d1f789937 | |||
11c2933e23 | |||
fbd4951ea7 | |||
744749f2f3 | |||
db1c65970b | |||
2d8ed4c77f | |||
9ba970b83d | |||
954d262d67 | |||
1b6090fe13 | |||
f8234b07f8 | |||
25318f696d | |||
1ab3d12bc7 | |||
d66e0a0b45 | |||
d46ceead04 | |||
1cc9480e56 | |||
c022b541f1 | |||
96588466d4 | |||
dbde12f8bf | |||
660f0fec16 | |||
fd9401cc62 | |||
1edb9f7525 | |||
15cfb9d1d9 | |||
da6744da2d | |||
bd5aa66a5f | |||
7c30fe7738 | |||
8ce599df38 | |||
75fe13f1df | |||
8ad6ded3ec | |||
38d22c47f5 | |||
956b6b89b6 | |||
f27c2e6813 | |||
d35c9f880a | |||
d62aacf301 | |||
716ea64212 | |||
c9d6b13f6a | |||
b437e68026 | |||
1dfc38d078 | |||
387184b052 | |||
beec47d7ad | |||
6b554337ff | |||
08f95264d6 | |||
2802920e93 | |||
b04c47c15f | |||
56d96383e2 | |||
f2cbf846e7 | |||
0088e94293 | |||
a03a077e3d | |||
85d2b9e32a | |||
aa6471b3cc | |||
b462a85c43 | |||
9d8f30f955 | |||
420db828e9 | |||
fd8def705d | |||
39c4fa1bf0 | |||
32b964e9b7 | |||
2980515c85 | |||
36bee16781 | |||
4f5d3e00db | |||
6fb044f351 | |||
b403845d03 | |||
9d0e00acce | |||
d5afe8f4f2 | |||
3eb5ca3653 | |||
db07aa42ea | |||
081f51b9eb | |||
38d9c16aba | |||
392a426ddf | |||
d77b2751a6 | |||
3b28308291 | |||
574ecb5ad4 | |||
7a57a780d8 | |||
cf9842433e | |||
c6d089d701 | |||
ec37e2d2b5 | |||
e68b648a33 | |||
56179d8a54 | |||
47d232f694 | |||
fc26fb2149 | |||
92d828b04e | |||
6f9dc913d4 | |||
9ea0f7255f | |||
938628a05f | |||
a765dfc52e | |||
d58f0646cf | |||
792b963bda | |||
9a8bf3b881 | |||
1e02081cd2 | |||
3f24a87034 | |||
dd9f8021ff | |||
74978e84f8 | |||
6ef775390f | |||
1313c1b157 | |||
6d6c400b25 | |||
46bd1b9b18 | |||
dcea8bed6d | |||
e8b35f4623 | |||
ae263bb4db | |||
754ca7c8f2 | |||
804c02701a | |||
fbb4077812 | |||
961e1b89a2 | |||
cc449228f3 | |||
3984c47867 | |||
cfb80266c2 | |||
cc5198205d | |||
a27b44a3c2 | |||
937d064860 | |||
9c814d1584 | |||
415563dc6e | |||
bed653737b | |||
b53be942d4 | |||
1d26161d23 | |||
39afb58472 | |||
22cd18571b | |||
5753eb6682 | |||
e26a6ea71b | |||
6fbe765636 | |||
22b2ccd83d | |||
fc5aadd6dd | |||
5a9f0c24b4 | |||
0c12c072fa | |||
f7284caefd | |||
4e7f317679 | |||
3534d6fddc | |||
0b79e9cc9e | |||
3ce97ccaa8 | |||
407a340b2b | |||
532346ecfb | |||
cd25f5b6cb | |||
5a0ac6c2ac | |||
f0da08bbb1 | |||
fb3f6e2238 | |||
8b977252f3 | |||
9582f9b6e5 | |||
3f15a41006 | |||
e1c4cfd7eb | |||
a326f40bbf | |||
09c2fff8fc | |||
a4c1eb12b4 | |||
4e80758970 | |||
40ae408b3b | |||
84d8d4f622 | |||
5c04840312 | |||
bd28d5c48a | |||
f176d890c0 | |||
3b158a96b7 | |||
dcd0b2bf66 | |||
edd1c89ea1 | |||
32d858dce3 | |||
1e4bb53a34 | |||
f5f94097bf | |||
77a3218db3 | |||
c3ed40905a | |||
268ac0bde8 | |||
88e3f6af47 | |||
21a85832b3 | |||
254efdd122 | |||
62ac6e74d9 | |||
2c2268b39d | |||
41aa14eaf0 | |||
1f50f4658d | |||
d31481fd8b | |||
80ab28bc3a | |||
48b7ebe1c0 | |||
c59cf18337 | |||
b7b1260540 | |||
897c5634b0 | |||
78e3a05e14 | |||
1bb6367b79 | |||
f5512ef21b | |||
a0fa9937ba | |||
ef2345ea85 | |||
dd8fd09470 | |||
a779e2aeca | |||
aaaf25d578 | |||
2e65c852c3 | |||
1b206fe94c | |||
8b93c97a09 | |||
6247b55fc3 | |||
12d9d49fa4 | |||
aef3f097e4 | |||
1a415d5fa7 | |||
e4d46aee97 | |||
d3a88e59b9 | |||
44e3811520 | |||
e0574d2861 | |||
d4f66da793 | |||
c7e3289396 | |||
9cb7aeb32d | |||
4537370a54 | |||
9d2bc1142f | |||
c44caa5c96 | |||
77dc587686 | |||
ce768241da | |||
5f9e50175f | |||
34ec457a47 | |||
dd1651f2d1 | |||
c3c529b001 | |||
aa569304bc | |||
3d57fd3227 | |||
c18a6a6577 | |||
9720301d01 | |||
5ea75499fe | |||
b45bbb77ef | |||
d29b86baf0 | |||
1730aff2b9 | |||
1b03484b04 | |||
f3749753ad | |||
ae80e81b75 | |||
8581e980e9 | |||
7234aa601f | |||
a8a2b7d7bb | |||
9a901c7add | |||
1a0b809ec5 | |||
802c7254b4 | |||
a99e3be44e | |||
d7cf203547 | |||
3364ce53e6 | |||
2a2bcc8984 | |||
6b8339e9b4 | |||
32613ba544 | |||
8e9d91fedc | |||
41bb1c8535 | |||
cb5e34f58a | |||
f7c3cf5d78 | |||
fdc0832506 | |||
4413f816e6 | |||
02224bb5fe | |||
c37c4d8c6d | |||
00ccbdaae9 | |||
777189d7bd | |||
c5dfc432d9 | |||
144f7d6813 | |||
6a19b7c1b0 | |||
741b204fc7 | |||
aa394754b6 | |||
492f8f58bb | |||
b9e1a5708d | |||
576a9e4654 | |||
a7bbbad185 | |||
12ec05aed2 | |||
2bde6612d1 | |||
8460aa7d6a | |||
64efbe703a | |||
d272b0cbf3 | |||
d52c95a15f | |||
2b8414a453 | |||
bc3d019ecf | |||
bc9d44e5d7 | |||
d1a8177778 | |||
8d9aa6388d | |||
6d317d300c | |||
f647b3f0e0 | |||
059b75cdbb | |||
35fa42ca56 | |||
4b450bab11 | |||
4394a05243 | |||
fd11ad95f6 | |||
07b57de03e | |||
a2a5f5df3f | |||
148f2210ca | |||
93f072d1fc | |||
1104a385fa | |||
f5b2febf13 | |||
1b03e55cc3 | |||
4a3f020cd9 | |||
2acd23b14c | |||
339a2f4a6f | |||
0fef796757 | |||
f0e5fb04fc | |||
156be19c2d | |||
956c486345 | |||
062235f3a7 | |||
945b357ed8 | |||
c95ec8e99f | |||
61eb631e59 | |||
1ec349f7c9 | |||
c4bc9616af | |||
de050991d4 | |||
6521951e82 | |||
e818ddf152 | |||
b6499e5248 | |||
3cb809b444 | |||
12b7e56261 | |||
0007343175 | |||
8e49c433e8 | |||
16b2169965 | |||
6a7fa52879 | |||
4afc7438a6 | |||
b80ce2a32f | |||
386f88c9b2 | |||
563f09d9de | |||
423bdc6af1 | |||
0d0413e027 | |||
d4942858ba | |||
81bb7009ea | |||
310dc03e2a | |||
362e0bf1ec | |||
3bb330fcc4 | |||
18f5d0241e | |||
97f5b3f74d | |||
f794acdb5b | |||
f20909ba04 | |||
f495ce13b1 | |||
cac1d67b75 | |||
548111e2ff | |||
f5c0706c2e | |||
e6ef11575b | |||
7563e04743 | |||
33e51cc38b | |||
ef09596648 | |||
93db5a091f | |||
978ab2cdaa | |||
cb09ae5cc0 | |||
6bd275669d | |||
51485396c7 | |||
73e1f238cf | |||
37487c243e | |||
c05627a49e | |||
4ba6791043 | |||
54bec54765 | |||
5c8c4e0aad | |||
5cca26a565 | |||
a347f02545 | |||
a45cc0a048 | |||
52ec5cf8e7 | |||
3e4d0954b5 | |||
e76bcce3bb | |||
5a06b34b1d | |||
79dcb0359f | |||
5dc540b894 | |||
0bbb2786f8 | |||
c1fb1ba94c | |||
974331b825 | |||
f74567fbab | |||
c77c01e437 | |||
2b63680f55 | |||
114d32a28f | |||
cba5bca842 | |||
e99351d4db | |||
2524829023 | |||
a7bcc4c00d | |||
58ca6ec6aa | |||
bb2599eb30 | |||
2a95273b79 | |||
d393bc45a4 | |||
3a14eb9363 | |||
3586e53fd9 | |||
db75734774 | |||
137cbbd141 | |||
24f142df1d | |||
b16ee1a3a6 | |||
1b8580f12b | |||
aefdf15a45 | |||
99af697cd7 | |||
30ff15ec0b | |||
13ce39058d | |||
4ed7f5eb67 | |||
2df0c02dfd | |||
158ca9746c | |||
c58a2e8e46 | |||
41117578c5 | |||
a8ea6c2c66 | |||
7652f4272c | |||
04f1f5f94b | |||
f78e17ce02 | |||
c1518dc728 | |||
899f7da032 | |||
eec4334a78 | |||
2ad9eafeaf | |||
9dc9103d6c | |||
ed0e880913 | |||
a70e74e478 | |||
4d34abeeef | |||
621810c409 | |||
62d2c7efc3 | |||
c410dce07a | |||
d2709c681f | |||
0e6625f719 | |||
cc6a408d5d | |||
d10e3d8498 | |||
c89bab0ff1 | |||
6bc8963e8e | |||
34db64234f | |||
b43f0a4536 | |||
45ba07c214 | |||
4d72bfd495 | |||
925eaa1db0 | |||
69957dac3d | |||
b1c2f4a8d6 | |||
aba6c222cb | |||
b8da674816 | |||
f06dba5007 | |||
8dfcee9039 | |||
be355bf7b4 | |||
a1eedd496c | |||
9f7bad82e5 | |||
4e17d11da6 |
5
.gitignore
vendored
@ -19,6 +19,8 @@ configure
|
||||
data/50-gnome-shell-*.xml
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-wayland.desktop
|
||||
data/gnome-shell-wayland.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
data/gschemas.compiled
|
||||
@ -71,13 +73,14 @@ src/calendar-server/evolution-calendar.desktop.in
|
||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||
src/gnome-shell
|
||||
src/gnome-shell-calendar-server
|
||||
src/gnome-shell-extension-tool
|
||||
src/gnome-shell-extension-prefs
|
||||
src/gnome-shell-extension-tool
|
||||
src/gnome-shell-hotplug-sniffer
|
||||
src/gnome-shell-jhbuild
|
||||
src/gnome-shell-perf-helper
|
||||
src/gnome-shell-perf-tool
|
||||
src/gnome-shell-real
|
||||
src/gnome-shell-wayland
|
||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||
src/run-js-test
|
||||
src/test-recorder
|
||||
|
4
HACKING
@ -138,8 +138,8 @@ GObjects, although this feature isn't used very often in the Shell itself.
|
||||
|
||||
_init: function(icon, label) {
|
||||
this.parent({ reactive: false });
|
||||
this.addActor(icon);
|
||||
this.addActor(label);
|
||||
this.actor.add_child(icon);
|
||||
this.actor.add_child(label);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
|
264
NEWS
@ -1,3 +1,267 @@
|
||||
3.11.1
|
||||
======
|
||||
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
|
||||
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
|
||||
#710137, #710596]
|
||||
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
|
||||
* Don't autostart remote search providers at login [Giovanni; #708830]
|
||||
* Fix spacing in end-session dialog [Sebastien; #710543]
|
||||
* Prepare for js24 [Tim; #711052]
|
||||
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
|
||||
#710144, #710541, #691409, #710745, #688331, #704912]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
|
||||
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
|
||||
|
||||
3.10.1
|
||||
======
|
||||
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
|
||||
* Fix signal strength indicators in network selector [Jasper; #708442]
|
||||
* Scroll search results when focusing provider icons [Jasper; #708868]
|
||||
* Add separate hover/active states to page indicators [Carlos; #708852]
|
||||
* Tweak appearance of user name and avatar [Yash; #702309]
|
||||
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
|
||||
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
|
||||
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
|
||||
* Make network settings items point to the right device [Giovanni; #709246]
|
||||
* Remove animation of window preview titles [Sebastien; #709392]
|
||||
* Add 'Notifications' switch to tray menu [Florian; #707073]
|
||||
* Make dropdown arrows consistent [Carlos; #709564]
|
||||
* power: Use icon from primary device for status [Jasper; #709925]
|
||||
* Fix XDND drags to overview [Adel; #708887]
|
||||
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
|
||||
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
|
||||
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
|
||||
* Use ARROW role for labels representing arrows [Alejandro; #710120]
|
||||
* Make selected view in app picker persistent [Florian; #710042]
|
||||
* Make network selector navigable by keyboard [Alejandro; #710144]
|
||||
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
|
||||
Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
|
||||
#709638, #709866, #709998, #710019, #710104, #710115]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
|
||||
Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
|
||||
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
|
||||
Dieter Verfaillie, Dan Winship
|
||||
|
||||
Translations:
|
||||
Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
|
||||
Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
|
||||
Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
|
||||
Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
|
||||
A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
|
||||
Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
|
||||
Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
|
||||
Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
|
||||
Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
|
||||
Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
|
||||
Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
|
||||
Wouter Bolsterlee [nl], António Lima [pt]
|
||||
|
||||
3.10.0.1
|
||||
=========
|
||||
* Fix login screen [Ray; #708691]
|
||||
|
||||
Contributors:
|
||||
Ray Strode, Giovanni Campagna, Jasper St. Pierree
|
||||
|
||||
Translations:
|
||||
Kjartan Maraas [nb], Marek Černocký [cs], A S Alam [pa], Daniel Mustieles [es],
|
||||
Ihar Hrachyshka [be], Chao-Hsiung Liao [zh_HK], Nilamdyuti Goswami [as],
|
||||
Yuri Myasoedov [ru], Baurzhan Muftakhidinov [kk]
|
||||
|
||||
3.10.0
|
||||
======
|
||||
* Fix fade effect in ScrollViews [Carlos; #708256]
|
||||
* network: Resync when activating connection changes [Jasper; #708322]
|
||||
* Close run dialog when the screen locks [Florian; #708218]
|
||||
* Fix entry growing out of password dialogs [Florian; #708324, #703833]
|
||||
* Vertically center labels in submenu items [Jasper; #708330]
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=708387 [Mike; #708387]
|
||||
* Fix bluetooth icon not being added to status menu [Jasper; #708541]
|
||||
* Fix GNOME 2 keyring dialogs appearing on lock screen [Florian; #708187]
|
||||
* Fix passwords being cleared twice when authentication fails [Florian; #708186]
|
||||
* Fix message tray appearing in a11y popup on login screen [Florian; #708380]
|
||||
* Increase width of aggregate menu popup [Adel; #708472]
|
||||
|
||||
Contributors:
|
||||
Adel Gadllah, Mike Gorse, Ryan Lortie, Florian Müllner, Frédéric Péters,
|
||||
Carlos Soriano, Jasper St. Pierre, Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Daniel Șerbănescu [ro], Ryan Lortie [eo], Ihar Hrachyshka [be],
|
||||
A S Alam [pa], Jiro Matsuzawa [ja], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||
Piotr Drąg [pl], Kristjan SCHMIDT [eo], Daniel Korostil [uk],
|
||||
Rūdolfs Mazurs [lv], Reinout van Schouwen [nl], Yosef Or Boczko [he],
|
||||
Fran Diéguez [gl], António Lima [pt], Andika Triwidada [id],
|
||||
Alexandre Franke [fr], Rafael Ferreira [pt_BR], Milo Casagrande [it],
|
||||
Kenneth Nielsen [da], Matej Urbančič [sl]
|
||||
|
||||
3.9.92
|
||||
======
|
||||
* Don't show page indicators if there's only one page [Florian; #707363]
|
||||
* Make :active style of app and non-app results consistent [Jakub; #704714]
|
||||
* Fade app pages when scrolled [Florian; #707409]
|
||||
* Don't block scrolling on page indicators [Carlos; #707609]
|
||||
* Tweak visual appearance of folder views [Florian; #707662]
|
||||
* Don't put minimized apps at the end of the app switcher [Florian; #707663]
|
||||
* Merge the wayland branch [Giovanni, Neil; #707467]
|
||||
* Make search entry behave better in RTL locales [Matthias, Florian; #705779]
|
||||
* Allow to change app pages with pageUp/pageDown keys [Carlos; #707979]
|
||||
* Set approriate a11y states on expandable menu items [Alejandro; #708038]
|
||||
* Improve page indicator animation [Carlos; #707565]
|
||||
* Misc bug fixes and cleanups [Florian, Olivier, Jasper, Giovanni, Magdalen,
|
||||
Adel, Carlos, Rico, Joanmarie; #707308, #707430, #707508, #707557, #707600,
|
||||
#707614, #707666, #707814, #707806, #707801, #707889, #707892, #707935,
|
||||
#707842, #707940, #707996, #708007, #708009, #708020, #707580, #708080]
|
||||
|
||||
Contributors:
|
||||
Magdalen Berns, Olivier Blin, Giovanni Campagna, Matthias Clasen,
|
||||
Joanmarie Diggs, Adel Gadllah, Florian Müllner, Alejandro Piñeiro,
|
||||
Neil Roberts, Carlos Soriano, Jasper St. Pierre, Jakub Steiner,
|
||||
Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Rafael Ferreira [pt_BR], Fran Diéguez [gl], Daniel Mustieles [es],
|
||||
Aurimas Černius [lt], Luca Ferretti [it], Piotr Drąg [pl],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Timo Jyrinki [fi], Daniel Korostil [uk],
|
||||
Dušan Kazik [sk], Adam Matoušek [cs], Marek Černocký [cs],
|
||||
Jiro Matsuzawa [ja], Yuri Myasoedov [ru], Tobias Endrigkeit [de],
|
||||
Kjartan Maraas [nb], Victor Ibragimov [tg], Мирослав Николић [sr, sr@latin],
|
||||
A S Alam [pa], Khaled Hosny [ar], Andika Triwidada [id],
|
||||
Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Rūdolfs Mazurs [lv],
|
||||
Mattias Põldaru [et], Gabor Kelemen [hu], Bruce Cowan [en_GB],
|
||||
Matej Urbančič [sl], Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
|
||||
Changwoo Ryu [ko], Kris Thomsen [da], Alexandre Franke [fr],
|
||||
Evgeny Bobkin [ru], Baurzhan Muftakhidinov [kk], Peter Mráz [sk],
|
||||
Inaki Larranaga Murgoitio [eu], Yosef Or Boczko [he]
|
||||
|
||||
3.9.91
|
||||
======
|
||||
* Improve submenu styling [Jakub; #706037]
|
||||
* Fix changing slider values via keyboard [Alejandro; #706386]
|
||||
* Fix accessibility of sliders [Alejandro; #706391]
|
||||
* Tweak system actions style [Jakub; #706638]
|
||||
* Add support for auth without username / fix Not Listed? [Ray; #706607]
|
||||
* Dash: Don't show tooltips for apps with open popups [Giovanni; #705611]
|
||||
* Implement new end-session/power-off dialog design [Jasper, Matthias; #706612]
|
||||
* Implement building separate binaries for x11 and wayland [Giovanni; #705497]
|
||||
* authPrompt: Fix controls moving when showing messages [Ray; #706670]
|
||||
* Tweak padding between system status icons [Allan; #706796]
|
||||
* Add a generic accessible usable by JS code [Alejandro; #648623]
|
||||
* Improve keynav and accessibility of the calendar [Alejandro; #706903]
|
||||
* Update to new NetworkManager APIs [Jasper; #706098]
|
||||
* Hide system actions section in the lock screen [Jasper; #706852]
|
||||
* Don't show other logged in users at log out [Giovanni; #707124]
|
||||
* Remove "Session" subtitle heading in login dialog [Jasper; #707072]
|
||||
* dash: Reload favorites when installed apps change [Giovanni; #706878]
|
||||
* Don't open overview after closing last window on workspace [Florian; #662581]
|
||||
* Add FocusApp DBus method [Giovanni; #654086]
|
||||
* Add ShowApplications DBus method [Giovanni; #698743]
|
||||
* Implement new app picker design [Carlos, Florian; #706081]
|
||||
* Improve frequent apps being empty by default [Carlos, Florian; #694710]
|
||||
* Extend clickable area of page indicators [Giovanni; #707314]
|
||||
|
||||
* Misc bug fixes [Ray, Giovanni, Jasper, Emmanuele; #706542, #706654, #706005,
|
||||
#706681, #706841, #706843, #707064, #706262, #707197, #707269]
|
||||
|
||||
Contributors:
|
||||
Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah,
|
||||
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
|
||||
Jakub Steiner, Ray Strode, Seán de Búrca
|
||||
|
||||
Translations:
|
||||
Piotr Drąg [pl], Kjartan Maraas [nb], Victor Ibragimov [tg],
|
||||
Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
|
||||
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Seán de Búrca [ga],
|
||||
Fran Diéguez [gl], Daniel Mustieles [es], Dušan Kazik [sk],
|
||||
Matej Urbančič [sl], Andika Triwidada [id], Jordi Mas [ca],
|
||||
Ihar Hrachyshka [be]
|
||||
|
||||
3.9.90
|
||||
======
|
||||
* workspaceThumbnails: Exclude transient windows when shifting workspaces
|
||||
[Bradley; #705174]
|
||||
* Never show a horizontal scrollbar on lock screen [Jasper; #704327]
|
||||
* authPrompt: Fix disable-user-list / Not Listed? [Ray; #705370]
|
||||
* Animate the lock screen notification transitions [Giovanni; #687660]
|
||||
* Wake up the screen when new notifications appear [Giovanni; #703084]
|
||||
* Use StartupWMClass for application matching [Giovanni; #673657, #705801]
|
||||
* dateMenu: Add style class for the clock label [Jonh; #705634]
|
||||
* keyboard: Translate IBus IME name if possible [Daiki; #695673]
|
||||
* power: Display single digit minutes correctly [Sebastian; #705803]
|
||||
* Implement new aggregate status menu [Jasper; #705845]
|
||||
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
|
||||
* Fix alignment of search provider icons [Tarun; #695760]
|
||||
* Slide dash and workspace switcher on overview transitions [Tarun; #694262]
|
||||
* Respect always-show-universal-access-status setting [Tanner; #705733]
|
||||
* Handle .desktop files with capital letters [Giovanni; #706252]
|
||||
* authPrompt: Add smartcard support [Ray; #683437]
|
||||
* Fix call notifications in busy mode [Emilio; #666221]
|
||||
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
|
||||
* Move message tray menu to a tray button [Jasper; #699272]
|
||||
* Wi-fi dialog improvements [Jasper, Allan; #705916, #706136]
|
||||
* Work towards running as wayland compositor [Giovanni]
|
||||
- Switch to Mutter abstraction layer for cursor tracking [#705911]
|
||||
- Add confirmation dialog for display changes [#706208]
|
||||
* Use a different background in screen shield [Giovanni; #688210]
|
||||
* Add fade animation before blanking the screen [Giovanni; #699112]
|
||||
* Misc. bugfixes and cleanups [Jasper, Giovanni, Adel, Colin, Ray, Florian,
|
||||
Magdalen; #704448, #702536, #686855, #695581, #700901, #701761, #701495,
|
||||
#701848, #697833, #701731, #705664, #705840, #705898, #706089, #706153,
|
||||
#704646, #706262, #706324, #703810, #703811, #704015, #706232, #705917,
|
||||
#706536]
|
||||
|
||||
Contributors:
|
||||
Magdalen Berns, Giovanni Campagna, Allan Day, Tanner Doshier, Adel Gadllah,
|
||||
Sebastian Keller, Tarun Kumar Joshi, Florian Müllner, Bradley Pankow,
|
||||
Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
|
||||
Daiki Ueno, Colin Walters, Jonh Wendell
|
||||
|
||||
Translations:
|
||||
Kjartan Maraas [nb], Aurimas Černius [lt], Yaron Shahrabani [he],
|
||||
Fran Diéguez [gl], Gabor Kelemen [hu],
|
||||
Juan Diego Martins da Costa Cruz [pt_BR], Inaki Larranaga Murgoitio [eu],
|
||||
Yuri Myasoedov [ru], Daniel Mustieles [es], Seán de Búrca [ga],
|
||||
Khaled Hosny [ar], Victor Ibragimov [tg], Friedel Wolff [af],
|
||||
Marek Černocký [cs], Matej Urbančič [sl], A S Alam [pa],
|
||||
Rafael Ferreira [pt_BR], Andika Triwidada [id], Dušan Kazik [sk]
|
||||
|
||||
3.9.5
|
||||
=====
|
||||
* Fix width changes of the calendar popup [Florian; #704200]
|
||||
* Work towards aggregate status menu [Jasper; #702539, #704336, #704368,
|
||||
#704670]
|
||||
* Update design of lock screen notifications [Allan; #702305]
|
||||
* Don't show empty backgroundMenu [Michael; #703868]
|
||||
* Add option to limit app switcher to current workspace [Adel; #703538]
|
||||
* Consolidate design of login screen and unlock dialog [Ray; #702308, #704795]
|
||||
* Respect hasWorkspace property of session mode [Jasper; #698593]
|
||||
* Fix fade of app menu icon in RTL locales [Jasper; #704583]
|
||||
* Destroy notifications when the close button is clicked [Adel; #687016]
|
||||
* Fix clicks on legacy tray icons in the message tray [Florian; #704095]
|
||||
* authPrompt: Fade out message if users start to type [Ray; #704817]
|
||||
* Export timestamps of global shortcuts on DBus [Bastien; #704859]
|
||||
* Fix duplicate search provider results [Jasper; #700283]
|
||||
* Misc bug fixes and cleanups [Lionel, Florian, Emilio, Ray, Jasper; #703859,
|
||||
#703540, #704077, #703997, #704318, #704347, #704265, #704411, #704430,
|
||||
#704347, #704453, #704471, #704542, #704707, #703905, #705037]
|
||||
|
||||
Contributors:
|
||||
Allan Day, Adel Gadllah, Lionel Landwerlin, Florian Müllner, Bastien Nocera,
|
||||
Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Colin Walters,
|
||||
Michael Wood
|
||||
|
||||
Translations:
|
||||
eternalhui [zh_CN], Victor Ibragimov [tg], Dušan Kazik [sk],
|
||||
Jiro Matsuzawa [ja], Kjartan Maraas [nb], Milo Casagrande [it],
|
||||
Marek Černocký [cs], Daniel Mustieles [es], Benjamin Steinwender [de]
|
||||
|
||||
3.9.4
|
||||
=====
|
||||
* Fix chat entries not being focused when expanded [Jasper; #698778]
|
||||
|
@ -17,5 +17,4 @@ libgnome_shell_browser_plugin_la_SOURCES = \
|
||||
|
||||
libgnome_shell_browser_plugin_la_CFLAGS = \
|
||||
$(BROWSER_PLUGIN_CFLAGS) \
|
||||
-DG_DISABLE_DEPRECATED \
|
||||
-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"
|
||||
|
67
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.9.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.11.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -16,6 +16,7 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
# Initialize libtool
|
||||
LT_PREREQ([2.2.6])
|
||||
@ -24,9 +25,6 @@ LT_INIT([disable-static])
|
||||
# i18n
|
||||
IT_PROG_INTLTOOL([0.40])
|
||||
|
||||
AM_GNU_GETTEXT([external])
|
||||
AM_GNU_GETTEXT_VERSION([0.17])
|
||||
|
||||
GETTEXT_PACKAGE=gnome-shell
|
||||
AC_SUBST(GETTEXT_PACKAGE)
|
||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||
@ -53,7 +51,7 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
@ -62,8 +60,8 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.13.4
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.35.4
|
||||
MUTTER_MIN_VERSION=3.9.4
|
||||
GJS_MIN_VERSION=1.38.1
|
||||
MUTTER_MIN_VERSION=3.11.1
|
||||
GTK_MIN_VERSION=3.7.9
|
||||
GIO_MIN_VERSION=2.37.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
@ -78,25 +76,35 @@ NETWORKMANAGER_MIN_VERSION=0.9.8
|
||||
PULSE_MIN_VERS=2.0
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
atk-bridge-2.0
|
||||
libmutter >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra libcanberra-gtk3
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
|
||||
SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
atk-bridge-2.0
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
xtst
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra libcanberra-gtk3
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
|
||||
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(MUTTER_WAYLAND, [libmutter-wayland >= $MUTTER_MIN_VERSION],
|
||||
[MUTTER_WAYLAND_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter-wayland`
|
||||
AC_SUBST(MUTTER_WAYLAND_TYPELIB_DIR)
|
||||
have_mutter_wayland=yes],
|
||||
[have_mutter_wayland=no])
|
||||
|
||||
AM_CONDITIONAL(HAVE_MUTTER_WAYLAND, test $have_mutter_wayland != no)
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
@ -132,8 +140,9 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
AC_SUBST(MUTTER_GIR_DIR)
|
||||
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
AC_SUBST(MUTTER_TYPELIB_DIR)
|
||||
|
||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
||||
@ -173,10 +182,6 @@ AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||
|
||||
GNOME_COMPILE_WARNINGS([error])
|
||||
|
||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
||||
|
||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="Screenshots"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-recording"
|
||||
_description="Record a screencast"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
@ -1,8 +1,9 @@
|
||||
wandadir = $(pkgdatadir)
|
||||
dist_wanda_DATA = wanda.png
|
||||
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
if HAVE_MUTTER_WAYLAND
|
||||
desktop_DATA += gnome-shell-wayland.desktop
|
||||
endif HAVE_MUTTER_WAYLAND
|
||||
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
@ -41,6 +42,10 @@ dist_theme_DATA = \
|
||||
theme/message-tray-background.png \
|
||||
theme/more-results.svg \
|
||||
theme/noise-texture.png \
|
||||
theme/page-indicator-active.svg \
|
||||
theme/page-indicator-inactive.svg \
|
||||
theme/page-indicator-checked.svg \
|
||||
theme/page-indicator-hover.svg \
|
||||
theme/panel-button-border.svg \
|
||||
theme/panel-button-highlight-narrow.svg \
|
||||
theme/panel-button-highlight-wide.svg \
|
||||
@ -56,10 +61,7 @@ dist_theme_DATA = \
|
||||
theme/ws-switch-arrow-down.png
|
||||
|
||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
keys_in_files = \
|
||||
50-gnome-shell-screenshot.xml.in \
|
||||
50-gnome-shell-system.xml.in \
|
||||
$(NULL)
|
||||
keys_in_files = 50-gnome-shell-system.xml.in
|
||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
@ -84,6 +86,7 @@ convert_DATA = gnome-shell-overrides.convert
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-wayland.desktop.in.in \
|
||||
gnome-shell-extension-prefs.desktop.in.in \
|
||||
$(introspection_DATA) \
|
||||
$(menu_DATA) \
|
||||
@ -93,6 +96,7 @@ EXTRA_DIST = \
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-wayland.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(keys_DATA) \
|
||||
|
15
data/gnome-shell-wayland.desktop.in.in
Normal file
@ -0,0 +1,15 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell (wayland compositor)
|
||||
_Comment=Window management and application launching
|
||||
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;Core;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=DisplayServer
|
||||
X-GNOME-Autostart-Notify=true
|
||||
X-GNOME-AutoRestart=false
|
@ -37,6 +37,13 @@
|
||||
application view, rather than being displayed inline in the main view.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="app-picker-view" type="u">
|
||||
<default>0</default>
|
||||
<summary>App Picker View</summary>
|
||||
<description>
|
||||
Index of the currently selected view in the application picker.
|
||||
</description>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
@ -45,16 +52,6 @@
|
||||
<default>[]</default>
|
||||
<_summary>History for the looking glass dialog</_summary>
|
||||
</key>
|
||||
<key name="saved-im-presence" type="i">
|
||||
<default>1</default>
|
||||
<_summary>Internally used to store the last IM presence explicitly set by the user. The
|
||||
value here is from the TpConnectionPresenceType enumeration.</_summary>
|
||||
</key>
|
||||
<key name="saved-session-presence" type="i">
|
||||
<default>0</default>
|
||||
<_summary>Internally used to store the last session presence status for the user. The
|
||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
<key name="always-show-log-out" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
|
||||
@ -74,7 +71,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</_description>
|
||||
</key>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||
</schema>
|
||||
@ -128,13 +124,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
Keybinding to focus the active notification.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-recording" type="as">
|
||||
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
|
||||
<_summary>Keybinding to toggle the screen recorder</_summary>
|
||||
<_description>
|
||||
Keybinding to start/stop the builtin screen recorder.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
||||
@ -148,44 +137,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="framerate" type="i">
|
||||
<default>30</default>
|
||||
<_summary>Framerate used for recording screencasts.</_summary>
|
||||
<_description>
|
||||
The framerate of the resulting screencast recordered
|
||||
by GNOME Shell's screencast recorder in frames-per-second.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="pipeline" type="s">
|
||||
<default>''</default>
|
||||
<_summary>The gstreamer pipeline used to encode the screencast</_summary>
|
||||
<_description>
|
||||
Sets the GStreamer pipeline used to encode recordings.
|
||||
It follows the syntax used for gst-launch. The pipeline should have
|
||||
an unconnected sink pad where the recorded video is recorded. It will
|
||||
normally have a unconnected source pad; output from that pad
|
||||
will be written into the output file. However the pipeline can also
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="file-extension" type="s">
|
||||
<default>'webm'</default>
|
||||
<_summary>File extension used for storing the screencast</_summary>
|
||||
<_description>
|
||||
The filename for recorded screencasts will be a unique filename
|
||||
based on the current date, and use this extension. It should be
|
||||
changed when recording to a different container format.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.app-switcher"
|
||||
path="/org/gnome/shell/app-switcher/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
@ -272,10 +223,10 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
|
||||
<key name="focus-change-on-pointer-rest" type="b">
|
||||
<default>true</default>
|
||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||
<description>
|
||||
<_summary>Delay focus changes in mouse mode until the pointer stops moving</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
@ -101,7 +101,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Check Boxes */
|
||||
|
||||
.check-box ShellGenericContainer {
|
||||
.check-box StBoxLayout {
|
||||
spacing: .8em;
|
||||
}
|
||||
|
||||
@ -127,20 +127,20 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.slider {
|
||||
height: 1em;
|
||||
min-width: 15em;
|
||||
-slider-height: 0.3em;
|
||||
-slider-background-color: #333333;
|
||||
-slider-border-color: #5f5f5f;
|
||||
-slider-active-background-color: #76b0ec;
|
||||
-slider-active-border-color: #1f6dbc;
|
||||
-slider-border-width: 1px;
|
||||
-slider-handle-radius: 0.5em;
|
||||
-slider-handle-radius: 6px;
|
||||
}
|
||||
|
||||
/* PopupMenu */
|
||||
|
||||
.popup-menu-ornament {
|
||||
text-align: center;
|
||||
text-align: right;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.popup-menu-boxpointer,
|
||||
@ -157,13 +157,17 @@ StScrollBar StButton#vhandle:active {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.unicode-arrow {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.popup-submenu-menu-item:open {
|
||||
background-color: #4c4c4c;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.popup-sub-menu {
|
||||
background-gradient-start: rgba(80,80,80,0.3);
|
||||
background-gradient-end: rgba(80,80,80,0.7);
|
||||
background-gradient-end: rgba(80,80,80,0.4);
|
||||
background-gradient-direction: vertical;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
}
|
||||
@ -197,8 +201,15 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.popup-menu-item {
|
||||
padding: .4em 1.75em;
|
||||
spacing: 1em;
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.popup-menu-item:ltr {
|
||||
padding: .4em 1.75em .4em 0em;
|
||||
}
|
||||
|
||||
.popup-menu-item:rtl {
|
||||
padding: .4em 0em .4em 1.75em;
|
||||
}
|
||||
|
||||
.popup-menu-item:active {
|
||||
@ -265,15 +276,17 @@ StScrollBar StButton#vhandle:active {
|
||||
/* Network */
|
||||
|
||||
.nm-dialog {
|
||||
max-height: 400px;
|
||||
max-height: 500px;
|
||||
min-height: 450px;
|
||||
min-width: 470px;
|
||||
}
|
||||
|
||||
.nm-dialog-content {
|
||||
spacing: 8px;
|
||||
spacing: 20px;
|
||||
}
|
||||
|
||||
.nm-dialog-header-hbox {
|
||||
spacing: 4px;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
.nm-dialog-header-icon {
|
||||
@ -293,9 +306,10 @@ StScrollBar StButton#vhandle:active {
|
||||
font-size: 12pt;
|
||||
border-bottom: 1px solid #666;
|
||||
padding: 12px;
|
||||
spacing: 20px;
|
||||
}
|
||||
|
||||
.nm-dialog-item:checked {
|
||||
.nm-dialog-item:selected {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
@ -614,16 +628,22 @@ StScrollBar StButton#vhandle:active {
|
||||
-boxpointer-gap: 4px;
|
||||
}
|
||||
|
||||
.panel-status-button-box {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.lock-screen-status-button-box {
|
||||
spacing: 8px;
|
||||
.panel-status-indicators-box,
|
||||
.panel-status-menu-box {
|
||||
spacing: 2px;
|
||||
}
|
||||
|
||||
.system-status-icon {
|
||||
icon-size: 1.09em;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.aggregate-menu {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.aggregate-menu .popup-menu-icon {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.system-switch-user-submenu-icon {
|
||||
@ -633,17 +653,31 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.system-menu-action {
|
||||
color: #e6e6e6;
|
||||
border-radius: 4px;
|
||||
padding: 6px;
|
||||
border-radius: 32px; /* wish we could do 50% */
|
||||
padding: 13px;
|
||||
border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */
|
||||
}
|
||||
|
||||
.system-menu-action:hover {
|
||||
.system-menu-action:hover,
|
||||
.system-menu-action:focus {
|
||||
color: white;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
background-color: #4c4c4c;
|
||||
border: none;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.system-menu-action:active {
|
||||
color: black;
|
||||
background-color: #6f6f6f;
|
||||
|
||||
}
|
||||
|
||||
.system-menu-action > StIcon {
|
||||
icon-size: 32px;
|
||||
icon-size: 16px;
|
||||
}
|
||||
|
||||
.screencast-indicator {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
/* Overview */
|
||||
@ -656,7 +690,9 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.workspace-thumbnails-background {
|
||||
.workspace-thumbnails {
|
||||
spacing: 11px;
|
||||
visible-width: 32px; /* Amount visible before hovering */
|
||||
border: 1px solid rgba(128, 128, 128, 0.4);
|
||||
border-right: 0px;
|
||||
border-radius: 9px 0px 0px 9px;
|
||||
@ -664,18 +700,13 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 11px 7px 11px 11px;
|
||||
}
|
||||
|
||||
.workspace-thumbnails-background:rtl {
|
||||
.workspace-thumbnails:rtl {
|
||||
border-right: 1px;
|
||||
border-left: 0px;
|
||||
border-radius: 0px 9px 9px 0px;
|
||||
padding: 11px 11px 11px 7px;
|
||||
}
|
||||
|
||||
.workspace-thumbnails {
|
||||
spacing: 11px;
|
||||
visible-width: 32px; /* Amount visible before hovering */
|
||||
}
|
||||
|
||||
.workspace-thumbnail-indicator {
|
||||
border: 4px solid rgba(255,255,255,0.7);
|
||||
border-radius: 4px;
|
||||
@ -871,9 +902,9 @@ StScrollBar StButton#vhandle:active {
|
||||
/* Application Launchers, Grid and List results */
|
||||
|
||||
.icon-grid {
|
||||
spacing: 36px;
|
||||
-shell-grid-horizontal-item-size: 118px;
|
||||
-shell-grid-vertical-item-size: 118px;
|
||||
spacing: 30px;
|
||||
-shell-grid-horizontal-item-size: 136px;
|
||||
-shell-grid-vertical-item-size: 136px;
|
||||
}
|
||||
|
||||
.icon-grid .overview-icon {
|
||||
@ -881,7 +912,6 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.app-display {
|
||||
padding: 8px;
|
||||
spacing: 20px;
|
||||
}
|
||||
|
||||
@ -897,12 +927,39 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 3px 31px;
|
||||
}
|
||||
|
||||
|
||||
.search-display > StBoxLayout,
|
||||
.all-apps > StBoxLayout,
|
||||
.all-apps,
|
||||
.frequent-apps > StBoxLayout {
|
||||
/* horizontal padding to make sure scrollbars or dash don't overlap content */
|
||||
padding: 0px 88px;
|
||||
padding: 0px 88px 10px 88px;
|
||||
}
|
||||
|
||||
.page-indicator {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.page-indicator .page-indicator-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-image: url(page-indicator-inactive.svg);
|
||||
}
|
||||
|
||||
.page-indicator:hover .page-indicator-icon {
|
||||
background-image: url(page-indicator-hover.svg);
|
||||
}
|
||||
|
||||
.page-indicator:active .page-indicator-icon {
|
||||
background-image: url(page-indicator-active.svg);
|
||||
}
|
||||
|
||||
.page-indicator:checked .page-indicator-icon,
|
||||
.page-indicator:checked:active .page-indicator-icon {
|
||||
background-image: url(page-indicator-checked.svg);
|
||||
}
|
||||
|
||||
.no-frequent-applications-label {
|
||||
font-size: 18pt;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.app-folder-icon {
|
||||
@ -934,27 +991,39 @@ StScrollBar StButton#vhandle:active {
|
||||
background-image: url("more-results.svg");
|
||||
}
|
||||
|
||||
.app-well-app > .overview-icon.overview-icon-with-label,
|
||||
.grid-search-result .overview-icon.overview-icon-with-label {
|
||||
/* since the label controls its own spacing, it is visually more
|
||||
consistent to use different padding values for top and bottom */
|
||||
padding: 10px 8px 5px 8px;
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.app-well-app > .overview-icon,
|
||||
.show-apps > .overview-icon,
|
||||
.search-provider-icon,
|
||||
.list-search-result,
|
||||
.grid-search-result .overview-icon {
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
padding: 6px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
transition-duration: 100ms;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search-provider-icon {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.app-folder-popup {
|
||||
-arrow-border-radius: 8px;
|
||||
-arrow-background-color: black;
|
||||
-arrow-background-color: rgba(0,0,0,0.3);
|
||||
-arrow-base: 24px;
|
||||
-arrow-rise: 11px;
|
||||
}
|
||||
|
||||
.app-folder-popup-bin {
|
||||
padding: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.app-well-app.running > .overview-icon {
|
||||
@ -964,7 +1033,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.app-well-app.app-folder > .overview-icon {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.app-well-app:hover > .overview-icon,
|
||||
@ -979,7 +1048,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.app-display .app-well-app > .overview-icon {
|
||||
border-radius: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.list-search-result:hover .list-search-result-description {
|
||||
@ -1001,12 +1070,14 @@ StScrollBar StButton#vhandle:active {
|
||||
.app-well-app:checked > .overview-icon,
|
||||
.app-well-app:active > .overview-icon,
|
||||
.show-apps:checked > .overview-icon,
|
||||
.show-apps:active > .overview-icon {
|
||||
.show-apps:active > .overview-icon,
|
||||
.search-provider-icon:active,
|
||||
.list-search-result:active {
|
||||
background-gradient-start: rgba(255, 255, 255, .05);
|
||||
background-gradient-end: rgba(255, 255, 255, .15);
|
||||
background-gradient-direction: vertical;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
|
||||
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 0.7);
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
@ -1171,6 +1242,10 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.calendar-month-label:focus {
|
||||
background-color: #999999;
|
||||
}
|
||||
|
||||
.calendar-change-month-back {
|
||||
width: 18px;
|
||||
height: 12px;
|
||||
@ -1216,6 +1291,10 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #eeeeec;
|
||||
}
|
||||
|
||||
.datemenu-date-label:focus {
|
||||
background-color: #999999;
|
||||
}
|
||||
|
||||
.calendar-day-base {
|
||||
font-size: 9pt;
|
||||
text-align: center;
|
||||
@ -1357,9 +1436,20 @@ StScrollBar StButton#vhandle:active {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.no-messages-label {
|
||||
font-family: cantarell, sans-serif;
|
||||
font-size: 11pt;
|
||||
.message-tray-menu-button StIcon {
|
||||
padding: 0 20px;
|
||||
color: #aaaaaa;
|
||||
icon-size: 24px;
|
||||
}
|
||||
|
||||
.message-tray-menu-button:hover StIcon,
|
||||
.message-tray-menu-button:active StIcon,
|
||||
.message-tray-menu-button:focus StIcon {
|
||||
color: #eeeeee;
|
||||
}
|
||||
|
||||
.no-messages-label,
|
||||
.no-networks-label {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
@ -1575,7 +1665,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.chat-notification-scrollview{
|
||||
max-height: 22em;
|
||||
max-height: 22em;
|
||||
}
|
||||
|
||||
.subscription-message {
|
||||
@ -1795,8 +1885,6 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.modal-dialog-button {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
padding: 4px 32px 5px;
|
||||
}
|
||||
|
||||
@ -1835,6 +1923,10 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 42px;
|
||||
}
|
||||
|
||||
.end-session-dialog-list {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.end-session-dialog-subject {
|
||||
padding-left: 17px;
|
||||
padding-bottom: 20px;
|
||||
@ -1868,50 +1960,35 @@ StScrollBar StButton#vhandle:active {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list {
|
||||
font-size: 10pt;
|
||||
.end-session-dialog-inhibitor-layout {
|
||||
spacing: 16px;
|
||||
max-height: 200px;
|
||||
padding-top: 42px;
|
||||
padding-left: 49px;
|
||||
padding-right: 32px;
|
||||
padding-right: 50px;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list:rtl {
|
||||
padding-right: 49px;
|
||||
padding-left: 32px;
|
||||
.end-session-dialog-session-list,
|
||||
.end-session-dialog-app-list {
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item {
|
||||
color: #ccc;
|
||||
.end-session-dialog-list-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item:hover {
|
||||
color: white;
|
||||
.end-session-dialog-app-list-item,
|
||||
.end-session-dialog-session-list-item {
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item:ltr {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item:rtl {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item-icon:ltr {
|
||||
padding-right: 17px;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item-icon:rtl {
|
||||
padding-left: 17px;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item-name {
|
||||
font-size: 10pt;
|
||||
.end-session-dialog-app-list-item-name,
|
||||
.end-session-dialog-session-list-item-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item-description {
|
||||
font-size: 8pt;
|
||||
color: #444444;
|
||||
color: #cccccc;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
/* ShellMountOperation Dialogs */
|
||||
@ -2198,9 +2275,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.framed-user-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 5px;
|
||||
width: 48pt;
|
||||
height: 48pt;
|
||||
border-radius: 3px;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
@ -2227,24 +2302,12 @@ StScrollBar StButton#vhandle:active {
|
||||
/* Reset border and background */
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
padding-bottom: 80px;
|
||||
padding-top: 80px;
|
||||
|
||||
border-radius: 16px;
|
||||
min-height: 150px;
|
||||
max-height: 700px;
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.login-dialog-button-box {
|
||||
spacing: 5px;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-login-hint-message {
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-view {
|
||||
-st-vfade-offset: 1em;
|
||||
}
|
||||
@ -2252,10 +2315,11 @@ StScrollBar StButton#vhandle:active {
|
||||
.login-dialog-user-list {
|
||||
spacing: 12px;
|
||||
padding: .2em;
|
||||
width: 23em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item {
|
||||
border-radius: 10px;
|
||||
border-radius: 5px;
|
||||
padding: .2em;
|
||||
}
|
||||
|
||||
@ -2268,19 +2332,20 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
||||
font-size: 20pt;
|
||||
padding-left: 1em;
|
||||
font-size: 20px;
|
||||
padding-left: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item {
|
||||
color: #666666;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item,
|
||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
|
||||
color: white;
|
||||
color: #bfbfbf;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
@ -2312,7 +2377,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.login-dialog-user-list-item-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 8px;
|
||||
border-radius: 3px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
@ -2324,6 +2389,10 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-selection-box {
|
||||
padding: 100px 0;
|
||||
}
|
||||
|
||||
.login-dialog-user-selection-box .login-dialog-not-listed-label {
|
||||
padding-left: 2px;
|
||||
}
|
||||
@ -2351,6 +2420,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.login-dialog-prompt-label {
|
||||
color: #eeeeee;
|
||||
font-size: 14px;
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button StIcon {
|
||||
@ -2412,19 +2482,27 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: rgba(102, 102, 102, 0.15);
|
||||
}
|
||||
|
||||
.login-dialog-message {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 16px;
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
.login-dialog-message-warning {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.user-widget {
|
||||
spacing: .4em;
|
||||
.login-dialog-message-hint {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-widget-label {
|
||||
font-size: 16pt;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-left: 15px;
|
||||
padding-left: 18px;
|
||||
color:white;
|
||||
text-shadow: black 0px 4px 3px 0px;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
height="16"
|
||||
id="svg12430"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="more-results.svg">
|
||||
<defs
|
||||
id="defs12432" />
|
||||
@ -25,18 +25,18 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="8.3155237"
|
||||
inkscape:cy="0.89548874"
|
||||
inkscape:zoom="90.509668"
|
||||
inkscape:cx="6.5009792"
|
||||
inkscape:cy="8.3589595"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g14642-3-0"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1376"
|
||||
inkscape:window-x="1200"
|
||||
inkscape:window-y="187"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="840"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
@ -90,6 +90,11 @@
|
||||
transform="translate(-2,0)"
|
||||
width="16"
|
||||
height="16" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
|
||||
d="M 7 5 L 7 7 L 5 7 L 5 9 L 7 9 L 7 11 L 9 11 L 9 9 L 11 9 L 11 7 L 9 7 L 9 5 L 7 5 z "
|
||||
transform="translate(141.99984,397.99107)"
|
||||
id="rect3757" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB |
71
data/theme/page-indicator-active.svg
Normal file
@ -0,0 +1,71 @@
|
||||
<?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="18"
|
||||
height="18"
|
||||
id="svg4703"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="page-indicator-pushed.svg">
|
||||
<defs
|
||||
id="defs4705" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="31.392433"
|
||||
inkscape:cx="1.0245308"
|
||||
inkscape:cy="13.3715"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1374"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid6140" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4708">
|
||||
<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,2)">
|
||||
<path
|
||||
transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
|
||||
d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
|
||||
sodipodi:ry="9.5964489"
|
||||
sodipodi:rx="10.985409"
|
||||
sodipodi:cy="638.83099"
|
||||
sodipodi:cx="2088.9954"
|
||||
id="path4711"
|
||||
style="fill:#fdffff;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
67
data/theme/page-indicator-checked.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?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="18"
|
||||
height="18"
|
||||
id="svg4703"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="page-indicator-active.svg">
|
||||
<defs
|
||||
id="defs4705" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.197802"
|
||||
inkscape:cx="2.1522887"
|
||||
inkscape:cy="16.782904"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1021"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata4708">
|
||||
<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,2)">
|
||||
<path
|
||||
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
|
||||
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
|
||||
sodipodi:ry="9.5964489"
|
||||
sodipodi:rx="10.985409"
|
||||
sodipodi:cy="638.83099"
|
||||
sodipodi:cx="2088.9954"
|
||||
id="path4711"
|
||||
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
67
data/theme/page-indicator-hover.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?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="18"
|
||||
height="18"
|
||||
id="svg5266"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="page-indicator-inactive.svg">
|
||||
<defs
|
||||
id="defs5268" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.313709"
|
||||
inkscape:cx="-2.307566"
|
||||
inkscape:cy="17.859535"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1374"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5271">
|
||||
<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,2)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="path5274"
|
||||
sodipodi:cx="2088.9954"
|
||||
sodipodi:cy="638.83099"
|
||||
sodipodi:rx="10.985409"
|
||||
sodipodi:ry="9.5964489"
|
||||
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
|
||||
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
67
data/theme/page-indicator-inactive.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?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="18"
|
||||
height="18"
|
||||
id="svg5266"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="page-indicator-inactive.svg">
|
||||
<defs
|
||||
id="defs5268" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.313709"
|
||||
inkscape:cx="-2.307566"
|
||||
inkscape:cy="17.859535"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1374"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5271">
|
||||
<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,2)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
|
||||
id="path5274"
|
||||
sodipodi:cx="2088.9954"
|
||||
sodipodi:cy="638.83099"
|
||||
sodipodi:rx="10.985409"
|
||||
sodipodi:ry="9.5964489"
|
||||
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
|
||||
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
BIN
data/wanda.png
Before Width: | Height: | Size: 13 KiB |
@ -112,7 +112,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -48,7 +48,6 @@
|
||||
<xi:include href="xml/shell-global.xml"/>
|
||||
<xi:include href="xml/shell-keybinding-modes.xml"/>
|
||||
<xi:include href="xml/shell-wm.xml"/>
|
||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
||||
<xi:include href="xml/shell-util.xml"/>
|
||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||
<xi:include href="xml/shell-network-agent.xml"/>
|
||||
|
@ -21,7 +21,7 @@ nobase_dist_js_DATA = \
|
||||
gdm/batch.js \
|
||||
gdm/fingerprint.js \
|
||||
gdm/loginDialog.js \
|
||||
gdm/powerMenu.js \
|
||||
gdm/oVirt.js \
|
||||
gdm/realmd.js \
|
||||
gdm/util.js \
|
||||
extensionPrefs/main.js \
|
||||
@ -34,7 +34,9 @@ nobase_dist_js_DATA = \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
misc/modemManager.js \
|
||||
misc/objectManager.js \
|
||||
misc/params.js \
|
||||
misc/smartcardManager.js \
|
||||
misc/util.js \
|
||||
perf/core.js \
|
||||
ui/altTab.js \
|
||||
@ -54,6 +56,7 @@ nobase_dist_js_DATA = \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/environment.js \
|
||||
ui/focusCaretTracker.js\
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/grabHelper.js \
|
||||
ui/iconGrid.js \
|
||||
@ -87,22 +90,22 @@ nobase_dist_js_DATA = \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
ui/searchDisplay.js \
|
||||
ui/shellDBus.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/brightness.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/lockScreenMenu.js \
|
||||
ui/status/network.js \
|
||||
ui/status/power.js \
|
||||
ui/status/rfkill.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/status/screencast.js \
|
||||
ui/status/system.js \
|
||||
ui/switcherPopup.js \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userWidget.js \
|
||||
ui/viewSelector.js \
|
||||
ui/wanda.js \
|
||||
ui/windowAttentionHandler.js \
|
||||
ui/windowManager.js \
|
||||
ui/workspace.js \
|
||||
@ -115,7 +118,6 @@ nobase_dist_js_DATA = \
|
||||
ui/components/automountManager.js \
|
||||
ui/components/networkAgent.js \
|
||||
ui/components/polkitAgent.js \
|
||||
ui/components/recorder.js \
|
||||
ui/components/telepathyClient.js \
|
||||
ui/components/keyring.js \
|
||||
$(NULL)
|
||||
|
@ -13,13 +13,15 @@ const _ = Gettext.gettext;
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
<arg type="s" name="error"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const GnomeShellIface = '<node> \
|
||||
<interface name="org.gnome.Shell.Extensions"> \
|
||||
<signal name="ExtensionStatusChanged"> \
|
||||
<arg type="s" name="uuid"/> \
|
||||
<arg type="i" name="state"/> \
|
||||
<arg type="s" name="error"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||||
|
||||
|
@ -24,11 +24,23 @@ const AuthPromptMode = {
|
||||
UNLOCK_OR_LOG_IN: 1
|
||||
};
|
||||
|
||||
const AuthPromptStatus = {
|
||||
NOT_VERIFYING: 0,
|
||||
VERIFYING: 1,
|
||||
VERIFICATION_FAILED: 2,
|
||||
VERIFICATION_SUCCEEDED: 3
|
||||
};
|
||||
|
||||
const BeginRequestType = {
|
||||
PROVIDE_USERNAME: 0,
|
||||
DONT_PROVIDE_USERNAME: 1
|
||||
};
|
||||
|
||||
const AuthPrompt = new Lang.Class({
|
||||
Name: 'AuthPrompt',
|
||||
|
||||
_init: function(gdmClient, mode) {
|
||||
this.verifyingUser = false;
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
|
||||
this._gdmClient = gdmClient;
|
||||
this._mode = mode;
|
||||
@ -46,8 +58,9 @@ const AuthPrompt = new Lang.Class({
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint));
|
||||
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
|
||||
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
|
||||
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||
|
||||
this.connect('next', Lang.bind(this, function() {
|
||||
this.updateSensitivity(false);
|
||||
@ -95,12 +108,10 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
this._entry.grab_key_focus();
|
||||
|
||||
this._message = new St.Label({ opacity: 0 });
|
||||
this._message = new St.Label({ opacity: 0,
|
||||
styleClass: 'login-dialog-message' });
|
||||
this._message.clutter_text.line_wrap = true;
|
||||
this.actor.add(this._message, { x_fill: true });
|
||||
|
||||
this._loginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
||||
this.actor.add(this._loginHint);
|
||||
this.actor.add(this._message, { x_fill: true, y_align: St.Align.START });
|
||||
|
||||
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
|
||||
vertical: false });
|
||||
@ -109,7 +120,7 @@ const AuthPrompt = new Lang.Class({
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
|
||||
this._defaultButtonWell = new St.Widget();
|
||||
this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this._defaultButtonWellActor = null;
|
||||
|
||||
this._initButtons();
|
||||
@ -123,6 +134,8 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
_onDestroy: function() {
|
||||
this._userVerifier.clear();
|
||||
this._userVerifier.disconnectAll();
|
||||
this._userVerifier = null;
|
||||
},
|
||||
|
||||
_initButtons: function() {
|
||||
@ -181,7 +194,8 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
if (this._preemptiveAnswer) {
|
||||
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||
if (this._queryingService)
|
||||
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||
this._preemptiveAnswer = null;
|
||||
return;
|
||||
}
|
||||
@ -193,11 +207,6 @@ const AuthPrompt = new Lang.Class({
|
||||
this.setPasswordChar(passwordChar);
|
||||
this.setQuestion(question);
|
||||
|
||||
if (this.verifyingUser)
|
||||
this.cancelButton.show();
|
||||
else
|
||||
this.cancelButton.hide();
|
||||
|
||||
if (passwordChar) {
|
||||
if (this._userVerifier.reauthenticating)
|
||||
this.nextButton.label = _("Unlock");
|
||||
@ -211,41 +220,57 @@ const AuthPrompt = new Lang.Class({
|
||||
this.emit('prompted');
|
||||
},
|
||||
|
||||
_onShowMessage: function(userVerifier, message, styleClass) {
|
||||
this.setMessage(message, styleClass);
|
||||
_onOVirtUserAuthenticated: function() {
|
||||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onSmartcardStatusChanged: function() {
|
||||
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||
|
||||
// Most of the time we want to reset if the user inserts or removes
|
||||
// a smartcard. Smartcard insertion "preempts" what the user was
|
||||
// doing, and smartcard removal aborts the preemption.
|
||||
// The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
|
||||
// with a smartcard
|
||||
// 2) Don't reset if we've already succeeded at verification and
|
||||
// the user is getting logged in.
|
||||
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
|
||||
this.verificationStatus == AuthPromptStatus.VERIFYING &&
|
||||
this.smartcardDetected)
|
||||
return;
|
||||
|
||||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onShowMessage: function(userVerifier, message, type) {
|
||||
this.setMessage(message, type);
|
||||
this.emit('prompted');
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._queryingService = null;
|
||||
this.clear();
|
||||
|
||||
this.updateSensitivity(true);
|
||||
this.setActorInDefaultButtonWell(null);
|
||||
this.userVerified = false;
|
||||
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this.userVerified = true;
|
||||
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
if (!this.userVerified)
|
||||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onShowLoginHint: function(verifier, message) {
|
||||
this.setHint(message);
|
||||
},
|
||||
|
||||
_onHideLoginHint: function() {
|
||||
this.setHint(null);
|
||||
}
|
||||
},
|
||||
|
||||
addActorToDefaultButtonWell: function(actor) {
|
||||
this._defaultButtonWell.add_child(actor);
|
||||
|
||||
actor.add_constraint(new Clutter.AlignConstraint({ source: this._spinner.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
},
|
||||
|
||||
setActorInDefaultButtonWell: function(actor, animate) {
|
||||
@ -325,14 +350,18 @@ const AuthPrompt = new Lang.Class({
|
||||
this._label.show();
|
||||
this._entry.show();
|
||||
|
||||
this._loginHint.opacity = 0;
|
||||
this._loginHint.show();
|
||||
|
||||
this._entry.grab_key_focus();
|
||||
},
|
||||
|
||||
getAnswer: function() {
|
||||
let text = this._entry.get_text();
|
||||
let text;
|
||||
|
||||
if (this._preemptiveAnswer) {
|
||||
text = this._preemptiveAnswer;
|
||||
this._preemptiveAnswer = null;
|
||||
} else {
|
||||
text = this._entry.get_text();
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
@ -348,11 +377,20 @@ const AuthPrompt = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
setMessage: function(message, styleClass) {
|
||||
setMessage: function(message, type) {
|
||||
if (type == GdmUtil.MessageType.ERROR)
|
||||
this._message.add_style_class_name('login-dialog-message-warning');
|
||||
else
|
||||
this._message.remove_style_class_name('login-dialog-message-warning');
|
||||
|
||||
if (type == GdmUtil.MessageType.HINT)
|
||||
this._message.add_style_class_name('login-dialog-message-hint');
|
||||
else
|
||||
this._message.remove_style_class_name('login-dialog-message-hint');
|
||||
|
||||
if (message) {
|
||||
Tweener.removeTweens(this._message);
|
||||
this._message.text = message;
|
||||
this._message.styleClass = styleClass;
|
||||
this._message.opacity = 255;
|
||||
} else {
|
||||
this._message.opacity = 0;
|
||||
@ -373,7 +411,7 @@ const AuthPrompt = new Lang.Class({
|
||||
hide: function() {
|
||||
this.setActorInDefaultButtonWell(null, true);
|
||||
this.actor.hide();
|
||||
this._loginHint.opacity = 0;
|
||||
this._message.opacity = 0;
|
||||
|
||||
this.setUser(null);
|
||||
|
||||
@ -390,27 +428,40 @@ const AuthPrompt = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
setHint: function(message) {
|
||||
if (message) {
|
||||
this._loginHint.set_text(message)
|
||||
this._loginHint.opacity = 255;
|
||||
} else {
|
||||
this._loginHint.opacity = 0;
|
||||
this._loginHint.set_text('');
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.verifyingUser = false;
|
||||
this.userVerified = false;
|
||||
let oldStatus = this.verificationStatus;
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
|
||||
if (oldStatus == AuthPromptStatus.VERIFYING)
|
||||
this._userVerifier.cancel();
|
||||
|
||||
this._queryingService = null;
|
||||
this.clear();
|
||||
this._message.opacity = 0;
|
||||
this.setUser(null);
|
||||
this.stopSpinning();
|
||||
this.setHint(null);
|
||||
|
||||
this.emit('reset');
|
||||
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
|
||||
this.emit('failed');
|
||||
|
||||
let beginRequestType;
|
||||
|
||||
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||||
// The user is constant at the unlock screen, so it will immediately
|
||||
// respond to the request with the username
|
||||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
|
||||
(this.smartcardDetected &&
|
||||
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
|
||||
// We don't need to know the username if the user preempted the login screen
|
||||
// with a smartcard or with preauthenticated oVirt credentials
|
||||
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
|
||||
} else {
|
||||
// In all other cases, we should get the username up front.
|
||||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||
}
|
||||
|
||||
this.emit('reset', beginRequestType);
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
@ -432,7 +483,7 @@ const AuthPrompt = new Lang.Class({
|
||||
hold = new Batch.Hold();
|
||||
|
||||
this._userVerifier.begin(params.userName, hold);
|
||||
this.verifyingUser = true;
|
||||
this.verificationStatus = AuthPromptStatus.VERIFYING;
|
||||
},
|
||||
|
||||
finish: function(onComplete) {
|
||||
@ -449,10 +500,8 @@ const AuthPrompt = new Lang.Class({
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this.verifyingUser)
|
||||
this._userVerifier.cancel();
|
||||
|
||||
this.reset();
|
||||
this.emit('cancelled');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AuthPrompt.prototype);
|
||||
|
@ -5,11 +5,13 @@ const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
|
||||
<method name='GetDefaultDevice'>
|
||||
<arg type='o' direction='out' />
|
||||
</method>
|
||||
</interface>;
|
||||
const FprintManagerIface = '<node> \
|
||||
<interface name="net.reactivated.Fprint.Manager"> \
|
||||
<method name="GetDefaultDevice"> \
|
||||
<arg type="o" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
|
||||
|
||||
|
@ -102,11 +102,6 @@ const UserListItem = new Lang.Class({
|
||||
|
||||
syncStyleClasses: function() {
|
||||
this._updateLoggedIn();
|
||||
|
||||
if (global.stage.get_key_focus() == this.actor)
|
||||
this.actor.add_style_pseudo_class('focus');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('focus');
|
||||
},
|
||||
|
||||
_updateLoggedIn: function() {
|
||||
@ -314,10 +309,6 @@ const SessionMenuButton = new Lang.Class({
|
||||
this._button.remove_style_pseudo_class('active');
|
||||
}));
|
||||
|
||||
let subtitle = new PopupMenu.PopupMenuItem(_("Session"), { style_class: 'popup-subtitle-menu-item',
|
||||
reactive: false });
|
||||
this._menu.addMenuItem(subtitle);
|
||||
|
||||
this._manager = new PopupMenu.PopupMenuManager({ actor: this._button });
|
||||
this._manager.addMenu(this._menu);
|
||||
|
||||
@ -393,6 +384,7 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
_init: function(parentActor) {
|
||||
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
style_class: 'login-dialog',
|
||||
visible: false });
|
||||
|
||||
@ -431,11 +423,12 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, this._updateLogoTexture));
|
||||
|
||||
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
vertical: true,
|
||||
visible: false });
|
||||
this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
this.actor.add_child(this._userSelectionBox);
|
||||
|
||||
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||
@ -451,16 +444,9 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
|
||||
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._reset));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._authPrompt.hide();
|
||||
|
||||
this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
|
||||
this.actor.add_child(this._authPrompt.actor);
|
||||
this._userList.actor.add_constraint(new Clutter.BindConstraint({ source: this._authPrompt.actor,
|
||||
coordinate: Clutter.BindCoordinate.WIDTH }));
|
||||
|
||||
// translators: this message is shown below the user list on the
|
||||
// login screen. It can be activated to reveal an entry for
|
||||
@ -475,7 +461,8 @@ const LoginDialog = new Lang.Class({
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
|
||||
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAskForUsernameAndBeginVerification));
|
||||
|
||||
this._notListedButton.hide();
|
||||
|
||||
this._userSelectionBox.add(this._notListedButton,
|
||||
@ -483,14 +470,11 @@ const LoginDialog = new Lang.Class({
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
|
||||
this._logoBin.set_y_align(Clutter.ActorAlign.END);
|
||||
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.X_AXIS,
|
||||
factor: 0.5 }));
|
||||
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.Y_AXIS,
|
||||
factor: 1.0 }));
|
||||
this._logoBin = new St.Widget({ style_class: 'login-dialog-logo-bin',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
this.actor.add_child(this._logoBin);
|
||||
this._updateLogo();
|
||||
|
||||
@ -529,11 +513,24 @@ const LoginDialog = new Lang.Class({
|
||||
if (disableUserList != this._disableUserList) {
|
||||
this._disableUserList = disableUserList;
|
||||
|
||||
if (!this._authPrompt.verifyingUser)
|
||||
this._reset();
|
||||
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
|
||||
this._authPrompt.reset();
|
||||
}
|
||||
},
|
||||
|
||||
_updateCancelButton: function() {
|
||||
let cancelVisible;
|
||||
|
||||
// Hide the cancel button if the user list is disabled and we're asking for
|
||||
// a username
|
||||
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING && this._disableUserList)
|
||||
cancelVisible = false;
|
||||
else
|
||||
cancelVisible = true;
|
||||
|
||||
this._authPrompt.cancelButton.visible = cancelVisible;
|
||||
},
|
||||
|
||||
_updateBanner: function() {
|
||||
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
|
||||
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
|
||||
@ -550,11 +547,10 @@ const LoginDialog = new Lang.Class({
|
||||
if (this._logoFileUri != uri)
|
||||
return;
|
||||
|
||||
let icon = null;
|
||||
this._logoBin.destroy_all_children();
|
||||
if (this._logoFileUri)
|
||||
icon = this._textureCache.load_uri_async(this._logoFileUri,
|
||||
-1, _LOGO_ICON_HEIGHT);
|
||||
this._logoBin.set_child(icon);
|
||||
this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri,
|
||||
-1, _LOGO_ICON_HEIGHT));
|
||||
},
|
||||
|
||||
_updateLogo: function() {
|
||||
@ -569,24 +565,22 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
if (this._shouldShowSessionMenuButton())
|
||||
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
|
||||
|
||||
this._authPrompt.cancelButton.show();
|
||||
|
||||
this._showPrompt();
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
if (this._authPrompt.verifyingUser)
|
||||
return;
|
||||
|
||||
_onReset: function(authPrompt, beginRequest) {
|
||||
this._sessionMenuButton.updateSensitivity(true);
|
||||
|
||||
this._user = null;
|
||||
|
||||
if (this._disableUserList)
|
||||
this._hideUserListAndLogIn();
|
||||
else
|
||||
this._showUserList();
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
if (!this._disableUserList)
|
||||
this._showUserList();
|
||||
else
|
||||
this._hideUserListAskForUsernameAndBeginVerification();
|
||||
} else {
|
||||
this._hideUserListAndBeginVerification();
|
||||
}
|
||||
},
|
||||
|
||||
_onDefaultSessionChanged: function(client, sessionId) {
|
||||
@ -594,13 +588,11 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_shouldShowSessionMenuButton: function() {
|
||||
if (this._authPrompt.verifyingUser)
|
||||
return true;
|
||||
|
||||
if (!this._user)
|
||||
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFYING &&
|
||||
this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_FAILED)
|
||||
return false;
|
||||
|
||||
if (this._user.is_logged_in)
|
||||
if (this._user && this._user.is_loaded && this._user.is_logged_in())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -627,10 +619,10 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
// Translators: this message is shown below the username entry field
|
||||
// to clue the user in on how to login to the local network realm
|
||||
this._authPrompt.setHint(_("(e.g., user or %s)").format(hint));
|
||||
this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), GdmUtil.MessageType.HINT);
|
||||
},
|
||||
|
||||
_askForUsernameAndLogIn: function() {
|
||||
_askForUsernameAndBeginVerification: function() {
|
||||
this._authPrompt.setPasswordChar('');
|
||||
this._authPrompt.setQuestion(_("Username: "));
|
||||
|
||||
@ -644,14 +636,16 @@ const LoginDialog = new Lang.Class({
|
||||
this._authPrompt.disconnect(nextSignalId);
|
||||
this._authPrompt.updateSensitivity(false);
|
||||
let answer = this._authPrompt.getAnswer();
|
||||
this._user = this._userManager.get_user(answer);
|
||||
this._authPrompt.clear();
|
||||
this._authPrompt.startSpinning();
|
||||
this._authPrompt.begin({ userName: answer });
|
||||
this._updateCancelButton();
|
||||
|
||||
realmManager.disconnect(realmSignalId)
|
||||
realmManager.release();
|
||||
}));
|
||||
this._authPrompt.cancelButton.hide();
|
||||
this._updateCancelButton();
|
||||
this._showPrompt();
|
||||
},
|
||||
|
||||
@ -813,11 +807,20 @@ const LoginDialog = new Lang.Class({
|
||||
this._userSelectionBox.visible = expanded;
|
||||
},
|
||||
|
||||
_hideUserListAndLogIn: function() {
|
||||
_hideUserList: function() {
|
||||
this._setUserListExpanded(false);
|
||||
if (this._userSelectionBox.visible)
|
||||
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
this._askForUsernameAndLogIn();
|
||||
},
|
||||
|
||||
_hideUserListAskForUsernameAndBeginVerification: function() {
|
||||
this._hideUserList();
|
||||
this._askForUsernameAndBeginVerification();
|
||||
},
|
||||
|
||||
_hideUserListAndBeginVerification: function() {
|
||||
this._hideUserList();
|
||||
this._authPrompt.begin();
|
||||
},
|
||||
|
||||
_showUserList: function() {
|
||||
@ -849,6 +852,8 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
this._user = activatedItem.user;
|
||||
|
||||
this._updateCancelButton();
|
||||
|
||||
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
|
||||
this._beginVerificationForItem(activatedItem)]);
|
||||
batch.run();
|
||||
@ -888,6 +893,12 @@ const LoginDialog = new Lang.Class({
|
||||
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
|
||||
this._userList.actor.grab_key_focus();
|
||||
this.actor.show();
|
||||
this.actor.opacity = 0;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: 1,
|
||||
transition: 'easeInQuad' });
|
||||
|
||||
return true;
|
||||
},
|
||||
@ -899,5 +910,9 @@ const LoginDialog = new Lang.Class({
|
||||
addCharacter: function(unichar) {
|
||||
this._authPrompt.addCharacter(unichar);
|
||||
},
|
||||
|
||||
finish: function(onComplete) {
|
||||
this._authPrompt.finish(onComplete);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(LoginDialog.prototype);
|
||||
|
64
js/gdm/oVirt.js
Normal file
@ -0,0 +1,64 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const OVirtCredentialsIface = '<node> \
|
||||
<interface name="org.ovirt.vdsm.Credentials"> \
|
||||
<signal name="UserAuthenticated"> \
|
||||
<arg type="s" name="token"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
|
||||
|
||||
let _oVirtCredentialsManager = null;
|
||||
|
||||
function OVirtCredentials() {
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
||||
g_interface_name: OVirtCredentialsInfo.name,
|
||||
g_interface_info: OVirtCredentialsInfo,
|
||||
g_name: 'org.ovirt.vdsm.Credentials',
|
||||
g_object_path: '/org/ovirt/vdsm/Credentials',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
||||
const OVirtCredentialsManager = new Lang.Class({
|
||||
Name: 'OVirtCredentialsManager',
|
||||
_init: function() {
|
||||
this._token = null;
|
||||
|
||||
this._credentials = new OVirtCredentials();
|
||||
this._credentials.connectSignal('UserAuthenticated',
|
||||
Lang.bind(this, this._onUserAuthenticated));
|
||||
},
|
||||
|
||||
_onUserAuthenticated: function(proxy, sender, [token]) {
|
||||
this._token = token;
|
||||
this.emit('user-authenticated', token);
|
||||
},
|
||||
|
||||
hasToken: function() {
|
||||
return this._token != null;
|
||||
},
|
||||
|
||||
getToken: function() {
|
||||
return this._token;
|
||||
},
|
||||
|
||||
resetToken: function() {
|
||||
this._token = null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
|
||||
|
||||
function getOVirtCredentialsManager() {
|
||||
if (!_oVirtCredentialsManager)
|
||||
_oVirtCredentialsManager = new OVirtCredentialsManager();
|
||||
|
||||
return _oVirtCredentialsManager;
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 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 Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const PowerMenuButton = new Lang.Class({
|
||||
Name: 'PowerMenuButton',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
/* Translators: accessible name of the power menu in the login screen */
|
||||
this.parent('system-shutdown-symbolic', _("Power"));
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed::disable-restart-buttons',
|
||||
Lang.bind(this, this._updateVisibility));
|
||||
|
||||
this._createSubMenu();
|
||||
|
||||
// ConsoleKit doesn't send notifications when shutdown/reboot
|
||||
// are disabled, so we update the menu item each time the menu opens
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
function(menu, open) {
|
||||
if (open) {
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
}
|
||||
}));
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
|
||||
this._haveShutdown = result;
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveRestart: function() {
|
||||
this._loginManager.canReboot(Lang.bind(this, function(result) {
|
||||
this._haveRestart = result;
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Suspend"));
|
||||
item.connect('activate', Lang.bind(this, this._onActivateSuspend));
|
||||
this.menu.addMenuItem(item);
|
||||
this._suspendItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Restart"));
|
||||
item.connect('activate', Lang.bind(this, this._onActivateRestart));
|
||||
this.menu.addMenuItem(item);
|
||||
this._restartItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Power Off"));
|
||||
item.connect('activate', Lang.bind(this, this._onActivatePowerOff));
|
||||
this.menu.addMenuItem(item);
|
||||
this._powerOffItem = item;
|
||||
},
|
||||
|
||||
_onActivateSuspend: function() {
|
||||
if (!this._haveSuspend)
|
||||
return;
|
||||
|
||||
this._loginManager.suspend();
|
||||
},
|
||||
|
||||
_onActivateRestart: function() {
|
||||
if (!this._haveRestart)
|
||||
return;
|
||||
|
||||
this._loginManager.reboot();
|
||||
},
|
||||
|
||||
_onActivatePowerOff: function() {
|
||||
if (!this._haveShutdown)
|
||||
return;
|
||||
|
||||
this._loginManager.powerOff();
|
||||
}
|
||||
});
|
@ -5,52 +5,58 @@ const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
|
||||
<property name="Name" type="s" access="read"/>
|
||||
<property name="Version" type="s" access="read"/>
|
||||
<property name="Realms" type="ao" access="read"/>
|
||||
<method name="Discover">
|
||||
<arg name="string" type="s" direction="in"/>
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
<arg name="relevance" type="i" direction="out"/>
|
||||
<arg name="realm" type="ao" direction="out"/>
|
||||
</method>
|
||||
</interface>;
|
||||
const ProviderIface = '<node> \
|
||||
<interface name="org.freedesktop.realmd.Provider"> \
|
||||
<property name="Name" type="s" access="read"/> \
|
||||
<property name="Version" type="s" access="read"/> \
|
||||
<property name="Realms" type="ao" access="read"/> \
|
||||
<method name="Discover"> \
|
||||
<arg name="string" type="s" direction="in"/> \
|
||||
<arg name="options" type="a{sv}" direction="in"/> \
|
||||
<arg name="relevance" type="i" direction="out"/> \
|
||||
<arg name="realm" type="ao" direction="out"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
|
||||
|
||||
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
|
||||
<method name="Cancel">
|
||||
<arg name="operation" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="Release" />
|
||||
<method name="SetLocale">
|
||||
<arg name="locale" type="s" direction="in"/>
|
||||
</method>
|
||||
<signal name="Diagnostics">
|
||||
<arg name="data" type="s"/>
|
||||
<arg name="operation" type="s"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const ServiceIface = '<node> \
|
||||
<interface name="org.freedesktop.realmd.Service"> \
|
||||
<method name="Cancel"> \
|
||||
<arg name="operation" type="s" direction="in"/> \
|
||||
</method> \
|
||||
<method name="Release" /> \
|
||||
<method name="SetLocale"> \
|
||||
<arg name="locale" type="s" direction="in"/> \
|
||||
</method> \
|
||||
<signal name="Diagnostics"> \
|
||||
<arg name="data" type="s"/> \
|
||||
<arg name="operation" type="s"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
|
||||
|
||||
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
|
||||
<property name="Name" type="s" access="read"/>
|
||||
<property name="Configured" type="s" access="read"/>
|
||||
<property name="Details" type="a(ss)" access="read"/>
|
||||
<property name="LoginFormats" type="as" access="read"/>
|
||||
<property name="LoginPolicy" type="s" access="read"/>
|
||||
<property name="PermittedLogins" type="as" access="read"/>
|
||||
<property name="SupportedInterfaces" type="as" access="read"/>
|
||||
<method name="ChangeLoginPolicy">
|
||||
<arg name="login_policy" type="s" direction="in"/>
|
||||
<arg name="permitted_add" type="as" direction="in"/>
|
||||
<arg name="permitted_remove" type="as" direction="in"/>
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
</method>
|
||||
<method name="Deconfigure">
|
||||
<arg name="options" type="a{sv}" direction="in"/>
|
||||
</method>
|
||||
</interface>;
|
||||
const RealmIface = '<node> \
|
||||
<interface name="org.freedesktop.realmd.Realm"> \
|
||||
<property name="Name" type="s" access="read"/> \
|
||||
<property name="Configured" type="s" access="read"/> \
|
||||
<property name="Details" type="a(ss)" access="read"/> \
|
||||
<property name="LoginFormats" type="as" access="read"/> \
|
||||
<property name="LoginPolicy" type="s" access="read"/> \
|
||||
<property name="PermittedLogins" type="as" access="read"/> \
|
||||
<property name="SupportedInterfaces" type="as" access="read"/> \
|
||||
<method name="ChangeLoginPolicy"> \
|
||||
<arg name="login_policy" type="s" direction="in"/> \
|
||||
<arg name="permitted_add" type="as" direction="in"/> \
|
||||
<arg name="permitted_remove" type="as" direction="in"/> \
|
||||
<arg name="options" type="a{sv}" direction="in"/> \
|
||||
</method> \
|
||||
<method name="Deconfigure"> \
|
||||
<arg name="options" type="a{sv}" direction="in"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
|
||||
|
||||
const Manager = new Lang.Class({
|
||||
|
240
js/gdm/util.js
@ -10,18 +10,24 @@ const St = imports.gi.St;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const Fprint = imports.gdm.fingerprint;
|
||||
const OVirt = imports.gdm.oVirt;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const SmartcardManager = imports.misc.smartcardManager;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
|
||||
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
|
||||
const FADE_ANIMATION_TIME = 0.16;
|
||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
||||
|
||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
|
||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
|
||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||
@ -32,6 +38,13 @@ const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
||||
// Give user 16ms to read each character of a PAM message
|
||||
const USER_READ_TIME = 16
|
||||
|
||||
const MessageType = {
|
||||
NONE: 0,
|
||||
ERROR: 1,
|
||||
INFO: 2,
|
||||
HINT: 3
|
||||
};
|
||||
|
||||
function fadeInActor(actor) {
|
||||
if (actor.opacity == 255 && actor.visible)
|
||||
return null;
|
||||
@ -116,14 +129,38 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._client = client;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed',
|
||||
Lang.bind(this, this._updateDefaultService));
|
||||
this._updateDefaultService();
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||
|
||||
// We check for smartcards right away, since an inserted smartcard
|
||||
// at startup should result in immediately initiating authentication.
|
||||
// This is different than fingeprint readers, where we only check them
|
||||
// after a user has been picked.
|
||||
this._checkForSmartcard();
|
||||
|
||||
this._smartcardManager.connect('smartcard-inserted',
|
||||
Lang.bind(this, this._checkForSmartcard));
|
||||
this._smartcardManager.connect('smartcard-removed',
|
||||
Lang.bind(this, this._checkForSmartcard));
|
||||
|
||||
this._messageQueue = [];
|
||||
this._messageQueueTimeoutId = 0;
|
||||
this.hasPendingMessages = false;
|
||||
this.reauthenticating = false;
|
||||
|
||||
this._failCounter = 0;
|
||||
|
||||
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
|
||||
|
||||
if (this._oVirtCredentialsManager.hasToken())
|
||||
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
|
||||
|
||||
this._oVirtCredentialsManager.connect('user-authenticated',
|
||||
Lang.bind(this, this._oVirtUserAuthenticated));
|
||||
},
|
||||
|
||||
begin: function(userName, hold) {
|
||||
@ -148,8 +185,10 @@ const ShellUserVerifier = new Lang.Class({
|
||||
if (this._cancellable)
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._userVerifier)
|
||||
if (this._userVerifier) {
|
||||
this._userVerifier.call_cancel_sync(null);
|
||||
this.clear();
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
@ -203,7 +242,8 @@ const ShellUserVerifier = new Lang.Class({
|
||||
return;
|
||||
|
||||
let message = this._messageQueue.shift();
|
||||
this.emit('show-message', message.text, message.iconName);
|
||||
|
||||
this.emit('show-message', message.text, message.type);
|
||||
|
||||
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
message.interval,
|
||||
@ -213,11 +253,11 @@ const ShellUserVerifier = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_queueMessage: function(message, iconName) {
|
||||
_queueMessage: function(message, messageType) {
|
||||
let interval = this._getIntervalForMessage(message);
|
||||
|
||||
this.hasPendingMessages = true;
|
||||
this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
|
||||
this._messageQueue.push({ text: message, type: messageType, interval: interval });
|
||||
this._queueMessageTimeout();
|
||||
},
|
||||
|
||||
@ -228,27 +268,57 @@ const ShellUserVerifier = new Lang.Class({
|
||||
GLib.source_remove(this._messageQueueTimeoutId);
|
||||
this._messageQueueTimeoutId = 0;
|
||||
}
|
||||
this.emit('show-message', null, null);
|
||||
this.emit('show-message', null, MessageType.NONE);
|
||||
},
|
||||
|
||||
_checkForFingerprintReader: function() {
|
||||
this._haveFingerprintReader = false;
|
||||
|
||||
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
||||
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) {
|
||||
this._updateDefaultService();
|
||||
return;
|
||||
}
|
||||
|
||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
||||
function(device, error) {
|
||||
if (!error && device)
|
||||
this._haveFingerprintReader = true;
|
||||
this._updateDefaultService();
|
||||
}));
|
||||
},
|
||||
|
||||
_oVirtUserAuthenticated: function(token) {
|
||||
this._preemptingService = OVIRT_SERVICE_NAME;
|
||||
this.emit('ovirt-user-authenticated');
|
||||
},
|
||||
|
||||
_checkForSmartcard: function() {
|
||||
let smartcardDetected;
|
||||
|
||||
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||
smartcardDetected = false;
|
||||
else if (this.reauthenticating)
|
||||
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
|
||||
else
|
||||
smartcardDetected = this._smartcardManager.hasInsertedTokens();
|
||||
|
||||
if (smartcardDetected != this.smartcardDetected) {
|
||||
this.smartcardDetected = smartcardDetected;
|
||||
|
||||
if (this.smartcardDetected)
|
||||
this._preemptingService = SMARTCARD_SERVICE_NAME;
|
||||
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
|
||||
this._preemptingService = null;
|
||||
|
||||
this.emit('smartcard-status-changed');
|
||||
}
|
||||
},
|
||||
|
||||
_reportInitError: function(where, error) {
|
||||
logError(error, where);
|
||||
this._hold.release();
|
||||
|
||||
this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
|
||||
this._queueMessage(_("Authentication error"), MessageType.ERROR);
|
||||
this._verificationFailed(false);
|
||||
},
|
||||
|
||||
@ -300,105 +370,119 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
},
|
||||
|
||||
_beginVerification: function() {
|
||||
_getForegroundService: function() {
|
||||
if (this._preemptingService)
|
||||
return this._preemptingService;
|
||||
|
||||
return this._defaultService;
|
||||
},
|
||||
|
||||
serviceIsForeground: function(serviceName) {
|
||||
return serviceName == this._getForegroundService();
|
||||
},
|
||||
|
||||
serviceIsDefault: function(serviceName) {
|
||||
return serviceName == this._defaultService;
|
||||
},
|
||||
|
||||
_updateDefaultService: function() {
|
||||
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
|
||||
this._defaultService = PASSWORD_SERVICE_NAME;
|
||||
else if (this.smartcardDetected)
|
||||
this._defaultService = SMARTCARD_SERVICE_NAME;
|
||||
else if (this._haveFingerprintReader)
|
||||
this._defaultService = FINGERPRINT_SERVICE_NAME;
|
||||
},
|
||||
|
||||
_startService: function(serviceName) {
|
||||
this._hold.acquire();
|
||||
|
||||
if (this._userName) {
|
||||
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification for user', e);
|
||||
return;
|
||||
}
|
||||
this._userVerifier.call_begin_verification_for_user(serviceName,
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification for user', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
}));
|
||||
|
||||
if (this._haveFingerprintReader) {
|
||||
this._hold.acquire();
|
||||
|
||||
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start fingerprint verification for user', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
}));
|
||||
}
|
||||
this._hold.release();
|
||||
}));
|
||||
} else {
|
||||
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification', e);
|
||||
return;
|
||||
}
|
||||
this._userVerifier.call_begin_verification(serviceName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
try {
|
||||
obj.call_begin_verification_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
}));
|
||||
this._hold.release();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_beginVerification: function() {
|
||||
this._startService(this._getForegroundService());
|
||||
|
||||
if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
|
||||
this._startService(FINGERPRINT_SERVICE_NAME);
|
||||
},
|
||||
|
||||
_onInfo: function(client, serviceName, info) {
|
||||
// We don't display fingerprint messages, because they
|
||||
// have words like UPEK in them. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||
if (this.serviceIsForeground(serviceName)) {
|
||||
this._queueMessage(info, MessageType.INFO);
|
||||
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||
this._haveFingerprintReader) {
|
||||
// We don't show fingerprint messages directly since it's
|
||||
// not the main auth service. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
|
||||
// Translators: this message is shown below the password entry field
|
||||
// to indicate the user can swipe their finger instead
|
||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this._queueMessage(info, 'login-dialog-message-info');
|
||||
this._queueMessage(_("(or swipe finger)"), MessageType.HINT);
|
||||
}
|
||||
},
|
||||
|
||||
_onProblem: function(client, serviceName, problem) {
|
||||
// we don't want to show auth failed messages to
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
if (!this.serviceIsForeground(serviceName))
|
||||
return;
|
||||
this._queueMessage(problem, 'login-dialog-message-warning');
|
||||
|
||||
this._queueMessage(problem, MessageType.ERROR);
|
||||
},
|
||||
|
||||
_onInfoQuery: function(client, serviceName, question) {
|
||||
// We only expect questions to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
if (!this.serviceIsForeground(serviceName))
|
||||
return;
|
||||
|
||||
this.emit('ask-question', serviceName, question, '');
|
||||
},
|
||||
|
||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||
// We only expect secret requests to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
if (!this.serviceIsForeground(serviceName))
|
||||
return;
|
||||
|
||||
if (serviceName == OVIRT_SERVICE_NAME) {
|
||||
// The only question asked by this service is "Token?"
|
||||
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
// Clear previous attempts to authenticate
|
||||
this._failCounter = 0;
|
||||
this._updateDefaultService();
|
||||
|
||||
this.emit('reset');
|
||||
},
|
||||
@ -452,14 +536,22 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onConversationStopped: function(client, serviceName) {
|
||||
// If the login failed with the preauthenticated oVirt credentials
|
||||
// then discard the credentials and revert to default authentication
|
||||
// mechanism.
|
||||
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
|
||||
this._oVirtCredentialsManager.resetToken();
|
||||
this._preemptingService = null;
|
||||
this._verificationFailed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// if the password service fails, then cancel everything.
|
||||
// But if, e.g., fingerprint fails, still give
|
||||
// password authentication a chance to succeed
|
||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
if (this.serviceIsForeground(serviceName)) {
|
||||
this._verificationFailed(true);
|
||||
}
|
||||
|
||||
this.emit('hide-login-hint');
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
||||
|
@ -117,7 +117,6 @@ function recursivelyMoveDir(srcDir, destDir) {
|
||||
let type = info.get_file_type();
|
||||
let srcChild = srcDir.get_child(info.get_name());
|
||||
let destChild = destDir.get_child(info.get_name());
|
||||
log([srcChild.get_path(), destChild.get_path()]);
|
||||
if (type == Gio.FileType.REGULAR)
|
||||
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
|
||||
else if (type == Gio.FileType.DIRECTORY)
|
||||
|
@ -4,15 +4,17 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
|
||||
<method name="SetStatus">
|
||||
<arg type="u" direction="in"/>
|
||||
</method>
|
||||
<property name="status" type="u" access="readwrite"/>
|
||||
<signal name="StatusChanged">
|
||||
<arg type="u" direction="out"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const PresenceIface = '<node> \
|
||||
<interface name="org.gnome.SessionManager.Presence"> \
|
||||
<method name="SetStatus"> \
|
||||
<arg type="u" direction="in"/> \
|
||||
</method> \
|
||||
<property name="status" type="u" access="readwrite"/> \
|
||||
<signal name="StatusChanged"> \
|
||||
<arg type="u" direction="out"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PresenceStatus = {
|
||||
AVAILABLE: 0,
|
||||
@ -30,14 +32,16 @@ function Presence(initCallback, cancellable) {
|
||||
// Note inhibitors are immutable objects, so they don't
|
||||
// change at runtime (changes always come in the form
|
||||
// of new inhibitors)
|
||||
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
|
||||
<method name="GetAppId">
|
||||
<arg type="s" direction="out" />
|
||||
</method>
|
||||
<method name="GetReason">
|
||||
<arg type="s" direction="out" />
|
||||
</method>
|
||||
</interface>;
|
||||
const InhibitorIface = '<node> \
|
||||
<interface name="org.gnome.SessionManager.Inhibitor"> \
|
||||
<method name="GetAppId"> \
|
||||
<arg type="s" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetReason"> \
|
||||
<arg type="s" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
|
||||
function Inhibitor(objectPath, initCallback, cancellable) {
|
||||
@ -45,27 +49,29 @@ function Inhibitor(objectPath, initCallback, cancellable) {
|
||||
}
|
||||
|
||||
// Not the full interface, only the methods we use
|
||||
const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
||||
<method name="Logout">
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
<method name="Shutdown" />
|
||||
<method name="Reboot" />
|
||||
<method name="CanShutdown">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="IsInhibited">
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<property name="SessionIsActive" type="b" access="read"/>
|
||||
<signal name="InhibitorAdded">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
<signal name="InhibitorRemoved">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const SessionManagerIface = '<node> \
|
||||
<interface name="org.gnome.SessionManager"> \
|
||||
<method name="Logout"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
<method name="Shutdown" /> \
|
||||
<method name="Reboot" /> \
|
||||
<method name="CanShutdown"> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<method name="IsInhibited"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<property name="SessionIsActive" type="b" access="read"/> \
|
||||
<signal name="InhibitorAdded"> \
|
||||
<arg type="o" direction="out"/> \
|
||||
</signal> \
|
||||
<signal name="InhibitorRemoved"> \
|
||||
<arg type="o" direction="out"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
||||
function SessionManager(initCallback, cancellable) {
|
||||
|
@ -7,70 +7,66 @@ const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||
<method name='PowerOff'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='Reboot'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='Suspend'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='CanPowerOff'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanReboot'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanSuspend'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='Inhibit'>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='h' direction='out'/>
|
||||
</method>
|
||||
<method name='GetSession'>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='o' direction='out'/>
|
||||
</method>
|
||||
<method name='ListSessions'>
|
||||
<arg name='sessions' type='a(susso)' direction='out'/>
|
||||
</method>
|
||||
<signal name='PrepareForSleep'>
|
||||
<arg type='b' direction='out'/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const SystemdLoginManagerIface = '<node> \
|
||||
<interface name="org.freedesktop.login1.Manager"> \
|
||||
<method name="Suspend"> \
|
||||
<arg type="b" direction="in"/> \
|
||||
</method> \
|
||||
<method name="CanSuspend"> \
|
||||
<arg type="s" direction="out"/> \
|
||||
</method> \
|
||||
<method name="Inhibit"> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="h" direction="out"/> \
|
||||
</method> \
|
||||
<method name="GetSession"> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="o" direction="out"/> \
|
||||
</method> \
|
||||
<method name="ListSessions"> \
|
||||
<arg name="sessions" type="a(susso)" direction="out"/> \
|
||||
</method> \
|
||||
<signal name="PrepareForSleep"> \
|
||||
<arg type="b" direction="out"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
<signal name='Lock' />
|
||||
<signal name='Unlock' />
|
||||
</interface>;
|
||||
const SystemdLoginSessionIface = '<node> \
|
||||
<interface name="org.freedesktop.login1.Session"> \
|
||||
<signal name="Lock" /> \
|
||||
<signal name="Unlock" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
|
||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
|
||||
<method name='CanRestart'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='CanStop'>
|
||||
<arg type='b' direction='out'/>
|
||||
</method>
|
||||
<method name='Restart' />
|
||||
<method name='Stop' />
|
||||
<method name='GetCurrentSession'>
|
||||
<arg type='o' direction='out' />
|
||||
</method>
|
||||
</interface>;
|
||||
const ConsoleKitManagerIface = '<node> \
|
||||
<interface name="org.freedesktop.ConsoleKit.Manager"> \
|
||||
<method name="CanRestart"> \
|
||||
<arg type="b" direction="out"/> \
|
||||
</method> \
|
||||
<method name="CanStop"> \
|
||||
<arg type="b" direction="out"/> \
|
||||
</method> \
|
||||
<method name="Restart" /> \
|
||||
<method name="Stop" /> \
|
||||
<method name="GetCurrentSession"> \
|
||||
<arg type="o" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
|
||||
<signal name='Lock' />
|
||||
<signal name='Unlock' />
|
||||
</interface>;
|
||||
const ConsoleKitSessionIface = '<node> \
|
||||
<interface name="org.freedesktop.ConsoleKit.Session"> \
|
||||
<signal name="Lock" /> \
|
||||
<signal name="Unlock" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||
@ -84,8 +80,10 @@ function versionCompare(required, reference) {
|
||||
reference = reference.split('.');
|
||||
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
if (required[i] != reference[i])
|
||||
return required[i] < reference[i];
|
||||
let requiredInt = parseInt(required[i]);
|
||||
let referenceInt = parseInt(reference[i]);
|
||||
if (requiredInt != referenceInt)
|
||||
return requiredInt < referenceInt;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -159,24 +157,6 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
this._proxy.CanPowerOffRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
});
|
||||
},
|
||||
|
||||
canReboot: function(asyncCallback) {
|
||||
this._proxy.CanRebootRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
});
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
this._proxy.CanSuspendRemote(function(result, error) {
|
||||
if (error)
|
||||
@ -195,14 +175,6 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.PowerOffRemote(true);
|
||||
},
|
||||
|
||||
reboot: function() {
|
||||
this._proxy.RebootRemote(true);
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this._proxy.SuspendRemote(true);
|
||||
},
|
||||
@ -264,24 +236,6 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
this._proxy.CanStopRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0]);
|
||||
});
|
||||
},
|
||||
|
||||
canReboot: function(asyncCallback) {
|
||||
this._proxy.CanRestartRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0]);
|
||||
});
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
asyncCallback(false);
|
||||
},
|
||||
@ -290,14 +244,6 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
asyncCallback([]);
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.StopRemote();
|
||||
},
|
||||
|
||||
reboot: function() {
|
||||
this._proxy.RestartRemote();
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this.emit('prepare-for-sleep', true);
|
||||
this.emit('prepare-for-sleep', false);
|
||||
|
@ -92,37 +92,41 @@ function _findProviderForSid(sid) {
|
||||
// The following are not the complete interfaces, just the methods we need
|
||||
// (or may need in the future)
|
||||
|
||||
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
|
||||
<method name="GetRegistrationInfo">
|
||||
<arg type="(uss)" direction="out" />
|
||||
</method>
|
||||
<method name="GetSignalQuality">
|
||||
<arg type="u" direction="out" />
|
||||
</method>
|
||||
<property name="AccessTechnology" type="u" access="read" />
|
||||
<signal name="SignalQuality">
|
||||
<arg type="u" direction="out" />
|
||||
</signal>
|
||||
<signal name="RegistrationInfo">
|
||||
<arg type="u" direction="out" />
|
||||
<arg type="s" direction="out" />
|
||||
<arg type="s" direction="out" />
|
||||
</signal>
|
||||
</interface>;
|
||||
const ModemGsmNetworkInterface = '<node> \
|
||||
<interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> \
|
||||
<method name="GetRegistrationInfo"> \
|
||||
<arg type="(uss)" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetSignalQuality"> \
|
||||
<arg type="u" direction="out" /> \
|
||||
</method> \
|
||||
<property name="AccessTechnology" type="u" access="read" /> \
|
||||
<signal name="SignalQuality"> \
|
||||
<arg type="u" direction="out" /> \
|
||||
</signal> \
|
||||
<signal name="RegistrationInfo"> \
|
||||
<arg type="u" direction="out" /> \
|
||||
<arg type="s" direction="out" /> \
|
||||
<arg type="s" direction="out" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
|
||||
|
||||
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
|
||||
<method name="GetSignalQuality">
|
||||
<arg type="u" direction="out" />
|
||||
</method>
|
||||
<method name="GetServingSystem">
|
||||
<arg type="(usu)" direction="out" />
|
||||
</method>
|
||||
<signal name="SignalQuality">
|
||||
<arg type="u" direction="out" />
|
||||
</signal>
|
||||
</interface>;
|
||||
const ModemCdmaInterface = '<node> \
|
||||
<interface name="org.freedesktop.ModemManager.Modem.Cdma"> \
|
||||
<method name="GetSignalQuality"> \
|
||||
<arg type="u" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetServingSystem"> \
|
||||
<arg type="(usu)" direction="out" /> \
|
||||
</method> \
|
||||
<signal name="SignalQuality"> \
|
||||
<arg type="u" direction="out" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
||||
|
||||
@ -218,20 +222,26 @@ Signals.addSignalMethods(ModemCdma.prototype);
|
||||
// Support for the new ModemManager1 interface (MM >= 0.7)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
|
||||
<property name="SignalQuality" type="(ub)" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModemInterface = '<node> \
|
||||
<interface name="org.freedesktop.ModemManager1.Modem"> \
|
||||
<property name="SignalQuality" type="(ub)" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
|
||||
|
||||
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
|
||||
<property name="OperatorCode" type="s" access="read" />
|
||||
<property name="OperatorName" type="s" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModem3gppInterface = '<node> \
|
||||
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> \
|
||||
<property name="OperatorCode" type="s" access="read" /> \
|
||||
<property name="OperatorName" type="s" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
|
||||
|
||||
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
|
||||
<property name="Sid" type="u" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModemCdmaInterface = '<node> \
|
||||
<interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> \
|
||||
<property name="Sid" type="u" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
|
||||
|
||||
const BroadbandModem = new Lang.Class({
|
||||
|
259
js/misc/objectManager.js
Normal file
@ -0,0 +1,259 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
const Signals = imports.signals;
|
||||
|
||||
// Specified in the D-Bus specification here:
|
||||
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
||||
const ObjectManagerIface = '<node> \
|
||||
<interface name="org.freedesktop.DBus.ObjectManager"> \
|
||||
<method name="GetManagedObjects"> \
|
||||
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
|
||||
</method> \
|
||||
<signal name="InterfacesAdded"> \
|
||||
<arg name="objectPath" type="o"/> \
|
||||
<arg name="interfaces" type="a{sa{sv}}" /> \
|
||||
</signal> \
|
||||
<signal name="InterfacesRemoved"> \
|
||||
<arg name="objectPath" type="o"/> \
|
||||
<arg name="interfaces" type="as" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
|
||||
|
||||
const ObjectManager = new Lang.Class({
|
||||
Name: 'ObjectManager',
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { connection: null,
|
||||
name: null,
|
||||
objectPath: null,
|
||||
knownInterfaces: null,
|
||||
cancellable: null,
|
||||
onLoaded: null });
|
||||
|
||||
this._connection = params.connection;
|
||||
this._serviceName = params.name;
|
||||
this._managerPath = params.objectPath;
|
||||
this._cancellable = params.cancellable;
|
||||
|
||||
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||
g_interface_name: ObjectManagerInfo.name,
|
||||
g_interface_info: ObjectManagerInfo,
|
||||
g_name: this._serviceName,
|
||||
g_object_path: this._managerPath,
|
||||
g_flags: Gio.DBusProxyFlags.NONE });
|
||||
|
||||
this._interfaceInfos = {};
|
||||
this._objects = {};
|
||||
this._interfaces = {};
|
||||
this._onLoaded = params.onLoaded;
|
||||
|
||||
if (params.knownInterfaces)
|
||||
this._registerInterfaces(params.knownInterfaces);
|
||||
|
||||
// Start out inhibiting load until at least the proxy
|
||||
// manager is loaded and the remote objects are fetched
|
||||
this._numLoadInhibitors = 1;
|
||||
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||
this._cancellable,
|
||||
Lang.bind(this, this._onManagerProxyLoaded));
|
||||
},
|
||||
|
||||
_tryToCompleteLoad: function() {
|
||||
this._numLoadInhibitors--;
|
||||
if (this._numLoadInhibitors == 0) {
|
||||
if (this._onLoaded)
|
||||
this._onLoaded();
|
||||
}
|
||||
},
|
||||
|
||||
_addInterface: function(objectPath, interfaceName, onFinished) {
|
||||
let info = this._interfaceInfos[interfaceName];
|
||||
|
||||
if (!info) {
|
||||
if (onFinished)
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||
g_name: this._serviceName,
|
||||
g_object_path: objectPath,
|
||||
g_interface_name: interfaceName,
|
||||
g_interface_info: info,
|
||||
g_flags: Gio.DBusProxyFlags.NONE });
|
||||
|
||||
proxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(initable, result) {
|
||||
let error = null;
|
||||
try {
|
||||
initable.init_finish(result);
|
||||
} catch(e) {
|
||||
logError(e, 'could not initialize proxy for interface ' + interfaceName);
|
||||
|
||||
if (onFinished)
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
let isNewObject;
|
||||
if (!this._objects[objectPath]) {
|
||||
this._objects[objectPath] = {};
|
||||
isNewObject = true;
|
||||
} else {
|
||||
isNewObject = false;
|
||||
}
|
||||
|
||||
this._objects[objectPath][interfaceName] = proxy;
|
||||
|
||||
if (!this._interfaces[interfaceName])
|
||||
this._interfaces[interfaceName] = [];
|
||||
|
||||
this._interfaces[interfaceName].push(proxy);
|
||||
|
||||
if (isNewObject)
|
||||
this.emit('object-added', objectPath);
|
||||
|
||||
this.emit('interface-added', interfaceName, proxy);
|
||||
|
||||
if (onFinished)
|
||||
onFinished();
|
||||
}));
|
||||
},
|
||||
|
||||
_removeInterface: function(objectPath, interfaceName) {
|
||||
if (!this._objects[objectPath])
|
||||
return;
|
||||
|
||||
let proxy = this._objects[objectPath][interfaceName];
|
||||
|
||||
if (this._interfaces[interfaceName]) {
|
||||
let index = this._interfaces[interfaceName].indexOf(proxy);
|
||||
|
||||
if (index >= 0)
|
||||
this._interfaces[interfaceName].splice(index, 1);
|
||||
|
||||
if (this._interfaces[interfaceName].length == 0)
|
||||
delete this._interfaces[interfaceName];
|
||||
}
|
||||
|
||||
this.emit('interface-removed', interfaceName, proxy);
|
||||
|
||||
this._objects[objectPath][interfaceName] = null;
|
||||
|
||||
if (Object.keys(this._objects[objectPath]).length == 0) {
|
||||
delete this._objects[objectPath];
|
||||
this.emit('object-removed', objectPath);
|
||||
}
|
||||
},
|
||||
|
||||
_onManagerProxyLoaded: function(initable, result) {
|
||||
let error = null;
|
||||
try {
|
||||
initable.init_finish(result);
|
||||
} catch(e) {
|
||||
logError(e, 'could not initialize object manager for object ' + params.name);
|
||||
|
||||
this._tryToCompleteLoad();
|
||||
return;
|
||||
}
|
||||
|
||||
this._managerProxy.connectSignal('InterfacesAdded',
|
||||
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
|
||||
let interfaceNames = Object.keys(interfaces);
|
||||
for (let i = 0; i < interfaceNames.length; i++)
|
||||
this._addInterface(objectPath, interfaceNames[i]);
|
||||
}));
|
||||
this._managerProxy.connectSignal('InterfacesRemoved',
|
||||
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
|
||||
for (let i = 0; i < interfaceNames.length; i++)
|
||||
this._removeInterface(objectPath, interfaceNames[i]);
|
||||
}));
|
||||
|
||||
if (Object.keys(this._interfaceInfos).length == 0) {
|
||||
this._tryToCompleteLoad();
|
||||
return;
|
||||
}
|
||||
|
||||
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
|
||||
if (!result) {
|
||||
if (error) {
|
||||
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
|
||||
}
|
||||
|
||||
this._tryToCompleteLoad();
|
||||
return;
|
||||
}
|
||||
|
||||
let [objects] = result;
|
||||
|
||||
let objectPaths = Object.keys(objects);
|
||||
for (let i = 0; i < objectPaths.length; i++) {
|
||||
let objectPath = objectPaths[i];
|
||||
let object = objects[objectPath];
|
||||
|
||||
let interfaceNames = Object.getOwnPropertyNames(object);
|
||||
for (let j = 0; j < interfaceNames.length; j++) {
|
||||
let interfaceName = interfaceNames[j];
|
||||
|
||||
// Prevent load from completing until the interface is loaded
|
||||
this._numLoadInhibitors++;
|
||||
this._addInterface(objectPath,
|
||||
interfaceName,
|
||||
Lang.bind(this, this._tryToCompleteLoad));
|
||||
}
|
||||
}
|
||||
this._tryToCompleteLoad();
|
||||
}));
|
||||
},
|
||||
|
||||
_registerInterfaces: function(interfaces) {
|
||||
for (let i = 0; i < interfaces.length; i++) {
|
||||
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
|
||||
this._interfaceInfos[info.name] = info;
|
||||
}
|
||||
},
|
||||
|
||||
getProxy: function(objectPath, interfaceName) {
|
||||
let object = this._objects[objectPath];
|
||||
|
||||
if (!object)
|
||||
return null;
|
||||
|
||||
return object[interfaceName];
|
||||
},
|
||||
|
||||
getProxiesForInterface: function(interfaceName) {
|
||||
let proxyList = this._interfaces[interfaceName];
|
||||
|
||||
if (!proxyList)
|
||||
return [];
|
||||
|
||||
return proxyList;
|
||||
},
|
||||
|
||||
getAllProxies: function() {
|
||||
let proxies = [];
|
||||
|
||||
let objectPaths = Object.keys(this._objects);
|
||||
for (let i = 0; i < objectPaths.length; i++) {
|
||||
let object = this._objects[objectPaths];
|
||||
|
||||
let interfaceNames = Object.keys(object);
|
||||
for (let j = 0; i < interfaceNames.length; i++) {
|
||||
let interfaceName = interfaceNames[i];
|
||||
if (object[interfaceName])
|
||||
proxies.push(object(interfaceName));
|
||||
}
|
||||
}
|
||||
|
||||
return proxies;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ObjectManager.prototype);
|
119
js/misc/smartcardManager.js
Normal file
@ -0,0 +1,119 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const ObjectManager = imports.misc.objectManager;
|
||||
|
||||
const SmartcardTokenIface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
|
||||
<property name="Name" type="s" access="read"/> \
|
||||
<property name="Driver" type="o" access="read"/> \
|
||||
<property name="IsInserted" type="b" access="read"/> \
|
||||
<property name="UsedToLogin" type="b" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
let _smartcardManager = null;
|
||||
|
||||
function getSmartcardManager() {
|
||||
if (_smartcardManager == null)
|
||||
_smartcardManager = new SmartcardManager();
|
||||
|
||||
return _smartcardManager;
|
||||
}
|
||||
|
||||
const SmartcardManager = new Lang.Class({
|
||||
Name: 'SmartcardManager',
|
||||
_init: function() {
|
||||
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
|
||||
name: "org.gnome.SettingsDaemon.Smartcard",
|
||||
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
|
||||
knownInterfaces: [ SmartcardTokenIface ],
|
||||
onLoaded: Lang.bind(this, this._onLoaded) });
|
||||
this._insertedTokens = {};
|
||||
this._loginToken = null;
|
||||
},
|
||||
|
||||
_onLoaded: function() {
|
||||
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
|
||||
|
||||
for (let i = 0; i < tokens.length; i++)
|
||||
this._addToken(tokens[i]);
|
||||
|
||||
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||
this._addToken(proxy);
|
||||
}));
|
||||
|
||||
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||
this._removeToken(proxy);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateToken: function(token) {
|
||||
let objectPath = token.get_object_path();
|
||||
|
||||
delete this._insertedTokens[objectPath];
|
||||
|
||||
if (token.IsInserted)
|
||||
this._insertedTokens[objectPath] = token;
|
||||
|
||||
if (token.UsedToLogin)
|
||||
this._loginToken = token;
|
||||
},
|
||||
|
||||
_addToken: function(token) {
|
||||
this._updateToken(token);
|
||||
|
||||
token.connect('g-properties-changed',
|
||||
Lang.bind(this, function(proxy, properties) {
|
||||
if ('IsInserted' in properties.deep_unpack()) {
|
||||
this._updateToken(token);
|
||||
|
||||
if (token.IsInserted) {
|
||||
this.emit('smartcard-inserted', token);
|
||||
} else {
|
||||
this.emit('smartcard-removed', token);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Emit a smartcard-inserted at startup if it's already plugged in
|
||||
if (token.IsInserted)
|
||||
this.emit('smartcard-inserted', token);
|
||||
},
|
||||
|
||||
_removeToken: function(token) {
|
||||
let objectPath = token.get_object_path();
|
||||
|
||||
if (this._insertedTokens[objectPath] == token) {
|
||||
delete this._insertedTokens[objectPath];
|
||||
this.emit('smartcard-removed', token);
|
||||
}
|
||||
|
||||
if (this._loginToken == token)
|
||||
this._loginToken = null;
|
||||
|
||||
token.disconnectAll();
|
||||
},
|
||||
|
||||
hasInsertedTokens: function() {
|
||||
return Object.keys(this._insertedTokens).length > 0;
|
||||
},
|
||||
|
||||
hasInsertedLoginToken: function() {
|
||||
if (!this._loginToken)
|
||||
return false;
|
||||
|
||||
if (!this._loginToken.IsInserted)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
Signals.addSignalMethods(SmartcardManager.prototype);
|
108
js/misc/util.js
@ -1,7 +1,9 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
@ -78,6 +80,22 @@ function spawnCommandLine(command_line) {
|
||||
}
|
||||
}
|
||||
|
||||
// spawnApp:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv as if it was an application, handling startup notification
|
||||
function spawnApp(argv) {
|
||||
try {
|
||||
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
|
||||
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
|
||||
|
||||
let context = global.create_app_launch_context();
|
||||
app.launch([], context);
|
||||
} catch(err) {
|
||||
_handleSpawnError(argv[0], err);
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
@ -139,31 +157,6 @@ function _handleSpawnError(command, err) {
|
||||
Main.notifyError(title, err.message);
|
||||
}
|
||||
|
||||
// killall:
|
||||
// @processName: a process name
|
||||
//
|
||||
// Kills @processName. If no process with the given name is found,
|
||||
// this will fail silently.
|
||||
function killall(processName) {
|
||||
try {
|
||||
// pkill is more portable than killall, but on Linux at least
|
||||
// it won't match if you pass more than 15 characters of the
|
||||
// process name... However, if you use the '-f' flag to match
|
||||
// the entire command line, it will work, but we have to be
|
||||
// careful in that case that we can match
|
||||
// '/usr/bin/processName' but not 'gedit processName.c' or
|
||||
// whatever...
|
||||
|
||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||
// It might be useful to return success/failure, but we'd need
|
||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||
// the current callers care, we don't bother.
|
||||
} catch (e) {
|
||||
logError(e, 'Failed to kill ' + processName);
|
||||
}
|
||||
}
|
||||
|
||||
// lowerBound:
|
||||
// @array: an array or array-like object, already sorted
|
||||
// according to @cmp
|
||||
@ -214,28 +207,57 @@ function insertSorted(array, val, cmp) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
function makeCloseButton() {
|
||||
let closeButton = new St.Button({ style_class: 'notification-close'});
|
||||
const CloseButton = new Lang.Class({
|
||||
Name: 'CloseButton',
|
||||
Extends: St.Button,
|
||||
|
||||
// This is a bit tricky. St.Bin has its own x-align/y-align properties
|
||||
// that compete with Clutter's properties. This should be fixed for
|
||||
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
|
||||
// setters are a workaround to get Clutter's version.
|
||||
closeButton.set_x_align(Clutter.ActorAlign.END);
|
||||
closeButton.set_y_align(Clutter.ActorAlign.START);
|
||||
_init: function(boxpointer) {
|
||||
this.parent({ style_class: 'notification-close'});
|
||||
|
||||
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
|
||||
// to respect the alignments.
|
||||
closeButton.set_x_expand(true);
|
||||
closeButton.set_y_expand(true);
|
||||
// This is a bit tricky. St.Bin has its own x-align/y-align properties
|
||||
// that compete with Clutter's properties. This should be fixed for
|
||||
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
|
||||
// setters are a workaround to get Clutter's version.
|
||||
this.set_x_align(Clutter.ActorAlign.END);
|
||||
this.set_y_align(Clutter.ActorAlign.START);
|
||||
|
||||
closeButton.connect('style-changed', function() {
|
||||
let themeNode = closeButton.get_theme_node();
|
||||
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
|
||||
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
|
||||
});
|
||||
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
|
||||
// to respect the alignments.
|
||||
this.set_x_expand(true);
|
||||
this.set_y_expand(true);
|
||||
|
||||
return closeButton;
|
||||
this._boxPointer = boxpointer;
|
||||
if (boxpointer)
|
||||
this._boxPointer.connect('arrow-side-changed', Lang.bind(this, this._sync));
|
||||
},
|
||||
|
||||
_computeBoxPointerOffset: function() {
|
||||
if (!this._boxPointer || !this._boxPointer.actor.get_stage())
|
||||
return 0;
|
||||
|
||||
let side = this._boxPointer.arrowSide;
|
||||
if (side == St.Side.TOP)
|
||||
return this._boxPointer.getArrowHeight();
|
||||
else
|
||||
return 0;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let themeNode = this.get_theme_node();
|
||||
|
||||
let offY = this._computeBoxPointerOffset();
|
||||
this.translation_x = themeNode.get_length('-shell-close-overlap-x')
|
||||
this.translation_y = themeNode.get_length('-shell-close-overlap-y') + offY;
|
||||
},
|
||||
|
||||
vfunc_style_changed: function() {
|
||||
this._sync();
|
||||
this.parent();
|
||||
},
|
||||
});
|
||||
|
||||
function makeCloseButton(boxpointer) {
|
||||
return new CloseButton(boxpointer);
|
||||
}
|
||||
|
||||
function ensureActorVisibleInScrollView(scrollView, actor) {
|
||||
|
@ -106,6 +106,8 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
|
||||
this._switcherList = new AppSwitcher(apps, this);
|
||||
this._items = this._switcherList.icons;
|
||||
if (this._items.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
@ -355,10 +357,13 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
Name: 'WindowSwitcherPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
|
||||
_init: function(items) {
|
||||
this.parent(items);
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
||||
},
|
||||
|
||||
_getWindowList: function() {
|
||||
let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
||||
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
|
||||
: null;
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
|
||||
},
|
||||
|
||||
@ -368,9 +373,13 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
if (windows.length == 0)
|
||||
return false;
|
||||
|
||||
this._switcherList = new WindowList(windows);
|
||||
let mode = this._settings.get_enum('app-icon-mode');
|
||||
this._switcherList = new WindowList(windows, mode);
|
||||
this._items = this._switcherList.icons;
|
||||
|
||||
if (this._items.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -663,7 +672,7 @@ const ThumbnailList = new Lang.Class({
|
||||
const WindowIcon = new Lang.Class({
|
||||
Name: 'WindowIcon',
|
||||
|
||||
_init: function(window) {
|
||||
_init: function(window, mode) {
|
||||
this.window = window;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
|
||||
@ -681,8 +690,7 @@ const WindowIcon = new Lang.Class({
|
||||
|
||||
this._icon.destroy_all_children();
|
||||
|
||||
let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
||||
switch (settings.get_enum('app-icon-mode')) {
|
||||
switch (mode) {
|
||||
case AppIconMode.THUMBNAIL_ONLY:
|
||||
size = WINDOW_PREVIEW_SIZE;
|
||||
this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
|
||||
@ -720,7 +728,7 @@ const WindowList = new Lang.Class({
|
||||
Name: 'WindowList',
|
||||
Extends: SwitcherPopup.SwitcherList,
|
||||
|
||||
_init : function(windows) {
|
||||
_init : function(windows, mode) {
|
||||
this.parent(true);
|
||||
|
||||
this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -732,7 +740,7 @@ const WindowList = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let win = windows[i];
|
||||
let icon = new WindowIcon(win);
|
||||
let icon = new WindowIcon(win, mode);
|
||||
|
||||
this.addItem(icon.actor, icon.label);
|
||||
this.icons.push(icon);
|
||||
|
@ -14,15 +14,15 @@ const AppFavorites = new Lang.Class({
|
||||
_init: function() {
|
||||
this._favorites = {};
|
||||
global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
|
||||
this._reload();
|
||||
this.reload();
|
||||
},
|
||||
|
||||
_onFavsChanged: function() {
|
||||
this._reload();
|
||||
this.reload();
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_reload: function() {
|
||||
reload: function() {
|
||||
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let apps = ids.map(function (id) {
|
||||
|
@ -142,40 +142,33 @@ const BackgroundCache = new Lang.Class({
|
||||
cancellable: null,
|
||||
onFinished: null });
|
||||
|
||||
let fileLoad = { filename: params.filename,
|
||||
style: params.style,
|
||||
shouldCopy: false,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished,
|
||||
cancellable: new Gio.Cancellable(), };
|
||||
this._pendingFileLoads.push(fileLoad);
|
||||
|
||||
if (params.cancellable) {
|
||||
params.cancellable.connect(Lang.bind(this, function(c) {
|
||||
fileLoad.cancellable.cancel();
|
||||
}));
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
if (this._pendingFileLoads[i].filename == params.filename &&
|
||||
this._pendingFileLoads[i].style == params.style) {
|
||||
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._pendingFileLoads.push({ filename: params.filename,
|
||||
style: params.style,
|
||||
callers: [{ shouldCopy: false,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished }] });
|
||||
|
||||
let content = new Meta.Background({ meta_screen: global.screen,
|
||||
monitor: params.monitorIndex,
|
||||
effects: params.effects });
|
||||
|
||||
content.load_file_async(params.filename,
|
||||
params.style,
|
||||
fileLoad.cancellable,
|
||||
params.cancellable,
|
||||
Lang.bind(this,
|
||||
function(object, result) {
|
||||
if (fileLoad.cancellable.is_cancelled()) {
|
||||
if (params.cancellable && params.cancellable.is_cancelled()) {
|
||||
if (params.onFinished)
|
||||
params.onFinished(null);
|
||||
this._removePendingFileLoad(fileLoad);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
content.load_file_finish(result);
|
||||
|
||||
@ -185,25 +178,22 @@ const BackgroundCache = new Lang.Class({
|
||||
content = null;
|
||||
}
|
||||
|
||||
let needsCopy = false;
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
let pendingLoad = this._pendingFileLoads[i];
|
||||
if (pendingLoad.filename != params.filename ||
|
||||
pendingLoad.style != params.style)
|
||||
continue;
|
||||
|
||||
if (pendingLoad.cancellable.is_cancelled())
|
||||
continue;
|
||||
for (let j = 0; j < pendingLoad.callers.length; j++) {
|
||||
if (pendingLoad.callers[j].onFinished) {
|
||||
if (content && pendingLoad.callers[j].shouldCopy) {
|
||||
content = object.copy(pendingLoad.callers[j].monitorIndex,
|
||||
pendingLoad.callers[j].effects);
|
||||
|
||||
pendingLoad.cancellable.cancel();
|
||||
if (pendingLoad.onFinished) {
|
||||
if (content && needsCopy) {
|
||||
content = object.copy(pendingLoad.monitorIndex,
|
||||
pendingLoad.effects);
|
||||
}
|
||||
|
||||
pendingLoad.callers[j].onFinished(content);
|
||||
}
|
||||
|
||||
needsCopy = true;
|
||||
pendingLoad.onFinished(content);
|
||||
}
|
||||
|
||||
this._pendingFileLoads.splice(i, 1);
|
||||
@ -211,15 +201,6 @@ const BackgroundCache = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_removePendingFileLoad: function(fileLoad) {
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
if (this._pendingFileLoads[i].cancellable == fileLoad.cancellable) {
|
||||
this._pendingFileLoads.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getImageContent: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
style: null,
|
||||
@ -314,14 +295,15 @@ const Background = new Lang.Class({
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
layoutManager: Main.layoutManager,
|
||||
effects: Meta.BackgroundEffects.NONE });
|
||||
effects: Meta.BackgroundEffects.NONE,
|
||||
settings: null });
|
||||
this.actor = new Meta.BackgroundGroup();
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._destroySignalId = this.actor.connect('destroy',
|
||||
Lang.bind(this, this._destroy));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
|
||||
this._settings = params.settings;
|
||||
this._monitorIndex = params.monitorIndex;
|
||||
this._layoutManager = params.layoutManager;
|
||||
this._effects = params.effects;
|
||||
@ -333,13 +315,12 @@ const Background = new Lang.Class({
|
||||
|
||||
this._brightness = 1.0;
|
||||
this._vignetteSharpness = 0.2;
|
||||
this._saturation = 1.0;
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this.isLoaded = false;
|
||||
|
||||
this._settings.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
|
||||
this._load();
|
||||
},
|
||||
@ -380,6 +361,10 @@ const Background = new Lang.Class({
|
||||
|
||||
this.actor.disconnect(this._destroySignalId);
|
||||
this._destroySignalId = 0;
|
||||
|
||||
if (this._settingsChangedSignalId != 0)
|
||||
this._settings.disconnect(this._settingsChangedSignalId);
|
||||
this._settingsChangedSignalId = 0;
|
||||
},
|
||||
|
||||
_setLoaded: function() {
|
||||
@ -430,7 +415,6 @@ const Background = new Lang.Class({
|
||||
},
|
||||
|
||||
_addImage: function(content, index, filename) {
|
||||
content.saturation = this._saturation;
|
||||
content.brightness = this._brightness;
|
||||
content.vignette_sharpness = this._vignetteSharpness;
|
||||
|
||||
@ -446,7 +430,6 @@ const Background = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateImage: function(content, index, filename) {
|
||||
content.saturation = this._saturation;
|
||||
content.brightness = this._brightness;
|
||||
content.vignette_sharpness = this._vignetteSharpness;
|
||||
|
||||
@ -604,24 +587,6 @@ const Background = new Lang.Class({
|
||||
this._loadFile(filename);
|
||||
},
|
||||
|
||||
get saturation() {
|
||||
return this._saturation;
|
||||
},
|
||||
|
||||
set saturation(saturation) {
|
||||
this._saturation = saturation;
|
||||
|
||||
if (this._pattern && this._pattern.content)
|
||||
this._pattern.content.saturation = saturation;
|
||||
|
||||
let keys = Object.keys(this._images);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let image = this._images[keys[i]];
|
||||
if (image && image.content)
|
||||
image.content.saturation = saturation;
|
||||
}
|
||||
},
|
||||
|
||||
get brightness() {
|
||||
return this._brightness;
|
||||
},
|
||||
@ -735,8 +700,10 @@ const BackgroundManager = new Lang.Class({
|
||||
layoutManager: Main.layoutManager,
|
||||
monitorIndex: null,
|
||||
effects: Meta.BackgroundEffects.NONE,
|
||||
controlPosition: true });
|
||||
controlPosition: true,
|
||||
settingsSchema: BACKGROUND_SCHEMA });
|
||||
|
||||
this._settings = new Gio.Settings({ schema: params.settingsSchema });
|
||||
this._container = params.container;
|
||||
this._layoutManager = params.layoutManager;
|
||||
this._effects = params.effects;
|
||||
@ -763,7 +730,6 @@ const BackgroundManager = new Lang.Class({
|
||||
let newBackground = this._createBackground(monitorIndex);
|
||||
newBackground.vignetteSharpness = background.vignetteSharpness;
|
||||
newBackground.brightness = background.brightness;
|
||||
newBackground.saturation = background.saturation;
|
||||
newBackground.visible = background.visible;
|
||||
|
||||
newBackground.loadedSignalId = newBackground.connect('loaded',
|
||||
@ -795,7 +761,8 @@ const BackgroundManager = new Lang.Class({
|
||||
_createBackground: function() {
|
||||
let background = new Background({ monitorIndex: this._monitorIndex,
|
||||
layoutManager: this._layoutManager,
|
||||
effects: this._effects });
|
||||
effects: this._effects,
|
||||
settings: this._settings });
|
||||
this._container.add_child(background.actor);
|
||||
|
||||
let monitor = this._layoutManager.monitors[this._monitorIndex];
|
||||
|
@ -3,8 +3,9 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -61,6 +62,10 @@ const BoxPointer = new Lang.Class({
|
||||
this._muteInput();
|
||||
},
|
||||
|
||||
get arrowSide() {
|
||||
return this._arrowSide;
|
||||
},
|
||||
|
||||
_muteInput: function() {
|
||||
if (this._capturedEventId == 0)
|
||||
this._capturedEventId = this.actor.connect('captured-event',
|
||||
@ -180,7 +185,9 @@ const BoxPointer = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
|
||||
alloc.min_size = minSize;
|
||||
alloc.natural_size = naturalSize;
|
||||
this._adjustAllocationForArrow(false, alloc);
|
||||
@ -612,6 +619,8 @@ const BoxPointer = new Lang.Class({
|
||||
this._container.queue_relayout();
|
||||
return false;
|
||||
}));
|
||||
|
||||
this.emit('arrow-side-changed');
|
||||
}
|
||||
},
|
||||
|
||||
@ -639,5 +648,21 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
get opacity() {
|
||||
return this.actor.opacity;
|
||||
},
|
||||
|
||||
updateArrowSide: function(side) {
|
||||
this._arrowSide = side;
|
||||
this._border.queue_repaint();
|
||||
|
||||
this.emit('arrow-side-changed');
|
||||
},
|
||||
|
||||
getPadding: function(side) {
|
||||
return this.bin.get_theme_node().get_padding(side);
|
||||
},
|
||||
|
||||
getArrowHeight: function() {
|
||||
return this.actor.get_theme_node().get_length('-arrow-rise');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(BoxPointer.prototype);
|
||||
|
@ -190,16 +190,18 @@ const EmptyEventSource = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(EmptyEventSource.prototype);
|
||||
|
||||
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
|
||||
<method name="GetEvents">
|
||||
<arg type="x" direction="in" />
|
||||
<arg type="x" direction="in" />
|
||||
<arg type="b" direction="in" />
|
||||
<arg type="a(sssbxxa{sv})" direction="out" />
|
||||
</method>
|
||||
<property name="HasCalendars" type="b" access="read" />
|
||||
<signal name="Changed" />
|
||||
</interface>;
|
||||
const CalendarServerIface = '<node> \
|
||||
<interface name="org.gnome.Shell.CalendarServer"> \
|
||||
<method name="GetEvents"> \
|
||||
<arg type="x" direction="in" /> \
|
||||
<arg type="x" direction="in" /> \
|
||||
<arg type="b" direction="in" /> \
|
||||
<arg type="a(sssbxxa{sv})" direction="out" /> \
|
||||
</method> \
|
||||
<property name="HasCalendars" type="b" access="read" /> \
|
||||
<signal name="Changed" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
|
||||
|
||||
@ -444,14 +446,17 @@ const Calendar = new Lang.Class({
|
||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||
|
||||
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
|
||||
accessible_name: _("Previous month"),
|
||||
can_focus: true });
|
||||
this._topBox.add(this._backButton);
|
||||
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||
|
||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label',
|
||||
can_focus: true });
|
||||
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
|
||||
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
|
||||
accessible_name: _("Next month"),
|
||||
can_focus: true });
|
||||
this._topBox.add(this._forwardButton);
|
||||
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
||||
|
@ -1,115 +1,40 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const CheckBoxContainer = new Lang.Class({
|
||||
Name: 'CheckBoxContainer',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.actor.connect('get-preferred-width',
|
||||
Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height',
|
||||
Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate',
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor.connect('style-changed', Lang.bind(this,
|
||||
function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
this._spacing = node.get_length('spacing');
|
||||
}));
|
||||
this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
|
||||
|
||||
this._box = new St.Bin();
|
||||
this.actor.add_actor(this._box);
|
||||
|
||||
this.label = new St.Label();
|
||||
this.label.clutter_text.set_line_wrap(true);
|
||||
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
||||
this.actor.add_actor(this.label);
|
||||
|
||||
this._spacing = 0;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
|
||||
|
||||
alloc.min_size = minWidth + this._spacing;
|
||||
alloc.natural_size = natWidth + this._spacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
/* FIXME: StBoxlayout currently does not handle
|
||||
height-for-width children correctly, so hard-code
|
||||
two lines for the label until that problem is fixed.
|
||||
|
||||
https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
|
||||
/*
|
||||
let [minBoxHeight, natBoxHeight] =
|
||||
this._box.get_preferred_height(forWidth);
|
||||
let [minLabelHeight, natLabelHeight] =
|
||||
this.label.get_preferred_height(forWidth);
|
||||
|
||||
alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
|
||||
alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
|
||||
*/
|
||||
let [minBoxHeight, natBoxHeight] =
|
||||
this._box.get_preferred_height(-1);
|
||||
let [minLabelHeight, natLabelHeight] =
|
||||
this.label.get_preferred_height(-1);
|
||||
|
||||
alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
|
||||
alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
let [minBoxWidth, natBoxWidth] =
|
||||
this._box.get_preferred_width(-1);
|
||||
let [minBoxHeight, natBoxHeight] =
|
||||
this._box.get_preferred_height(-1);
|
||||
childBox.x1 = box.x1;
|
||||
childBox.x2 = box.x1 + natBoxWidth;
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y1 + natBoxHeight;
|
||||
this._box.allocate(childBox, flags);
|
||||
|
||||
childBox.x1 = box.x1 + natBoxWidth + this._spacing;
|
||||
childBox.x2 = availWidth - childBox.x1;
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y2;
|
||||
this.label.allocate(childBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const CheckBox = new Lang.Class({
|
||||
Name: 'CheckBox',
|
||||
|
||||
_init: function(label) {
|
||||
let container = new St.BoxLayout();
|
||||
this.actor = new St.Button({ style_class: 'check-box',
|
||||
child: container,
|
||||
button_mask: St.ButtonMask.ONE,
|
||||
toggle_mode: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._container = new CheckBoxContainer();
|
||||
this.actor.set_child(this._container.actor);
|
||||
|
||||
this._box = new St.Bin();
|
||||
this._box.set_y_align(Clutter.ActorAlign.START);
|
||||
container.add_actor(this._box);
|
||||
|
||||
this._label = new St.Label();
|
||||
this._label.clutter_text.set_line_wrap(true);
|
||||
this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
||||
container.add_actor(this._label);
|
||||
|
||||
if (label)
|
||||
this.setLabel(label);
|
||||
},
|
||||
|
||||
setLabel: function(label) {
|
||||
this._container.label.set_text(label);
|
||||
this._label.set_text(label);
|
||||
},
|
||||
|
||||
getLabelActor: function() {
|
||||
return this._container.label;
|
||||
return this._label;
|
||||
}
|
||||
});
|
||||
|
@ -75,12 +75,14 @@ function startAppForMount(app, mount) {
|
||||
|
||||
/******************************************/
|
||||
|
||||
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
|
||||
<method name="SniffURI">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
</interface>;
|
||||
const HotplugSnifferIface = '<node> \
|
||||
<interface name="org.gnome.Shell.HotplugSniffer"> \
|
||||
<method name="SniffURI"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="as" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
|
||||
function HotplugSniffer() {
|
||||
|
@ -13,8 +13,6 @@ const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
|
||||
let prompter = null;
|
||||
|
||||
const KeyringDialog = new Lang.Class({
|
||||
Name: 'KeyringDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -80,23 +78,26 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_buildControlTable: function() {
|
||||
let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
|
||||
let layout = new Clutter.TableLayout();
|
||||
let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(table);
|
||||
let row = 0;
|
||||
|
||||
if (this.prompt.password_visible) {
|
||||
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||
let label = new St.Label({ style_class: 'prompt-dialog-password-label' });
|
||||
label.set_text(_("Password:"));
|
||||
table.add(label, { row: row, col: 0,
|
||||
x_expand: false, x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
layout.pack(label, 0, row);
|
||||
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||
x_align: Clutter.TableAlignment.START });
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: '',
|
||||
can_focus: true});
|
||||
can_focus: true });
|
||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
|
||||
table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
|
||||
layout.pack(this._passwordEntry, 1, row);
|
||||
row++;
|
||||
} else {
|
||||
this._passwordEntry = null;
|
||||
@ -105,17 +106,16 @@ const KeyringDialog = new Lang.Class({
|
||||
if (this.prompt.confirm_visible) {
|
||||
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||
label.set_text(_("Type again:"));
|
||||
table.add(label, { row: row, col: 0,
|
||||
x_expand: false, x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
layout.pack(label, 0, row);
|
||||
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||
x_align: Clutter.TableAlignment.START });
|
||||
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: '',
|
||||
can_focus: true});
|
||||
can_focus: true });
|
||||
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
|
||||
this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
|
||||
table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
|
||||
layout.pack(this._confirmEntry, 1, row);
|
||||
row++;
|
||||
} else {
|
||||
this._confirmEntry = null;
|
||||
@ -128,14 +128,14 @@ const KeyringDialog = new Lang.Class({
|
||||
let choice = new CheckBox.CheckBox();
|
||||
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
|
||||
table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
|
||||
layout.pack(choice.actor, 1, row);
|
||||
row++;
|
||||
}
|
||||
|
||||
let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
warning.clutter_text.line_wrap = true;
|
||||
table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
|
||||
layout.pack(warning, 1, row);
|
||||
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
@ -221,27 +221,56 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
});
|
||||
|
||||
const KeyringDummyDialog = new Lang.Class({
|
||||
Name: 'KeyringDummyDialog',
|
||||
|
||||
_init: function() {
|
||||
this.prompt = new Shell.KeyringPrompt();
|
||||
this.prompt.connect('show-password',
|
||||
Lang.bind(this, this._cancelPrompt));
|
||||
this.prompt.connect('show-confirm', Lang.bind(this,
|
||||
this._cancelPrompt));
|
||||
},
|
||||
|
||||
_cancelPrompt: function() {
|
||||
this.prompt.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
const KeyringPrompter = new Lang.Class({
|
||||
Name: 'KeyringPrompter',
|
||||
|
||||
_init: function() {
|
||||
this._prompter = new Gcr.SystemPrompter();
|
||||
this._prompter.connect('new-prompt', function(prompter) {
|
||||
let dialog = new KeyringDialog();
|
||||
return dialog.prompt;
|
||||
});
|
||||
this._prompter.connect('new-prompt', Lang.bind(this,
|
||||
function() {
|
||||
let dialog = this._enabled ? new KeyringDialog()
|
||||
: new KeyringDummyDialog();
|
||||
this._currentPrompt = dialog.prompt;
|
||||
return this._currentPrompt;
|
||||
}));
|
||||
this._dbusId = null;
|
||||
this._registered = false;
|
||||
this._enabled = false;
|
||||
this._currentPrompt = null;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._prompter.register(Gio.DBus.session);
|
||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
|
||||
if (!this._registered) {
|
||||
this._prompter.register(Gio.DBus.session);
|
||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
|
||||
this._registered = true;
|
||||
}
|
||||
this._enabled = true;
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._prompter.unregister(false);
|
||||
Gio.DBus.session.unown_name(this._dbusId);
|
||||
this._enabled = false;
|
||||
|
||||
if (this._prompter.prompting)
|
||||
this._currentPrompt.cancel();
|
||||
this._currentPrompt = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -62,14 +62,9 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
|
||||
if (this._content.message != null) {
|
||||
let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||
text: this._content.message,
|
||||
// HACK: for reasons unknown to me, the label
|
||||
// is not asked the correct height for width,
|
||||
// and thus is underallocated
|
||||
// place a fixed height to avoid overflowing
|
||||
style: 'height: 3em'
|
||||
});
|
||||
text: this._content.message });
|
||||
descriptionLabel.clutter_text.line_wrap = true;
|
||||
descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
|
||||
messageBox.add(descriptionLabel,
|
||||
{ y_fill: true,
|
||||
@ -77,13 +72,18 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
expand: true });
|
||||
}
|
||||
|
||||
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
|
||||
let layout = new Clutter.TableLayout();
|
||||
let secretTable = new St.Widget({ style_class: 'network-dialog-secret-table',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(secretTable);
|
||||
|
||||
let initialFocusSet = false;
|
||||
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: 'prompt-dialog-password-label',
|
||||
text: secret.label });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
|
||||
let reactive = secret.key != null;
|
||||
|
||||
@ -116,11 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
} else
|
||||
secret.valid = true;
|
||||
|
||||
secretTable.add(label, { row: pos, col: 0,
|
||||
x_expand: false, x_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
||||
layout.pack(label, 0, pos);
|
||||
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||
x_align: Clutter.TableAlignment.START });
|
||||
layout.pack(secret.entry, 1, pos);
|
||||
pos++;
|
||||
|
||||
if (secret.password)
|
||||
@ -385,11 +384,7 @@ const VPNRequestHandler = new Lang.Class({
|
||||
this._childPid = pid;
|
||||
this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
|
||||
this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
|
||||
// We need this one too, even if don't actually care of what the process
|
||||
// has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
|
||||
// is kept open indefinitely
|
||||
let stderrStream = new Gio.UnixInputStream({ fd: stderr, close_fd: true });
|
||||
stderrStream.close(null);
|
||||
GLib.close(stderr);
|
||||
this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
|
||||
|
||||
if (this._newStylePlugin)
|
||||
|
@ -1,61 +0,0 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Recorder = new Lang.Class({
|
||||
Name: 'Recorder',
|
||||
|
||||
_init: function() {
|
||||
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||
this._recorder = null;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
Main.wm.addKeybinding('toggle-recording',
|
||||
this._bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Lang.bind(this, this._toggleRecorder));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
Main.wm.removeKeybinding('toggle-recording');
|
||||
},
|
||||
|
||||
_ensureRecorder: function() {
|
||||
if (this._recorder == null)
|
||||
this._recorder = new Shell.Recorder({ stage: global.stage });
|
||||
return this._recorder;
|
||||
},
|
||||
|
||||
_toggleRecorder: function() {
|
||||
let recorder = this._ensureRecorder();
|
||||
if (recorder.is_recording()) {
|
||||
recorder.close();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
|
||||
/* Translators: this is a filename used for screencast recording */
|
||||
// xgettext:no-c-format
|
||||
recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
|
||||
let pipeline = this._recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const Component = Recorder;
|
@ -13,7 +13,6 @@ const Tp = imports.gi.TelepathyGLib;
|
||||
const History = imports.misc.history;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -416,7 +415,7 @@ const TelepathyClient = new Lang.Class({
|
||||
_ensureAppSource: function() {
|
||||
if (this._appSource == null) {
|
||||
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
||||
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
this._appSource.policy = new MessageTray.NotificationApplicationPolicy('empathy');
|
||||
|
||||
Main.messageTray.add(this._appSource);
|
||||
this._appSource.connect('destroy', Lang.bind(this, function () {
|
||||
@ -447,6 +446,7 @@ const ChatSource = new Lang.Class({
|
||||
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
|
||||
|
||||
this._notification = new ChatNotification(this);
|
||||
this._notification.connect('clicked', Lang.bind(this, this.open));
|
||||
this._notification.setUrgency(MessageTray.Urgency.HIGH);
|
||||
this._notifyTimeoutId = 0;
|
||||
|
||||
@ -488,7 +488,7 @@ const ChatSource = new Lang.Class({
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
return new MessageTray.NotificationApplicationPolicy('empathy');
|
||||
},
|
||||
|
||||
_updateAlias: function() {
|
||||
@ -545,20 +545,19 @@ const ChatSource = new Lang.Class({
|
||||
this._notification.update(this._notification.title, null, { customContent: true });
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
if (this._client.is_handling_channel(this._channel)) {
|
||||
// We are handling the channel, try to pass it to Empathy
|
||||
this._client.delegate_channels_async([this._channel],
|
||||
global.get_current_time(),
|
||||
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
||||
}
|
||||
else {
|
||||
// We are not the handler, just ask to present the channel
|
||||
let dbus = Tp.DBusDaemon.dup();
|
||||
let cd = Tp.ChannelDispatcher.new(dbus);
|
||||
open: function() {
|
||||
if (this._client.is_handling_channel(this._channel)) {
|
||||
// We are handling the channel, try to pass it to Empathy
|
||||
this._client.delegate_channels_async([this._channel],
|
||||
global.get_current_time(),
|
||||
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
||||
} else {
|
||||
// We are not the handler, just ask to present the channel
|
||||
let dbus = Tp.DBusDaemon.dup();
|
||||
let cd = Tp.ChannelDispatcher.new(dbus);
|
||||
|
||||
cd.present_channel_async(this._channel, global.get_current_time(), null);
|
||||
}
|
||||
cd.present_channel_async(this._channel, global.get_current_time(), null);
|
||||
}
|
||||
},
|
||||
|
||||
_getLogMessages: function() {
|
||||
@ -1061,7 +1060,7 @@ const ApproverSource = new Lang.Class({
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
return new MessageTray.NotificationApplicationPolicy('empathy');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -1096,22 +1095,16 @@ const RoomInviteNotification = new Lang.Class({
|
||||
* for example. */
|
||||
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
|
||||
|
||||
this.addButton('decline', _("Decline"));
|
||||
this.addButton('accept', _("Accept"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'decline':
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
||||
'', function(src, result) {
|
||||
src.leave_channels_finish(result)});
|
||||
break;
|
||||
case 'accept':
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
||||
function(src, result) {
|
||||
src.handle_with_time_finish(result)});
|
||||
break;
|
||||
}
|
||||
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
|
||||
src.leave_channels_finish(result);
|
||||
});
|
||||
this.destroy();
|
||||
}));
|
||||
this.addAction(_("Accept"), Lang.bind(this, function() {
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
|
||||
src.handle_with_time_finish(result);
|
||||
});
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
@ -1135,23 +1128,19 @@ const AudioVideoNotification = new Lang.Class({
|
||||
this.parent(source, title, null, { customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this.addButton('reject', _("Decline"));
|
||||
/* translators: this is a button label (verb), not a noun */
|
||||
this.addButton('answer', _("Answer"));
|
||||
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'reject':
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
||||
'', function(src, result) {
|
||||
src.leave_channels_finish(result)});
|
||||
break;
|
||||
case 'answer':
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
||||
function(src, result) {
|
||||
src.handle_with_time_finish(result)});
|
||||
break;
|
||||
}
|
||||
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
|
||||
src.leave_channels_finish(result);
|
||||
});
|
||||
this.destroy();
|
||||
}));
|
||||
/* translators: this is a button label (verb), not a noun */
|
||||
this.addAction(_("Answer"), Lang.bind(this, function() {
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
|
||||
src.handle_with_time_finish(result);
|
||||
});
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
@ -1175,22 +1164,16 @@ const FileTransferNotification = new Lang.Class({
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this.addButton('decline', _("Decline"));
|
||||
this.addButton('accept', _("Accept"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'decline':
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
||||
'', function(src, result) {
|
||||
src.leave_channels_finish(result)});
|
||||
break;
|
||||
case 'accept':
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
||||
function(src, result) {
|
||||
src.handle_with_time_finish(result)});
|
||||
break;
|
||||
}
|
||||
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
|
||||
src.leave_channels_finish(result);
|
||||
});
|
||||
this.destroy();
|
||||
}));
|
||||
this.addAction(_("Accept"), Lang.bind(this, function() {
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
|
||||
src.handle_with_time_finish(result);
|
||||
});
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
@ -1238,27 +1221,20 @@ const SubscriptionRequestNotification = new Lang.Class({
|
||||
|
||||
this.addActor(layout);
|
||||
|
||||
this.addButton('decline', _("Decline"));
|
||||
this.addButton('accept', _("Accept"));
|
||||
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||
contact.remove_async(function(src, result) {
|
||||
src.remove_finish(result);
|
||||
});
|
||||
}));
|
||||
this.addAction(_("Accept"), Lang.bind(this, function() {
|
||||
// Authorize the contact and request to see his status as well
|
||||
contact.authorize_publication_async(function(src, result) {
|
||||
src.authorize_publication_finish(result);
|
||||
});
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'decline':
|
||||
contact.remove_async(function(src, result) {
|
||||
src.remove_finish(result)});
|
||||
break;
|
||||
case 'accept':
|
||||
// Authorize the contact and request to see his status as well
|
||||
contact.authorize_publication_async(function(src, result) {
|
||||
src.authorize_publication_finish(result)});
|
||||
|
||||
contact.request_subscription_async('', function(src, result) {
|
||||
src.request_subscription_finish(result)});
|
||||
break;
|
||||
}
|
||||
|
||||
// rely on _subscriptionStatesChangedCb to destroy the
|
||||
// notification
|
||||
contact.request_subscription_async('', function(src, result) {
|
||||
src.request_subscription_finish(result);
|
||||
});
|
||||
}));
|
||||
|
||||
this._changedId = contact.connect('subscription-states-changed',
|
||||
@ -1357,18 +1333,11 @@ const AccountNotification = new Lang.Class({
|
||||
|
||||
this._account = account;
|
||||
|
||||
this.addButton('view', _("View account"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'view':
|
||||
let cmd = 'empathy-accounts --select-account=' +
|
||||
account.get_path_suffix();
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||
app_info.launch([], global.create_app_launch_context());
|
||||
break;
|
||||
}
|
||||
this.destroy();
|
||||
this.addAction(_("View account"), Lang.bind(this, function() {
|
||||
let cmd = 'empathy-accounts --select-account=' +
|
||||
account.get_path_suffix();
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||
app_info.launch([], global.create_app_launch_context());
|
||||
}));
|
||||
|
||||
this._enabledId = account.connect('notify::enabled',
|
||||
|
@ -423,7 +423,10 @@ const Dash = new Lang.Class({
|
||||
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
||||
AppFavorites.getAppFavorites().reload();
|
||||
this._queueRedisplay();
|
||||
}));
|
||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
||||
|
||||
@ -502,15 +505,21 @@ const Dash = new Lang.Class({
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_hookUpLabel: function(item) {
|
||||
_hookUpLabel: function(item, appIcon) {
|
||||
item.child.connect('notify::hover', Lang.bind(this, function() {
|
||||
this._onHover(item);
|
||||
this._syncLabel(item, appIcon);
|
||||
}));
|
||||
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
this._labelShowing = false;
|
||||
item.hideLabel();
|
||||
}));
|
||||
|
||||
if (appIcon) {
|
||||
appIcon.connect('sync-tooltip', Lang.bind(this, function() {
|
||||
this._syncLabel(item, appIcon);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_createAppItem: function(app) {
|
||||
@ -539,7 +548,7 @@ const Dash = new Lang.Class({
|
||||
item.setLabelText(app.get_name());
|
||||
|
||||
appIcon.icon.setIconSize(this.iconSize);
|
||||
this._hookUpLabel(item);
|
||||
this._hookUpLabel(item, appIcon);
|
||||
|
||||
return item;
|
||||
},
|
||||
@ -557,8 +566,10 @@ const Dash = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_onHover: function (item) {
|
||||
if (item.child.get_hover()) {
|
||||
_syncLabel: function (item, appIcon) {
|
||||
let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
|
||||
|
||||
if (shouldShow) {
|
||||
if (this._showLabelTimeoutId == 0) {
|
||||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||
|
@ -49,11 +49,13 @@ const DateMenuButton = new Lang.Class({
|
||||
menuAlignment = 1.0 - menuAlignment;
|
||||
this.parent(menuAlignment);
|
||||
|
||||
this._clockDisplay = new St.Label();
|
||||
this._clockDisplay = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.label_actor = this._clockDisplay;
|
||||
this.actor.add_actor(this._clockDisplay);
|
||||
this.actor.add_style_class_name ('clock-display');
|
||||
|
||||
hbox = new St.BoxLayout({name: 'calendarArea' });
|
||||
this.menu.addActor(hbox);
|
||||
hbox = new St.BoxLayout({ name: 'calendarArea' });
|
||||
this.menu.box.add_child(hbox);
|
||||
|
||||
// Fill up the first column
|
||||
|
||||
@ -61,9 +63,8 @@ const DateMenuButton = new Lang.Class({
|
||||
hbox.add(vbox);
|
||||
|
||||
// Date
|
||||
this._date = new St.Label();
|
||||
this.actor.label_actor = this._clockDisplay;
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
this._date = new St.Label({ style_class: 'datemenu-date-label',
|
||||
can_focus: true });
|
||||
vbox.add(this._date);
|
||||
|
||||
this._eventList = new Calendar.EventsList();
|
||||
|
66
js/ui/dnd.js
@ -5,6 +5,7 @@ const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const St = imports.gi.St;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -27,9 +28,9 @@ const DragMotionResult = {
|
||||
};
|
||||
|
||||
const DRAG_CURSOR_MAP = {
|
||||
0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.DND_COPY,
|
||||
2: Shell.Cursor.DND_MOVE
|
||||
0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
|
||||
1: Meta.Cursor.DND_COPY,
|
||||
2: Meta.Cursor.DND_MOVE
|
||||
};
|
||||
|
||||
const DragDropResult = {
|
||||
@ -85,11 +86,6 @@ const _Draggable = new Lang.Class({
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
|
||||
// If the drag actor is destroyed and we were going to fix
|
||||
// up its hover state, fix up the parent hover state instead
|
||||
if (this.actor == this._firstLeaveActor)
|
||||
this._firstLeaveActor = this._dragOrigParent;
|
||||
|
||||
if (this._dragInProgress && this._dragCancellable)
|
||||
this._cancelDrag(global.get_current_time());
|
||||
this.disconnectAll();
|
||||
@ -105,12 +101,6 @@ const _Draggable = new Lang.Class({
|
||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||
this._dragCancellable = true;
|
||||
|
||||
// During the drag, we eat enter/leave events so that actors don't prelight.
|
||||
// But we remember the actors that we first left/last entered so we can
|
||||
// fix up the hover state after the drag ends.
|
||||
this._firstLeaveActor = null;
|
||||
this._lastEnterActor = null;
|
||||
|
||||
this._eventsGrabbed = false;
|
||||
},
|
||||
|
||||
@ -196,11 +186,6 @@ const _Draggable = new Lang.Class({
|
||||
this._cancelDrag(event.get_time());
|
||||
return true;
|
||||
}
|
||||
} else if (event.type() == Clutter.EventType.LEAVE) {
|
||||
if (this._firstLeaveActor == null)
|
||||
this._firstLeaveActor = event.get_source();
|
||||
} else if (event.type() == Clutter.EventType.ENTER) {
|
||||
this._lastEnterActor = event.get_source();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -244,7 +229,7 @@ const _Draggable = new Lang.Class({
|
||||
if (this._onEventId)
|
||||
this._ungrabActor();
|
||||
this._grabEvents();
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
|
||||
|
||||
this._dragX = this._dragStartX = stageX;
|
||||
this._dragY = this._dragStartY = stageY;
|
||||
@ -374,7 +359,7 @@ const _Draggable = new Lang.Class({
|
||||
if (motionFunc) {
|
||||
let result = motionFunc(dragEvent);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -392,13 +377,13 @@ const _Draggable = new Lang.Class({
|
||||
targY,
|
||||
0);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
|
||||
return false;
|
||||
},
|
||||
|
||||
@ -470,7 +455,7 @@ const _Draggable = new Lang.Class({
|
||||
}
|
||||
|
||||
this._dragInProgress = false;
|
||||
global.unset_cursor();
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this.emit('drag-end', event.get_time(), true);
|
||||
this._dragComplete();
|
||||
return true;
|
||||
@ -517,17 +502,12 @@ const _Draggable = new Lang.Class({
|
||||
},
|
||||
|
||||
_cancelDrag: function(eventTime) {
|
||||
if (this._updateHoverId) {
|
||||
GLib.source_remove(this._updateHoverId);
|
||||
this._updateHoverId = 0;
|
||||
}
|
||||
|
||||
this.emit('drag-cancelled', eventTime);
|
||||
this._dragInProgress = false;
|
||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||
|
||||
if (this._actorDestroyed) {
|
||||
global.unset_cursor();
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
if (!this._buttonDown)
|
||||
this._dragComplete();
|
||||
this.emit('drag-end', eventTime, false);
|
||||
@ -581,7 +561,7 @@ const _Draggable = new Lang.Class({
|
||||
} else {
|
||||
dragActor.destroy();
|
||||
}
|
||||
global.unset_cursor();
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this.emit('drag-end', eventTime, false);
|
||||
|
||||
this._animationInProgress = false;
|
||||
@ -589,32 +569,16 @@ const _Draggable = new Lang.Class({
|
||||
this._dragComplete();
|
||||
},
|
||||
|
||||
// Actor is an actor we have entered or left during the drag; call
|
||||
// st_widget_sync_hover on all StWidget ancestors
|
||||
_syncHover: function(actor) {
|
||||
while (actor) {
|
||||
let parent = actor.get_parent();
|
||||
if (actor instanceof St.Widget)
|
||||
actor.sync_hover();
|
||||
|
||||
actor = parent;
|
||||
}
|
||||
},
|
||||
|
||||
_dragComplete: function() {
|
||||
if (!this._actorDestroyed)
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
||||
|
||||
this._ungrabEvents();
|
||||
global.sync_pointer();
|
||||
|
||||
if (this._firstLeaveActor) {
|
||||
this._syncHover(this._firstLeaveActor);
|
||||
this._firstLeaveActor = null;
|
||||
}
|
||||
|
||||
if (this._lastEnterActor) {
|
||||
this._syncHover(this._lastEnterActor);
|
||||
this._lastEnterActor = null;
|
||||
if (this._updateHoverId) {
|
||||
GLib.source_remove(this._updateHoverId);
|
||||
this._updateHoverId = 0;
|
||||
}
|
||||
|
||||
this._dragActor = undefined;
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
@ -32,7 +31,7 @@ const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
@ -44,81 +43,118 @@ const _DIALOG_ICON_SIZE = 32;
|
||||
|
||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
|
||||
|
||||
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
|
||||
<method name="Open">
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="ao" direction="in" />
|
||||
</method>
|
||||
<method name="Close" />
|
||||
<signal name="ConfirmedLogout" />
|
||||
<signal name="ConfirmedReboot" />
|
||||
<signal name="ConfirmedShutdown" />
|
||||
<signal name="Canceled" />
|
||||
<signal name="Closed" />
|
||||
</interface>;
|
||||
const EndSessionDialogIface = '<node> \
|
||||
<interface name="org.gnome.SessionManager.EndSessionDialog"> \
|
||||
<method name="Open"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
<arg type="u" direction="in" /> \
|
||||
<arg type="u" direction="in" /> \
|
||||
<arg type="ao" direction="in" /> \
|
||||
</method> \
|
||||
<method name="Close" /> \
|
||||
<signal name="ConfirmedLogout" /> \
|
||||
<signal name="ConfirmedReboot" /> \
|
||||
<signal name="ConfirmedShutdown" /> \
|
||||
<signal name="Canceled" /> \
|
||||
<signal name="Closed" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const logoutDialogContent = {
|
||||
subjectWithUser: C_("title", "Log Out %s"),
|
||||
subject: C_("title", "Log Out"),
|
||||
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
|
||||
uninhibitedDescriptionWithUser: function(user, seconds) {
|
||||
descriptionWithUser: function(user, seconds) {
|
||||
return ngettext("%s will be logged out automatically in %d second.",
|
||||
"%s will be logged out automatically in %d seconds.",
|
||||
seconds).format(user, seconds);
|
||||
},
|
||||
uninhibitedDescription: function(seconds) {
|
||||
description: function(seconds) {
|
||||
return ngettext("You will be logged out automatically in %d second.",
|
||||
"You will be logged out automatically in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
endDescription: _("Logging out of the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedLogout',
|
||||
label: C_("button", "Log Out") }],
|
||||
iconStyleClass: 'end-session-dialog-logout-icon'
|
||||
iconStyleClass: 'end-session-dialog-logout-icon',
|
||||
showOtherSessions: false,
|
||||
};
|
||||
|
||||
const shutdownDialogContent = {
|
||||
subject: C_("title", "Power Off"),
|
||||
inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
|
||||
uninhibitedDescription: function(seconds) {
|
||||
description: function(seconds) {
|
||||
return ngettext("The system will power off automatically in %d second.",
|
||||
"The system will power off automatically in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
endDescription: _("Powering off the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart") },
|
||||
{ signal: 'ConfirmedShutdown',
|
||||
label: C_("button", "Power Off") }],
|
||||
iconName: 'system-shutdown-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const restartDialogContent = {
|
||||
subject: C_("title", "Restart"),
|
||||
inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
|
||||
uninhibitedDescription: function(seconds) {
|
||||
description: function(seconds) {
|
||||
return ngettext("The system will restart automatically in %d second.",
|
||||
"The system will restart automatically in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
endDescription: _("Restarting the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart") }],
|
||||
iconName: 'view-refresh-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const restartInstallDialogContent = {
|
||||
|
||||
subject: C_("title", "Restart & Install Updates"),
|
||||
description: function(seconds) {
|
||||
return ngettext("The system will automatically restart and install updates in %d second.",
|
||||
"The system will automatically restart and install updates in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart & Install") }],
|
||||
iconName: 'view-refresh-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const DialogContent = {
|
||||
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
|
||||
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
|
||||
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent,
|
||||
3: restartInstallDialogContent
|
||||
};
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
|
||||
const LogindSessionIface = '<node> \
|
||||
<interface name="org.freedesktop.login1.Session"> \
|
||||
<property name="Id" type="s" access="read"/> \
|
||||
<property name="Remote" type="b" access="read"/> \
|
||||
<property name="Class" type="s" access="read"/> \
|
||||
<property name="Type" type="s" access="read"/> \
|
||||
<property name="State" type="s" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
|
||||
|
||||
function findAppFromInhibitor(inhibitor) {
|
||||
let [desktopFile] = inhibitor.GetAppIdSync();
|
||||
let desktopFile;
|
||||
try {
|
||||
[desktopFile] = inhibitor.GetAppIdSync();
|
||||
} catch(e) {
|
||||
// XXX -- sometimes JIT inhibitors generated by gnome-session
|
||||
// get removed too soon. Don't fail in this case.
|
||||
log('gnome-session gave us a dead inhibitor: %s'.format(inhibitor.get_object_path()));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
|
||||
desktopFile += '.desktop';
|
||||
@ -126,58 +162,6 @@ function findAppFromInhibitor(inhibitor) {
|
||||
return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
|
||||
}
|
||||
|
||||
const ListItem = new Lang.Class({
|
||||
Name: 'ListItem',
|
||||
|
||||
_init: function(app, reason) {
|
||||
this._app = app;
|
||||
this._reason = reason;
|
||||
|
||||
if (this._reason == null)
|
||||
this._reason = '';
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false});
|
||||
|
||||
this.actor = new St.Button({ style_class: 'end-session-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(_ITEM_ICON_SIZE);
|
||||
|
||||
let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
|
||||
child: this._icon });
|
||||
layout.add(iconBin);
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
|
||||
vertical: true });
|
||||
layout.add(textLayout);
|
||||
|
||||
this._nameLabel = new St.Label({ text: this._app.get_name(),
|
||||
style_class: 'end-session-dialog-app-list-item-name' });
|
||||
textLayout.add(this._nameLabel,
|
||||
{ expand: false,
|
||||
x_fill: true });
|
||||
|
||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
||||
style_class: 'end-session-dialog-app-list-item-description' });
|
||||
this.actor.label_actor = this._nameLabel;
|
||||
textLayout.add(this._descriptionLabel,
|
||||
{ expand: true,
|
||||
x_fill: true });
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
this._app.activate();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ListItem.prototype);
|
||||
|
||||
// The logout timer only shows updates every 10 seconds
|
||||
// until the last 10 seconds, then it shows updates every
|
||||
// second. This function takes a given time and returns
|
||||
@ -228,22 +212,23 @@ const EndSessionDialog = new Lang.Class({
|
||||
this.parent({ styleClass: 'end-session-dialog',
|
||||
destroyOnClose: false });
|
||||
|
||||
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
this._updatesFile = Gio.File.new_for_path('/system-update');
|
||||
|
||||
this._secondsLeft = 0;
|
||||
this._totalSecondsToStayOpen = 0;
|
||||
this._inhibitors = [];
|
||||
this._applications = [];
|
||||
this._sessions = [];
|
||||
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
this.connect('opened',
|
||||
Lang.bind(this, this._onOpened));
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||
Lang.bind(this, this._updateContent));
|
||||
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._updateContent));
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded', Lang.bind(this, this._sync));
|
||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._sync));
|
||||
|
||||
let mainContentLayout = new St.BoxLayout({ vertical: false });
|
||||
this.contentLayout.add(mainContentLayout,
|
||||
@ -275,28 +260,30 @@ const EndSessionDialog = new Lang.Class({
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
this.contentLayout.add(scrollView,
|
||||
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.contentLayout.add(this._scrollView,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
scrollView.hide();
|
||||
this._scrollView.hide();
|
||||
|
||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(this._applicationList);
|
||||
this._inhibitorSection = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'end-session-dialog-inhibitor-layout' });
|
||||
this._scrollView.add_actor(this._inhibitorSection);
|
||||
|
||||
this._applicationList.connect('actor-added',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_n_children() == 1)
|
||||
scrollView.show();
|
||||
}));
|
||||
this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
|
||||
text: _("Some applications are busy or have unsaved work.") });
|
||||
this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list',
|
||||
vertical: true });
|
||||
this._inhibitorSection.add_actor(this._applicationHeader);
|
||||
this._inhibitorSection.add_actor(this._applicationList);
|
||||
|
||||
this._applicationList.connect('actor-removed',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_n_children() == 0)
|
||||
scrollView.hide();
|
||||
}));
|
||||
this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
|
||||
text: _("Other users are logged in.") });
|
||||
this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list',
|
||||
vertical: true });
|
||||
this._inhibitorSection.add_actor(this._sessionHeader);
|
||||
this._inhibitorSection.add_actor(this._sessionList);
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
|
||||
@ -307,52 +294,42 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_updateDescription: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
_sync: function() {
|
||||
let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED);
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
if (this._type == 2 && this._updatesFile.query_exists(null))
|
||||
this._type = 3;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
let subject = dialogContent.subject;
|
||||
|
||||
let description;
|
||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||
this._secondsLeft,
|
||||
10);
|
||||
|
||||
if (this._inhibitors.length > 0) {
|
||||
this._stopTimer();
|
||||
description = dialogContent.inhibitedDescription;
|
||||
} else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
|
||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||
this._secondsLeft,
|
||||
10);
|
||||
if (this._user.is_loaded) {
|
||||
let realName = this._user.get_real_name();
|
||||
|
||||
if (this._user.is_loaded) {
|
||||
let realName = this._user.get_real_name();
|
||||
if (realName != null) {
|
||||
if (dialogContent.subjectWithUser)
|
||||
subject = dialogContent.subjectWithUser.format(realName);
|
||||
|
||||
if (realName != null) {
|
||||
if (dialogContent.subjectWithUser)
|
||||
subject = dialogContent.subjectWithUser.format(realName);
|
||||
|
||||
if (dialogContent.uninhibitedDescriptionWithUser)
|
||||
description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime);
|
||||
else
|
||||
description = dialogContent.uninhibitedDescription(displayTime);
|
||||
}
|
||||
if (dialogContent.descriptionWithUser)
|
||||
description = dialogContent.descriptionWithUser(realName, displayTime);
|
||||
else
|
||||
description = dialogContent.description(displayTime);
|
||||
}
|
||||
|
||||
if (!description)
|
||||
description = dialogContent.uninhibitedDescription(displayTime);
|
||||
} else {
|
||||
description = dialogContent.endDescription;
|
||||
}
|
||||
|
||||
_setLabelText(this._subjectLabel, subject);
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
},
|
||||
if (!description)
|
||||
description = dialogContent.description(displayTime);
|
||||
|
||||
_updateContent: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
_setLabelText(this._subjectLabel, subject);
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
if (dialogContent.iconName) {
|
||||
@ -367,7 +344,11 @@ const EndSessionDialog = new Lang.Class({
|
||||
avatarWidget.update();
|
||||
}
|
||||
|
||||
this._updateDescription();
|
||||
let hasApplications = this._applications.length > 0;
|
||||
let hasSessions = this._sessions.length > 0;
|
||||
this._scrollView.visible = hasApplications || hasSessions;
|
||||
this._applicationHeader.visible = hasApplications;
|
||||
this._sessionHeader.visible = hasSessions;
|
||||
},
|
||||
|
||||
_updateButtons: function() {
|
||||
@ -413,14 +394,12 @@ const EndSessionDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
if (this._inhibitors.length == 0)
|
||||
this._startTimer();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_startTimer: function() {
|
||||
let startTime = GLib.get_monotonic_time();
|
||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||
this._updateDescription();
|
||||
|
||||
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
|
||||
function() {
|
||||
@ -429,7 +408,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
|
||||
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
|
||||
if (this._secondsLeft > 0) {
|
||||
this._updateDescription();
|
||||
this._sync();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -442,7 +421,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_stopTimer: function() {
|
||||
if (this._timerId != 0) {
|
||||
if (this._timerId > 0) {
|
||||
Mainloop.source_remove(this._timerId);
|
||||
this._timerId = 0;
|
||||
}
|
||||
@ -450,8 +429,33 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._secondsLeft = 0;
|
||||
},
|
||||
|
||||
_constructListItemForApp: function(inhibitor, app) {
|
||||
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item',
|
||||
can_focus: true });
|
||||
actor.add(app.create_icon_texture(_ITEM_ICON_SIZE));
|
||||
|
||||
let textLayout = new St.BoxLayout({ vertical: true,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
actor.add(textLayout);
|
||||
|
||||
let nameLabel = new St.Label({ text: app.get_name(),
|
||||
style_class: 'end-session-dialog-app-list-item-name' });
|
||||
textLayout.add(nameLabel);
|
||||
actor.label_actor = nameLabel;
|
||||
|
||||
let [reason] = inhibitor.GetReasonSync();
|
||||
if (reason) {
|
||||
let reasonLabel = new St.Label({ text: reason,
|
||||
style_class: 'end-session-dialog-app-list-item-description' });
|
||||
textLayout.add(reasonLabel);
|
||||
}
|
||||
|
||||
return actor;
|
||||
},
|
||||
|
||||
_onInhibitorLoaded: function(inhibitor) {
|
||||
if (this._inhibitors.indexOf(inhibitor) < 0) {
|
||||
if (this._applications.indexOf(inhibitor) < 0) {
|
||||
// Stale inhibitor
|
||||
return;
|
||||
}
|
||||
@ -459,29 +463,92 @@ const EndSessionDialog = new Lang.Class({
|
||||
let app = findAppFromInhibitor(inhibitor);
|
||||
|
||||
if (app) {
|
||||
let [reason] = inhibitor.GetReasonSync();
|
||||
let item = new ListItem(app, reason);
|
||||
item.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
this.close();
|
||||
}));
|
||||
this._applicationList.add(item.actor, { x_fill: true });
|
||||
this._stopTimer();
|
||||
let actor = this._constructListItemForApp(inhibitor, app);
|
||||
this._applicationList.add(actor);
|
||||
} else {
|
||||
// inhibiting app is a service, not an application
|
||||
this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
|
||||
this._applications.splice(this._applications.indexOf(inhibitor), 1);
|
||||
}
|
||||
|
||||
this._updateContent();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_constructListItemForSession: function(session) {
|
||||
let avatar = new UserWidget.Avatar(session.user, { iconSize: _ITEM_ICON_SIZE });
|
||||
avatar.update();
|
||||
|
||||
let userName = session.user.get_real_name() ? session.user.get_real_name() : session.username;
|
||||
let userLabelText;
|
||||
|
||||
if (session.remote)
|
||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||
userLabelText = _("%s (remote)").format(userName);
|
||||
else if (session.type == "tty")
|
||||
/* Translators: Console here refers to a tty like a VT console */
|
||||
userLabelText = _("%s (console)").format(userName);
|
||||
else
|
||||
userLabelText = userName;
|
||||
|
||||
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
|
||||
can_focus: true });
|
||||
actor.add(avatar.actor);
|
||||
|
||||
let nameLabel = new St.Label({ text: userLabelText,
|
||||
style_class: 'end-session-dialog-session-list-item-name',
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
actor.add(nameLabel);
|
||||
actor.label_actor = nameLabel;
|
||||
|
||||
return actor;
|
||||
},
|
||||
|
||||
_loadSessions: function() {
|
||||
this._loginManager.listSessions(Lang.bind(this, function(result) {
|
||||
let n = 0;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||
let proxy = new LogindSession(Gio.DBus.system, 'org.freedesktop.login1', sessionPath);
|
||||
|
||||
if (proxy.Class != 'user')
|
||||
continue;
|
||||
|
||||
if (proxy.State == 'closing')
|
||||
continue;
|
||||
|
||||
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||
continue;
|
||||
|
||||
let session = { user: this._userManager.get_user(userName),
|
||||
username: userName,
|
||||
type: proxy.Type,
|
||||
remote: proxy.Remote };
|
||||
this._sessions.push(session);
|
||||
|
||||
let actor = this._constructListItemForSession(session);
|
||||
this._sessionList.add(actor);
|
||||
|
||||
// limit the number of entries
|
||||
n++;
|
||||
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||
break;
|
||||
}
|
||||
|
||||
this._sync();
|
||||
}));
|
||||
},
|
||||
|
||||
OpenAsync: function(parameters, invocation) {
|
||||
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._inhibitors = [];
|
||||
this._applicationList.destroy_all_children();
|
||||
this._type = type;
|
||||
|
||||
this._applications = [];
|
||||
this._applicationList.destroy_all_children();
|
||||
|
||||
this._sessions = [];
|
||||
this._sessionList.destroy_all_children();
|
||||
|
||||
if (!(this._type in DialogContent)) {
|
||||
invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
|
||||
"Unknown dialog type requested");
|
||||
@ -493,9 +560,12 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._onInhibitorLoaded(proxy);
|
||||
}));
|
||||
|
||||
this._inhibitors.push(inhibitor);
|
||||
this._applications.push(inhibitor);
|
||||
}
|
||||
|
||||
if (DialogContent[type].showOtherSessions)
|
||||
this._loadSessions();
|
||||
|
||||
this._updateButtons();
|
||||
|
||||
if (!this.open(timestamp)) {
|
||||
@ -504,7 +574,8 @@ const EndSessionDialog = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateContent();
|
||||
this._startTimer();
|
||||
this._sync();
|
||||
|
||||
let signalId = this.connect('opened',
|
||||
Lang.bind(this, function() {
|
||||
|
@ -76,7 +76,11 @@ function disableExtension(uuid) {
|
||||
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||
}
|
||||
|
||||
extension.stateObj.disable();
|
||||
try {
|
||||
extension.stateObj.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
let uuid = order[i];
|
||||
@ -89,8 +93,10 @@ function disableExtension(uuid) {
|
||||
|
||||
extensionOrder.splice(orderIdx, 1);
|
||||
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
if ( extension.state != ExtensionState.ERROR ) {
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
}
|
||||
|
||||
function enableExtension(uuid) {
|
||||
@ -117,10 +123,15 @@ function enableExtension(uuid) {
|
||||
}
|
||||
}
|
||||
|
||||
extension.stateObj.enable();
|
||||
|
||||
extension.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
try {
|
||||
extension.stateObj.enable();
|
||||
extension.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
return;
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function logExtensionError(uuid, error) {
|
||||
@ -150,7 +161,8 @@ function loadExtension(extension) {
|
||||
} else {
|
||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||
if (enabled) {
|
||||
initExtension(extension.uuid);
|
||||
if (!initExtension(extension.uuid))
|
||||
return;
|
||||
if (extension.state == ExtensionState.DISABLED)
|
||||
enableExtension(extension.uuid);
|
||||
} else {
|
||||
@ -205,7 +217,12 @@ function initExtension(uuid) {
|
||||
extensionModule = extension.imports.extension;
|
||||
|
||||
if (extensionModule.init) {
|
||||
extensionState = extensionModule.init(extension);
|
||||
try {
|
||||
extensionState = extensionModule.init(extension);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!extensionState)
|
||||
@ -214,6 +231,7 @@ function initExtension(uuid) {
|
||||
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-loaded', uuid);
|
||||
return true;
|
||||
}
|
||||
|
||||
function getEnabledExtensions() {
|
||||
@ -235,11 +253,7 @@ function onEnabledExtensionsChanged() {
|
||||
newEnabledExtensions.filter(function(uuid) {
|
||||
return enabledExtensions.indexOf(uuid) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
try {
|
||||
enableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||
@ -247,11 +261,7 @@ function onEnabledExtensionsChanged() {
|
||||
enabledExtensions.filter(function(item) {
|
||||
return newEnabledExtensions.indexOf(item) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
try {
|
||||
disableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
@ -263,11 +273,7 @@ function _loadExtensions() {
|
||||
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', function(signals, extension) {
|
||||
try {
|
||||
loadExtension(extension);
|
||||
} catch(e) {
|
||||
logExtensionError(extension.uuid, e);
|
||||
}
|
||||
loadExtension(extension);
|
||||
});
|
||||
finder.scanExtensions();
|
||||
}
|
||||
|
65
js/ui/focusCaretTracker.js
Normal file
@ -0,0 +1,65 @@
|
||||
/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* Copyright 2012 Inclusive Design Research Centre, OCAD University.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author:
|
||||
* Joseph Scheuhammer <clown@alum.mit.edu>
|
||||
* Contributor:
|
||||
* Magdalen Berns <m.berns@sms.ed.ac.uk>
|
||||
*/
|
||||
|
||||
const Atspi = imports.gi.Atspi;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const CARETMOVED = 'object:text-caret-moved';
|
||||
const STATECHANGED = 'object:state-changed';
|
||||
|
||||
const FocusCaretTracker = new Lang.Class({
|
||||
Name: 'FocusCaretTracker',
|
||||
|
||||
_init: function() {
|
||||
Atspi.init();
|
||||
Atspi.set_timeout(250, 250);
|
||||
this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
|
||||
},
|
||||
|
||||
_onChanged: function(event) {
|
||||
if (event.type.indexOf(STATECHANGED) == 0)
|
||||
this.emit('focus-changed', event);
|
||||
else if (event.type == CARETMOVED)
|
||||
this.emit('caret-moved', event);
|
||||
},
|
||||
|
||||
registerFocusListener: function() {
|
||||
return this._atspiListener.register(STATECHANGED + ':focused') &&
|
||||
this._atspiListener.register(STATECHANGED + ':selected');
|
||||
},
|
||||
|
||||
registerCaretListener: function() {
|
||||
return this._atspiListener.register(CARETMOVED);
|
||||
},
|
||||
|
||||
deregisterFocusListener: function() {
|
||||
return this._atspiListener.deregister(STATECHANGED + ':focused') &&
|
||||
this._atspiListener.deregister(STATECHANGED + ':selected');
|
||||
},
|
||||
|
||||
deregisterCaretListener: function() {
|
||||
return this._atspiListener.deregister(CARETMOVED);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(FocusCaretTracker.prototype);
|
@ -10,6 +10,31 @@ const St = imports.gi.St;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
let _capturedEventId = 0;
|
||||
let _grabHelperStack = [];
|
||||
function _onCapturedEvent(actor, event) {
|
||||
let grabHelper = _grabHelperStack[_grabHelperStack.length - 1];
|
||||
return grabHelper.onCapturedEvent(event);
|
||||
}
|
||||
|
||||
function _pushGrabHelper(grabHelper) {
|
||||
_grabHelperStack.push(grabHelper);
|
||||
|
||||
if (_capturedEventId == 0)
|
||||
_capturedEventId = global.stage.connect('captured-event', _onCapturedEvent);
|
||||
}
|
||||
|
||||
function _popGrabHelper(grabHelper) {
|
||||
let poppedHelper = _grabHelperStack.pop();
|
||||
if (poppedHelper != grabHelper)
|
||||
throw new Error("incorrect grab helper pop");
|
||||
|
||||
if (_grabHelperStack.length == 0) {
|
||||
global.stage.disconnect(_capturedEventId);
|
||||
_capturedEventId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// GrabHelper:
|
||||
// @owner: the actor that owns the GrabHelper
|
||||
// @params: optional parameters to pass to Main.pushModal()
|
||||
@ -31,7 +56,6 @@ const GrabHelper = new Lang.Class({
|
||||
this._grabStack = [];
|
||||
|
||||
this._actors = [];
|
||||
this._capturedEventId = 0;
|
||||
this._ignoreRelease = false;
|
||||
|
||||
this._modalCount = 0;
|
||||
@ -177,7 +201,7 @@ const GrabHelper = new Lang.Class({
|
||||
if (!Main.pushModal(this._owner, this._modalParams))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
_pushGrabHelper(this);
|
||||
}
|
||||
|
||||
this._modalCount++;
|
||||
@ -189,8 +213,7 @@ const GrabHelper = new Lang.Class({
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
_popGrabHelper(this);
|
||||
|
||||
this._ignoreRelease = false;
|
||||
|
||||
@ -251,7 +274,7 @@ const GrabHelper = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
onCapturedEvent: function(event) {
|
||||
let type = event.type();
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
|
@ -1,14 +1,20 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ICON_SIZE = 48;
|
||||
const ICON_SIZE = 96;
|
||||
const MIN_ICON_SIZE = 16;
|
||||
|
||||
const EXTRA_SPACE_ANIMATION_TIME = 0.25;
|
||||
|
||||
const BaseIcon = new Lang.Class({
|
||||
Name: 'BaseIcon',
|
||||
@ -17,7 +23,12 @@ const BaseIcon = new Lang.Class({
|
||||
params = Params.parse(params, { createIcon: null,
|
||||
setSizeManually: false,
|
||||
showLabel: true });
|
||||
this.actor = new St.Bin({ style_class: 'overview-icon',
|
||||
|
||||
let styleClass = 'overview-icon';
|
||||
if (params.showLabel)
|
||||
styleClass += ' overview-icon-with-label';
|
||||
|
||||
this.actor = new St.Bin({ style_class: styleClass,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
@ -176,19 +187,31 @@ const IconGrid = new Lang.Class({
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { rowLimit: null,
|
||||
columnLimit: null,
|
||||
minRows: 1,
|
||||
minColumns: 1,
|
||||
fillParent: false,
|
||||
xAlign: St.Align.MIDDLE });
|
||||
xAlign: St.Align.MIDDLE,
|
||||
padWithSpacing: false });
|
||||
this._rowLimit = params.rowLimit;
|
||||
this._colLimit = params.columnLimit;
|
||||
this._minRows = params.minRows;
|
||||
this._minColumns = params.minColumns;
|
||||
this._xAlign = params.xAlign;
|
||||
this._fillParent = params.fillParent;
|
||||
this._padWithSpacing = params.padWithSpacing;
|
||||
|
||||
this.topPadding = 0;
|
||||
this.bottomPadding = 0;
|
||||
this.rightPadding = 0;
|
||||
this.leftPadding = 0;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
||||
vertical: true });
|
||||
|
||||
this._items = [];
|
||||
// Pulled from CSS, but hardcode some defaults here
|
||||
this._spacing = 0;
|
||||
this._hItemSize = this._vItemSize = ICON_SIZE;
|
||||
this._fixedHItemSize = this._fixedVItemSize = undefined;
|
||||
this._grid = new Shell.GenericContainer();
|
||||
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
@ -204,16 +227,16 @@ const IconGrid = new Lang.Class({
|
||||
// later we'll allocate as many children as fit the parent
|
||||
return;
|
||||
|
||||
let children = this._grid.get_children();
|
||||
let nChildren = this._grid.get_n_children();
|
||||
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
||||
children.length)
|
||||
: children.length;
|
||||
let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
|
||||
nChildren)
|
||||
: nChildren;
|
||||
let totalSpacing = Math.max(0, nColumns - 1) * this._getSpacing();
|
||||
// Kind of a lie, but not really an issue right now. If
|
||||
// we wanted to support some sort of hidden/overflow that would
|
||||
// need higher level design
|
||||
alloc.min_size = this._hItemSize;
|
||||
alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
|
||||
alloc.min_size = this._getHItemSize() + this.leftPadding + this.rightPadding;
|
||||
alloc.natural_size = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding;
|
||||
},
|
||||
|
||||
_getVisibleChildren: function() {
|
||||
@ -231,13 +254,11 @@ const IconGrid = new Lang.Class({
|
||||
return;
|
||||
|
||||
let children = this._getVisibleChildren();
|
||||
let nColumns, spacing;
|
||||
if (forWidth < 0) {
|
||||
let nColumns;
|
||||
if (forWidth < 0)
|
||||
nColumns = children.length;
|
||||
spacing = this._spacing;
|
||||
} else {
|
||||
[nColumns, , spacing] = this._computeLayout(forWidth);
|
||||
}
|
||||
else
|
||||
[nColumns, ] = this._computeLayout(forWidth);
|
||||
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
@ -246,8 +267,8 @@ const IconGrid = new Lang.Class({
|
||||
nRows = 0;
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
let totalSpacing = Math.max(0, nRows - 1) * spacing;
|
||||
let height = nRows * this._vItemSize + totalSpacing;
|
||||
let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing();
|
||||
let height = nRows * this._getVItemSize() + totalSpacing + this.topPadding + this.bottomPadding;
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
},
|
||||
@ -263,48 +284,30 @@ const IconGrid = new Lang.Class({
|
||||
let children = this._getVisibleChildren();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
let spacing = this._getSpacing();
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||
|
||||
let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
|
||||
|
||||
let leftPadding;
|
||||
let leftEmptySpace;
|
||||
switch(this._xAlign) {
|
||||
case St.Align.START:
|
||||
leftPadding = 0;
|
||||
leftEmptySpace = 0;
|
||||
break;
|
||||
case St.Align.MIDDLE:
|
||||
leftPadding = Math.floor((availWidth - usedWidth) / 2);
|
||||
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
|
||||
break;
|
||||
case St.Align.END:
|
||||
leftPadding = availWidth - usedWidth;
|
||||
leftEmptySpace = availWidth - usedWidth;
|
||||
}
|
||||
|
||||
let x = box.x1 + leftPadding;
|
||||
let y = box.y1;
|
||||
let x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||
let y = box.y1 + this.topPadding;
|
||||
let columnIndex = 0;
|
||||
let rowIndex = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
|
||||
= children[i].get_preferred_size();
|
||||
|
||||
/* Center the item in its allocation horizontally */
|
||||
let width = Math.min(this._hItemSize, childNaturalWidth);
|
||||
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
||||
let height = Math.min(this._vItemSize, childNaturalHeight);
|
||||
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||
let _x = box.x2 - (x + width);
|
||||
childBox.x1 = Math.floor(_x - childXSpacing);
|
||||
} else {
|
||||
childBox.x1 = Math.floor(x + childXSpacing);
|
||||
}
|
||||
childBox.y1 = Math.floor(y + childYSpacing);
|
||||
childBox.x2 = childBox.x1 + width;
|
||||
childBox.y2 = childBox.y1 + height;
|
||||
let childBox = this._calculateChildBox(children[i], x, y, box);
|
||||
|
||||
if (this._rowLimit && rowIndex >= this._rowLimit ||
|
||||
this._fillParent && childBox.y2 > availHeight) {
|
||||
this._fillParent && childBox.y2 > availHeight - this.bottomPadding) {
|
||||
this._grid.set_skip_paint(children[i], true);
|
||||
} else {
|
||||
children[i].allocate(childBox, flags);
|
||||
@ -318,15 +321,38 @@ const IconGrid = new Lang.Class({
|
||||
}
|
||||
|
||||
if (columnIndex == 0) {
|
||||
y += this._vItemSize + spacing;
|
||||
x = box.x1 + leftPadding;
|
||||
y += this._getVItemSize() + spacing;
|
||||
x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||
} else {
|
||||
x += this._hItemSize + spacing;
|
||||
x += this._getHItemSize() + spacing;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
childrenInRow: function(rowWidth) {
|
||||
_calculateChildBox: function(child, x, y, box) {
|
||||
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
|
||||
child.get_preferred_size();
|
||||
|
||||
/* Center the item in its allocation horizontally */
|
||||
let width = Math.min(this._getHItemSize(), childNaturalWidth);
|
||||
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
||||
let height = Math.min(this._getVItemSize(), childNaturalHeight);
|
||||
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||
let _x = box.x2 - (x + width);
|
||||
childBox.x1 = Math.floor(_x - childXSpacing);
|
||||
} else {
|
||||
childBox.x1 = Math.floor(x + childXSpacing);
|
||||
}
|
||||
childBox.y1 = Math.floor(y + childYSpacing);
|
||||
childBox.x2 = childBox.x1 + width;
|
||||
childBox.y2 = childBox.y1 + height;
|
||||
return childBox;
|
||||
},
|
||||
|
||||
columnsForWidth: function(rowWidth) {
|
||||
return this._computeLayout(rowWidth)[0];
|
||||
},
|
||||
|
||||
@ -336,26 +362,19 @@ const IconGrid = new Lang.Class({
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
let spacing = this._spacing;
|
||||
|
||||
if (this._colLimit) {
|
||||
let itemWidth = this._hItemSize * this._colLimit;
|
||||
let emptyArea = forWidth - itemWidth;
|
||||
spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
|
||||
spacing = Math.round(spacing);
|
||||
}
|
||||
let usedWidth = this.leftPadding + this.rightPadding;
|
||||
let spacing = this._getSpacing();
|
||||
|
||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||
(usedWidth + this._hItemSize <= forWidth)) {
|
||||
usedWidth += this._hItemSize + spacing;
|
||||
(usedWidth + this._getHItemSize() <= forWidth)) {
|
||||
usedWidth += this._getHItemSize() + spacing;
|
||||
nColumns += 1;
|
||||
}
|
||||
|
||||
if (nColumns > 0)
|
||||
usedWidth -= spacing;
|
||||
|
||||
return [nColumns, usedWidth, spacing];
|
||||
return [nColumns, usedWidth];
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
@ -366,15 +385,49 @@ const IconGrid = new Lang.Class({
|
||||
this._grid.queue_relayout();
|
||||
},
|
||||
|
||||
nRows: function(forWidth) {
|
||||
let children = this._getVisibleChildren();
|
||||
let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0];
|
||||
let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0;
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
return nRows;
|
||||
},
|
||||
|
||||
rowsForHeight: function(forHeight) {
|
||||
return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._getVItemSize() + this._getSpacing()));
|
||||
},
|
||||
|
||||
usedHeightForNRows: function(nRows) {
|
||||
return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding;
|
||||
},
|
||||
|
||||
usedWidth: function(forWidth) {
|
||||
return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
|
||||
},
|
||||
|
||||
usedWidthForNColumns: function(columns) {
|
||||
let usedWidth = columns * (this._getHItemSize() + this._getSpacing());
|
||||
usedWidth -= this._getSpacing();
|
||||
return usedWidth + this.leftPadding + this.rightPadding;
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
this._items = [];
|
||||
this._grid.destroy_all_children();
|
||||
},
|
||||
|
||||
addItem: function(actor, index) {
|
||||
addItem: function(item, index) {
|
||||
if (!item.icon || !item.icon instanceof BaseIcon) {
|
||||
log('Only items with a BaseIcon icon property can be added to IconGrid');
|
||||
return;
|
||||
}
|
||||
|
||||
this._items.push(item);
|
||||
if (index !== undefined)
|
||||
this._grid.insert_child_at_index(actor, index);
|
||||
this._grid.insert_child_at_index(item.actor, index);
|
||||
else
|
||||
this._grid.add_actor(actor);
|
||||
this._grid.add_actor(item.actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
@ -383,5 +436,311 @@ const IconGrid = new Lang.Class({
|
||||
|
||||
visibleItemsCount: function() {
|
||||
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
|
||||
},
|
||||
|
||||
setSpacing: function(spacing) {
|
||||
this._fixedSpacing = spacing;
|
||||
},
|
||||
|
||||
_getSpacing: function() {
|
||||
return this._fixedSpacing ? this._fixedSpacing : this._spacing;
|
||||
},
|
||||
|
||||
_getHItemSize: function() {
|
||||
return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
|
||||
},
|
||||
|
||||
_getVItemSize: function() {
|
||||
return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
|
||||
},
|
||||
|
||||
_updateSpacingForSize: function(availWidth, availHeight) {
|
||||
let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
|
||||
let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
|
||||
let maxHSpacing, maxVSpacing;
|
||||
|
||||
if (this._padWithSpacing) {
|
||||
// minRows + 1 because we want to put spacing before the first row, so it is like we have one more row
|
||||
// to divide the empty space
|
||||
maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows +1));
|
||||
maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns +1));
|
||||
} else {
|
||||
if (this._minRows <= 1)
|
||||
maxVSpacing = maxEmptyVArea;
|
||||
else
|
||||
maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows - 1));
|
||||
|
||||
if (this._minColumns <= 1)
|
||||
maxHSpacing = maxEmptyHArea;
|
||||
else
|
||||
maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns - 1));
|
||||
}
|
||||
|
||||
let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
|
||||
// Limit spacing to the item size
|
||||
maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
|
||||
// The minimum spacing, regardless of whether it satisfies the row/columng minima,
|
||||
// is the spacing we get from CSS.
|
||||
let spacing = Math.max(this._spacing, maxSpacing);
|
||||
this.setSpacing(spacing);
|
||||
if (this._padWithSpacing)
|
||||
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
|
||||
},
|
||||
|
||||
/**
|
||||
* This function must to be called before iconGrid allocation,
|
||||
* to know how much spacing can the grid has
|
||||
*/
|
||||
adaptToSize: function(availWidth, availHeight) {
|
||||
this._fixedHItemSize = this._hItemSize;
|
||||
this._fixedVItemSize = this._vItemSize;
|
||||
this._updateSpacingForSize(availWidth, availHeight);
|
||||
let spacing = this._getSpacing();
|
||||
|
||||
if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) < this._minRows) {
|
||||
let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth ;
|
||||
let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight ;
|
||||
|
||||
let neededSpacePerItem = (neededWidth > neededHeight) ? Math.ceil(neededWidth / this._minColumns)
|
||||
: Math.ceil(neededHeight / this._minRows);
|
||||
this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
|
||||
this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
|
||||
|
||||
if (this._fixedHItemSize < MIN_ICON_SIZE)
|
||||
this._fixedHItemSize = MIN_ICON_SIZE;
|
||||
if (this._fixedVItemSize < MIN_ICON_SIZE)
|
||||
this._fixedVItemSize = MIN_ICON_SIZE;
|
||||
|
||||
this._updateSpacingForSize(availWidth, availHeight);
|
||||
}
|
||||
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateChildrenScale(scale); }));
|
||||
},
|
||||
|
||||
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
|
||||
_updateChildrenScale: function(scale) {
|
||||
for (let i in this._items) {
|
||||
let newIconSize = Math.floor(ICON_SIZE * scale);
|
||||
this._items[i].icon.setIconSize(newIconSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PaginatedIconGrid = new Lang.Class({
|
||||
Name: 'PaginatedIconGrid',
|
||||
Extends: IconGrid,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
this._nPages = 0;
|
||||
this._rowsPerPage = 0;
|
||||
this._spaceBetweenPages = 0;
|
||||
this._childrenPerPage = 0;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
alloc.min_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
|
||||
alloc.natural_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
if (this._childrenPerPage == 0)
|
||||
log('computePages() must be called before allocate(); pagination will not work.');
|
||||
|
||||
if (this._fillParent) {
|
||||
// Reset the passed in box to fill the parent
|
||||
let parentBox = this.actor.get_parent().allocation;
|
||||
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
|
||||
box = this._grid.get_theme_node().get_content_box(gridBox);
|
||||
}
|
||||
let children = this._getVisibleChildren();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
let spacing = this._getSpacing();
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||
|
||||
let leftEmptySpace;
|
||||
switch(this._xAlign) {
|
||||
case St.Align.START:
|
||||
leftEmptySpace = 0;
|
||||
break;
|
||||
case St.Align.MIDDLE:
|
||||
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
|
||||
break;
|
||||
case St.Align.END:
|
||||
leftEmptySpace = availWidth - usedWidth;
|
||||
}
|
||||
|
||||
let x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||
let y = box.y1 + this.topPadding;
|
||||
let columnIndex = 0;
|
||||
let rowIndex = 0;
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let childBox = this._calculateChildBox(children[i], x, y, box);
|
||||
children[i].allocate(childBox, flags);
|
||||
this._grid.set_skip_paint(children[i], false);
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex == nColumns) {
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
}
|
||||
if (columnIndex == 0) {
|
||||
y += this._getVItemSize() + spacing;
|
||||
if ((i + 1) % this._childrenPerPage == 0)
|
||||
y += this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
|
||||
x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||
} else
|
||||
x += this._getHItemSize() + spacing;
|
||||
}
|
||||
},
|
||||
|
||||
_computePages: function (availWidthPerPage, availHeightPerPage) {
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
|
||||
let nRows;
|
||||
let children = this._getVisibleChildren();
|
||||
if (nColumns > 0)
|
||||
nRows = Math.ceil(children.length / nColumns);
|
||||
else
|
||||
nRows = 0;
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
|
||||
let spacing = this._getSpacing();
|
||||
// We want to contain the grid inside the parent box with padding
|
||||
this._rowsPerPage = this.rowsForHeight(availHeightPerPage);
|
||||
this._nPages = Math.ceil(nRows / this._rowsPerPage);
|
||||
this._spaceBetweenPages = availHeightPerPage - (this.topPadding + this.bottomPadding) - this._availableHeightPerPageForItems();
|
||||
this._childrenPerPage = nColumns * this._rowsPerPage;
|
||||
},
|
||||
|
||||
adaptToSize: function(availWidth, availHeight) {
|
||||
this.parent(availWidth, availHeight);
|
||||
this._computePages(availWidth, availHeight);
|
||||
},
|
||||
|
||||
_availableHeightPerPageForItems: function() {
|
||||
return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
|
||||
},
|
||||
|
||||
nPages: function() {
|
||||
return this._nPages;
|
||||
},
|
||||
|
||||
getPageY: function(pageNumber) {
|
||||
if (!this._nPages)
|
||||
return 0;
|
||||
|
||||
let firstPageItem = pageNumber * this._childrenPerPage
|
||||
let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
|
||||
return childBox.y1 - this.topPadding;
|
||||
},
|
||||
|
||||
getItemPage: function(item) {
|
||||
let children = this._getVisibleChildren();
|
||||
let index = children.indexOf(item);
|
||||
if (index == -1) {
|
||||
throw new Error('Item not found.');
|
||||
return 0;
|
||||
}
|
||||
return Math.floor(index / this._childrenPerPage);
|
||||
},
|
||||
|
||||
/**
|
||||
* openExtraSpace:
|
||||
* @sourceItem: the item for which to create extra space
|
||||
* @side: where @sourceItem should be located relative to the created space
|
||||
* @nRows: the amount of space to create
|
||||
*
|
||||
* Pan view to create extra space for @nRows above or below @sourceItem.
|
||||
*/
|
||||
openExtraSpace: function(sourceItem, side, nRows) {
|
||||
let children = this._getVisibleChildren();
|
||||
let index = children.indexOf(sourceItem.actor);
|
||||
if (index == -1) {
|
||||
throw new Error('Item not found.');
|
||||
return;
|
||||
}
|
||||
let pageIndex = Math.floor(index / this._childrenPerPage);
|
||||
let pageOffset = pageIndex * this._childrenPerPage;
|
||||
|
||||
let childrenPerRow = this._childrenPerPage / this._rowsPerPage;
|
||||
let sourceRow = Math.floor((index - pageOffset) / childrenPerRow);
|
||||
|
||||
let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1
|
||||
: sourceRow;
|
||||
let nRowsBelow = this._rowsPerPage - nRowsAbove;
|
||||
|
||||
let nRowsUp, nRowsDown;
|
||||
if (side == St.Side.TOP) {
|
||||
nRowsDown = Math.min(nRowsBelow, nRows);
|
||||
nRowsUp = nRows - nRowsDown;
|
||||
} else {
|
||||
nRowsUp = Math.min(nRowsAbove, nRows);
|
||||
nRowsDown = nRows - nRowsUp;
|
||||
}
|
||||
|
||||
let childrenDown = children.splice(pageOffset +
|
||||
nRowsAbove * childrenPerRow,
|
||||
nRowsBelow * childrenPerRow);
|
||||
let childrenUp = children.splice(pageOffset,
|
||||
nRowsAbove * childrenPerRow);
|
||||
|
||||
// Special case: On the last row with no rows below the icon,
|
||||
// there's no need to move any rows either up or down
|
||||
if (childrenDown.length == 0 && nRowsUp == 0) {
|
||||
this._translatedChildren = [];
|
||||
this.emit('space-opened');
|
||||
} else {
|
||||
this._translateChildren(childrenUp, Gtk.DirectionType.UP, nRowsUp);
|
||||
this._translateChildren(childrenDown, Gtk.DirectionType.DOWN, nRowsDown);
|
||||
this._translatedChildren = childrenUp.concat(childrenDown);
|
||||
}
|
||||
},
|
||||
|
||||
_translateChildren: function(children, direction, nRows) {
|
||||
let translationY = nRows * (this._getVItemSize() + this._getSpacing());
|
||||
if (translationY == 0)
|
||||
return;
|
||||
|
||||
if (direction == Gtk.DirectionType.UP)
|
||||
translationY *= -1;
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
children[i].translation_y = 0;
|
||||
let params = { translation_y: translationY,
|
||||
time: EXTRA_SPACE_ANIMATION_TIME,
|
||||
transition: 'easeInOutQuad'
|
||||
};
|
||||
if (i == (children.length - 1))
|
||||
params.onComplete = Lang.bind(this,
|
||||
function() {
|
||||
this.emit('space-opened');
|
||||
});
|
||||
Tweener.addTween(children[i], params);
|
||||
}
|
||||
},
|
||||
|
||||
closeExtraSpace: function() {
|
||||
if (!this._translatedChildren || !this._translatedChildren.length) {
|
||||
this.emit('space-closed');
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._translatedChildren.length; i++) {
|
||||
if (!this._translatedChildren[i].translation_y)
|
||||
continue;
|
||||
Tweener.addTween(this._translatedChildren[i],
|
||||
{ translation_y: 0,
|
||||
time: EXTRA_SPACE_ANIMATION_TIME,
|
||||
transition: 'easeInOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.emit('space-closed');
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PaginatedIconGrid.prototype);
|
||||
|
@ -23,27 +23,29 @@ const KEYBOARD_TYPE = 'keyboard-type';
|
||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
||||
|
||||
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
|
||||
<method name='Show'>
|
||||
<arg type='u' direction='in' />
|
||||
</method>
|
||||
<method name='Hide'>
|
||||
<arg type='u' direction='in' />
|
||||
</method>
|
||||
<method name='SetCursorLocation'>
|
||||
<arg type='i' direction='in' />
|
||||
<arg type='i' direction='in' />
|
||||
<arg type='i' direction='in' />
|
||||
<arg type='i' direction='in' />
|
||||
</method>
|
||||
<method name='SetEntryLocation'>
|
||||
<arg type='i' direction='in' />
|
||||
<arg type='i' direction='in' />
|
||||
<arg type='i' direction='in' />
|
||||
<arg type='i' direction='in' />
|
||||
</method>
|
||||
<property name='Name' access='read' type='s' />
|
||||
</interface>;
|
||||
const CaribouKeyboardIface = '<node> \
|
||||
<interface name="org.gnome.Caribou.Keyboard"> \
|
||||
<method name="Show"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
<method name="Hide"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
<method name="SetCursorLocation"> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
</method> \
|
||||
<method name="SetEntryLocation"> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
</method> \
|
||||
<property name="Name" access="read" type="s" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const Key = new Lang.Class({
|
||||
Name: 'Key',
|
||||
|
@ -227,11 +227,6 @@ const LayoutManager = new Lang.Class({
|
||||
this._backgroundGroup.lower_bottom();
|
||||
this._bgManagers = [];
|
||||
|
||||
// This blocks the XDND picks from finding the activities button
|
||||
// and we never attempt to pick anything from it anyway so make
|
||||
// it invisible from picks
|
||||
Shell.util_set_hidden_from_pick(global.top_window_group, true);
|
||||
|
||||
// Need to update struts on new workspaces when they are added
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
@ -256,7 +251,7 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._inOverview = true;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
this._updateRegions();
|
||||
},
|
||||
|
||||
hideOverview: function() {
|
||||
@ -367,7 +362,7 @@ const LayoutManager = new Lang.Class({
|
||||
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
|
||||
}));
|
||||
|
||||
this._bgManagers.push(bgManager);
|
||||
this._bgManagers[monitorIndex] = bgManager;
|
||||
|
||||
return bgManager.background;
|
||||
},
|
||||
@ -600,6 +595,8 @@ const LayoutManager = new Lang.Class({
|
||||
if (Main.sessionMode.isGreeter) {
|
||||
this.panelBox.translation_y = -this.panelBox.height;
|
||||
} else {
|
||||
this._createPrimaryBackground();
|
||||
|
||||
// We need to force an update of the regions now before we scale
|
||||
// the UI group to get the coorect allocation for the struts.
|
||||
this._updateRegions();
|
||||
@ -649,7 +646,6 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_startupAnimationSession: function() {
|
||||
this._createPrimaryBackground();
|
||||
Tweener.addTween(this.uiGroup,
|
||||
{ scale_x: 1,
|
||||
scale_y: 1,
|
||||
|
@ -42,15 +42,11 @@ const Lightbox = new Lang.Class({
|
||||
params = Params.parse(params, { inhibitEvents: false,
|
||||
width: null,
|
||||
height: null,
|
||||
fadeInTime: null,
|
||||
fadeOutTime: null,
|
||||
fadeFactor: DEFAULT_FADE_FACTOR
|
||||
fadeFactor: DEFAULT_FADE_FACTOR,
|
||||
});
|
||||
|
||||
this._container = container;
|
||||
this._children = container.get_children();
|
||||
this._fadeInTime = params.fadeInTime;
|
||||
this._fadeOutTime = params.fadeOutTime;
|
||||
this._fadeFactor = params.fadeFactor;
|
||||
this.actor = new St.Bin({ x: 0,
|
||||
y: 0,
|
||||
@ -101,14 +97,16 @@ const Lightbox = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
show: function() {
|
||||
show: function(fadeInTime) {
|
||||
fadeInTime = fadeInTime || 0;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (this._fadeInTime) {
|
||||
if (fadeInTime != 0) {
|
||||
this.shown = false;
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255 * this._fadeFactor,
|
||||
time: this._fadeInTime,
|
||||
time: fadeInTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.shown = true;
|
||||
@ -123,13 +121,15 @@ const Lightbox = new Lang.Class({
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
hide: function(fadeOutTime) {
|
||||
fadeOutTime = fadeOutTime || 0;
|
||||
|
||||
this.shown = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (this._fadeOutTime) {
|
||||
if (fadeOutTime != 0) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: this._fadeOutTime,
|
||||
time: fadeOutTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.actor.hide();
|
||||
|
@ -629,55 +629,6 @@ const Inspector = new Lang.Class({
|
||||
|
||||
Signals.addSignalMethods(Inspector.prototype);
|
||||
|
||||
const Memory = new Lang.Class({
|
||||
Name: 'Memory',
|
||||
|
||||
_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._last_gc_seconds_ago = new St.Label();
|
||||
this.actor.add(this._last_gc_seconds_ago);
|
||||
|
||||
this._gcbutton = new St.Button({ label: 'Full GC',
|
||||
style_class: 'lg-obj-inspector-button' });
|
||||
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
|
||||
this.actor.add(this._gcbutton, { x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
|
||||
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;
|
||||
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
|
||||
}
|
||||
});
|
||||
|
||||
const Extensions = new Lang.Class({
|
||||
Name: 'Extensions',
|
||||
|
||||
@ -880,6 +831,19 @@ const LookingGlass = new Lang.Class({
|
||||
return true;
|
||||
}));
|
||||
|
||||
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
|
||||
icon_size: 24 });
|
||||
toolbar.add_actor(gcIcon);
|
||||
gcIcon.reactive = true;
|
||||
gcIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
gcIcon.icon_name = 'gnome-fs-trash-empty';
|
||||
System.gc();
|
||||
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
|
||||
gcIcon.icon_name = 'gnome-fs-trash-full';
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
}));
|
||||
}));
|
||||
|
||||
let notebook = new Notebook();
|
||||
this._notebook = notebook;
|
||||
this.actor.add(notebook.actor, { expand: true });
|
||||
@ -907,9 +871,6 @@ const LookingGlass = new Lang.Class({
|
||||
this._windowList = new WindowList(this);
|
||||
notebook.appendPage('Windows', this._windowList.actor);
|
||||
|
||||
this._memory = new Memory();
|
||||
notebook.appendPage('Memory', this._memory.actor);
|
||||
|
||||
this._extensions = new Extensions(this);
|
||||
notebook.appendPage('Extensions', this._extensions.actor);
|
||||
|
||||
@ -920,7 +881,7 @@ const LookingGlass = new Lang.Class({
|
||||
let text = o.get_text();
|
||||
// Ensure we don't get newlines in the command; the history file is
|
||||
// newline-separated.
|
||||
text.replace('\n', ' ');
|
||||
text = text.replace('\n', ' ');
|
||||
// Strip leading and trailing whitespace
|
||||
text = text.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
if (text == '')
|
||||
@ -1072,15 +1033,15 @@ const LookingGlass = new Lang.Class({
|
||||
let myWidth = primary.width * 0.7;
|
||||
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
|
||||
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
|
||||
this.actor.x = (primary.width - myWidth) / 2;
|
||||
this._hiddenY = Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
|
||||
this.actor.x = primary.x + (primary.width - myWidth) / 2;
|
||||
this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
|
||||
this._targetY = this._hiddenY + myHeight;
|
||||
this.actor.y = this._hiddenY;
|
||||
this.actor.width = myWidth;
|
||||
this.actor.height = myHeight;
|
||||
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
||||
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
|
||||
primary.y + this._targetY + Math.floor(myHeight * 0.1));
|
||||
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
|
||||
this._targetY + Math.floor(myHeight * 0.1));
|
||||
},
|
||||
|
||||
insertObject: function(obj) {
|
||||
@ -1150,7 +1111,7 @@ const LookingGlass = new Lang.Class({
|
||||
|
||||
Main.popModal(this._entry);
|
||||
|
||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||
Tweener.addTween(this.actor, { time: Math.min(0.5 / St.get_slow_down_factor(), 0.5),
|
||||
transition: 'easeOutQuad',
|
||||
y: this._hiddenY,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
|
@ -1,5 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Atspi = imports.gi.Atspi;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||
const Gio = imports.gi.Gio;
|
||||
@ -7,8 +8,10 @@ const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const FocusCaretTracker = imports.ui.focusCaretTracker;
|
||||
const Main = imports.ui.main;
|
||||
const MagnifierDBus = imports.ui.magnifierDBus;
|
||||
const Params = imports.misc.params;
|
||||
@ -36,6 +39,8 @@ const CONTRAST_BLUE_KEY = 'contrast-blue';
|
||||
const LENS_MODE_KEY = 'lens-mode';
|
||||
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
||||
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
||||
const FOCUS_TRACKING_KEY = 'focus-tracking';
|
||||
const CARET_TRACKING_KEY = 'caret-tracking';
|
||||
const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs';
|
||||
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
|
||||
const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color';
|
||||
@ -52,10 +57,24 @@ const Magnifier = new Lang.Class({
|
||||
// Magnifier is a manager of ZoomRegions.
|
||||
this._zoomRegions = [];
|
||||
|
||||
// Export to dbus.
|
||||
magDBusService = new MagnifierDBus.ShellMagnifier();
|
||||
|
||||
let showAtLaunch = this._settingsInit();
|
||||
this.setActive(showAtLaunch);
|
||||
},
|
||||
|
||||
_initialize: function() {
|
||||
if (this._initialized)
|
||||
return;
|
||||
this._initialized = true;
|
||||
|
||||
this._settingsInitLate();
|
||||
|
||||
// Create small clutter tree for the magnified mouse.
|
||||
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
||||
let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
|
||||
this._mouseSprite = new Clutter.Texture();
|
||||
xfixesCursor.update_texture_image(this._mouseSprite);
|
||||
Shell.util_cursor_tracker_to_clutter(cursorTracker, this._mouseSprite);
|
||||
this._cursorRoot = new Clutter.Actor();
|
||||
this._cursorRoot.add_actor(this._mouseSprite);
|
||||
|
||||
@ -67,15 +86,11 @@ const Magnifier = new Lang.Class({
|
||||
|
||||
let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
|
||||
this._zoomRegions.push(aZoomRegion);
|
||||
let showAtLaunch = this._settingsInit(aZoomRegion);
|
||||
this._settingsInitRegion(aZoomRegion);
|
||||
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
|
||||
|
||||
xfixesCursor.connect('cursor-change', Lang.bind(this, this._updateMouseSprite));
|
||||
this._xfixesCursor = xfixesCursor;
|
||||
|
||||
// Export to dbus.
|
||||
magDBusService = new MagnifierDBus.ShellMagnifier();
|
||||
this.setActive(showAtLaunch);
|
||||
cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
|
||||
this._cursorTracker = cursorTracker;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -83,7 +98,7 @@ const Magnifier = new Lang.Class({
|
||||
* Show the system mouse pointer.
|
||||
*/
|
||||
showSystemCursor: function() {
|
||||
this._xfixesCursor.show();
|
||||
this._cursorTracker.set_pointer_visible(true);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -91,7 +106,7 @@ const Magnifier = new Lang.Class({
|
||||
* Hide the system mouse pointer.
|
||||
*/
|
||||
hideSystemCursor: function() {
|
||||
this._xfixesCursor.hide();
|
||||
this._cursorTracker.set_pointer_visible(false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -100,6 +115,12 @@ const Magnifier = new Lang.Class({
|
||||
* @activate: Boolean to activate or de-activate the magnifier.
|
||||
*/
|
||||
setActive: function(activate) {
|
||||
if (activate == this.isActive())
|
||||
return;
|
||||
|
||||
if (activate)
|
||||
this._initialize();
|
||||
|
||||
this._zoomRegions.forEach (function(zoomRegion, index, array) {
|
||||
zoomRegion.setActive(activate);
|
||||
});
|
||||
@ -112,7 +133,7 @@ const Magnifier = new Lang.Class({
|
||||
// Make sure system mouse pointer is shown when all zoom regions are
|
||||
// invisible.
|
||||
if (!activate)
|
||||
this._xfixesCursor.show();
|
||||
this._cursorTracker.set_pointer_visible(true);
|
||||
|
||||
// Notify interested parties of this change
|
||||
this.emit('active-changed', activate);
|
||||
@ -422,62 +443,73 @@ const Magnifier = new Lang.Class({
|
||||
//// Private methods ////
|
||||
|
||||
_updateMouseSprite: function() {
|
||||
this._xfixesCursor.update_texture_image(this._mouseSprite);
|
||||
let xHot = this._xfixesCursor.get_hot_x();
|
||||
let yHot = this._xfixesCursor.get_hot_y();
|
||||
Shell.util_cursor_tracker_to_clutter(this._cursorTracker, this._mouseSprite);
|
||||
let [xHot, yHot] = this._cursorTracker.get_hot();
|
||||
this._mouseSprite.set_anchor_point(xHot, yHot);
|
||||
},
|
||||
|
||||
_settingsInit: function(zoomRegion) {
|
||||
_settingsInitRegion: function(zoomRegion) {
|
||||
// Mag factor is accurate to two decimal places.
|
||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||
if (aPref != 0.0)
|
||||
zoomRegion.setMagFactor(aPref, aPref);
|
||||
|
||||
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setScreenPosition(aPref);
|
||||
|
||||
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
|
||||
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
|
||||
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setFocusTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setCaretTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setInvertLightness(aPref);
|
||||
|
||||
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setColorSaturation(aPref);
|
||||
|
||||
let bc = {};
|
||||
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
zoomRegion.setBrightness(bc);
|
||||
|
||||
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
zoomRegion.setContrast(bc);
|
||||
},
|
||||
|
||||
_settingsInit: function() {
|
||||
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
|
||||
|
||||
if (zoomRegion) {
|
||||
// Mag factor is accurate to two decimal places.
|
||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||
if (aPref != 0.0)
|
||||
zoomRegion.setMagFactor(aPref, aPref);
|
||||
this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
|
||||
let active = this._appSettings.get_boolean(SHOW_KEY);
|
||||
this.setActive(active);
|
||||
}));
|
||||
|
||||
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setScreenPosition(aPref);
|
||||
|
||||
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
|
||||
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
|
||||
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setInvertLightness(aPref);
|
||||
|
||||
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setColorSaturation(aPref);
|
||||
|
||||
let bc = {};
|
||||
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
zoomRegion.setBrightness(bc);
|
||||
|
||||
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
zoomRegion.setContrast(bc);
|
||||
}
|
||||
return this._appSettings.get_boolean(SHOW_KEY);
|
||||
},
|
||||
|
||||
_settingsInitLate: function() {
|
||||
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
||||
this.addCrosshairs();
|
||||
this.setCrosshairsVisible(showCrosshairs);
|
||||
|
||||
this._appSettings.connect('changed::' + SHOW_KEY,
|
||||
Lang.bind(this, function() {
|
||||
this.setActive(this._appSettings.get_boolean(SHOW_KEY));
|
||||
}));
|
||||
|
||||
this._settings.connect('changed::' + SCREEN_POSITION_KEY,
|
||||
Lang.bind(this, this._updateScreenPosition));
|
||||
this._settings.connect('changed::' + MAG_FACTOR_KEY,
|
||||
@ -488,6 +520,10 @@ const Magnifier = new Lang.Class({
|
||||
Lang.bind(this, this._updateClampMode));
|
||||
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
||||
Lang.bind(this, this._updateMouseTrackingMode));
|
||||
this._settings.connect('changed::' + FOCUS_TRACKING_KEY,
|
||||
Lang.bind(this, this._updateFocusTrackingMode));
|
||||
this._settings.connect('changed::' + CARET_TRACKING_KEY,
|
||||
Lang.bind(this, this._updateCaretTrackingMode));
|
||||
|
||||
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
|
||||
Lang.bind(this, this._updateInvertLightness));
|
||||
@ -537,8 +573,6 @@ const Magnifier = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
|
||||
}));
|
||||
|
||||
return this._appSettings.get_boolean(SHOW_KEY);
|
||||
},
|
||||
|
||||
_updateScreenPosition: function() {
|
||||
@ -585,6 +619,24 @@ const Magnifier = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateFocusTrackingMode: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
this._zoomRegions[0].setFocusTrackingMode(
|
||||
this._settings.get_enum(FOCUS_TRACKING_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateCaretTrackingMode: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
this._zoomRegions[0].setCaretTrackingMode(
|
||||
this._settings.get_enum(CARET_TRACKING_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateInvertLightness: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
@ -623,7 +675,7 @@ const Magnifier = new Lang.Class({
|
||||
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
this._zoomRegions[0].setContrast(contrast);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Magnifier.prototype);
|
||||
|
||||
@ -632,8 +684,11 @@ const ZoomRegion = new Lang.Class({
|
||||
|
||||
_init: function(magnifier, mouseSourceActor) {
|
||||
this._magnifier = magnifier;
|
||||
this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
|
||||
|
||||
this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
|
||||
this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
|
||||
this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
|
||||
this._clampScrollingAtEdges = false;
|
||||
this._lensMode = false;
|
||||
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
||||
@ -659,9 +714,35 @@ const ZoomRegion = new Lang.Class({
|
||||
this._xMagFactor = 1;
|
||||
this._yMagFactor = 1;
|
||||
this._followingCursor = false;
|
||||
this._xFocus = 0;
|
||||
this._yFocus = 0;
|
||||
this._xCaret = 0;
|
||||
this._yCaret = 0;
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._focusCaretTracker.connect('caret-moved',
|
||||
Lang.bind(this, this._updateCaret));
|
||||
this._focusCaretTracker.connect('focus-changed',
|
||||
Lang.bind(this, this._updateFocus));
|
||||
},
|
||||
|
||||
_updateFocus: function(caller, event) {
|
||||
let component = event.source.get_component_iface();
|
||||
if (!component || event.detail1 != 1)
|
||||
return;
|
||||
let extents = component.get_extents(Atspi.CoordType.SCREEN);
|
||||
[this._xFocus, this._yFocus] = [extents.x, extents.y]
|
||||
this._centerFromFocusPosition();
|
||||
},
|
||||
|
||||
_updateCaret: function(caller, event) {
|
||||
let text = event.source.get_text_iface();
|
||||
if (!text)
|
||||
return;
|
||||
let extents = text.get_character_extents(text.get_caret_offset(), 0);
|
||||
[this._xCaret, this._yCaret] = [extents.x, extents.y];
|
||||
this._centerFromCaretPosition();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -669,14 +750,17 @@ const ZoomRegion = new Lang.Class({
|
||||
* @activate: Boolean to show/hide the ZoomRegion.
|
||||
*/
|
||||
setActive: function(activate) {
|
||||
if (activate && !this.isActive()) {
|
||||
if (activate == this.isActive())
|
||||
return;
|
||||
|
||||
if (activate) {
|
||||
this._createActors();
|
||||
if (this._isMouseOverRegion())
|
||||
this._magnifier.hideSystemCursor();
|
||||
this._updateMagViewGeometry();
|
||||
this._updateCloneGeometry();
|
||||
this._updateMousePosition();
|
||||
} else if (!activate && this.isActive()) {
|
||||
} else {
|
||||
this._destroyActors();
|
||||
}
|
||||
},
|
||||
@ -732,6 +816,30 @@ const ZoomRegion = new Lang.Class({
|
||||
return this._mouseTrackingMode;
|
||||
},
|
||||
|
||||
/**
|
||||
* setFocusTrackingMode
|
||||
* @mode: One of the enum FocusTrackingMode values.
|
||||
*/
|
||||
setFocusTrackingMode: function(mode) {
|
||||
this._focusTrackingMode = mode;
|
||||
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
|
||||
this._focusCaretTracker.deregisterFocusListener();
|
||||
else
|
||||
this._focusCaretTracker.registerFocusListener();
|
||||
},
|
||||
|
||||
/**
|
||||
* setCaretTrackingMode
|
||||
* @mode: One of the enum CaretTrackingMode values.
|
||||
*/
|
||||
setCaretTrackingMode: function(mode) {
|
||||
this._caretTrackingMode = mode;
|
||||
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
|
||||
this._focusCaretTracker.deregisterCaretListener();
|
||||
else
|
||||
this._focusCaretTracker.registerCaretListener();
|
||||
},
|
||||
|
||||
/**
|
||||
* setViewPort
|
||||
* Sets the position and size of the ZoomRegion on screen.
|
||||
@ -1023,20 +1131,6 @@ const ZoomRegion = new Lang.Class({
|
||||
this._magShaderEffects.setBrightness(this._brightness);
|
||||
},
|
||||
|
||||
/**
|
||||
* getBrightness:
|
||||
* Retrive the current brightness of the Zoom Region.
|
||||
* @return Object containing the brightness change for the red, green,
|
||||
* and blue channels.
|
||||
*/
|
||||
getBrightness: function() {
|
||||
let brightness = {};
|
||||
brightness.r = this._brightness.r;
|
||||
brightness.g = this._brightness.g;
|
||||
brightness.b = this._brightness.b;
|
||||
return brightness;
|
||||
},
|
||||
|
||||
/**
|
||||
* setContrast:
|
||||
* Alter the contrast of the magnified view.
|
||||
@ -1243,19 +1337,47 @@ const ZoomRegion = new Lang.Class({
|
||||
let yMouse = this._magnifier.yMouse;
|
||||
|
||||
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
|
||||
return this._centerFromMouseProportional(xMouse, yMouse);
|
||||
return this._centerFromPointProportional(xMouse, yMouse);
|
||||
}
|
||||
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
|
||||
return this._centerFromMousePush(xMouse, yMouse);
|
||||
return this._centerFromPointPush(xMouse, yMouse);
|
||||
}
|
||||
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
|
||||
return this._centerFromMouseCentered(xMouse, yMouse);
|
||||
return this._centerFromPointCentered(xMouse, yMouse);
|
||||
}
|
||||
|
||||
return null; // Should never be hit
|
||||
},
|
||||
|
||||
_centerFromMousePush: function(xMouse, yMouse) {
|
||||
_centerFromCaretPosition: function() {
|
||||
let xCaret = this._xCaret;
|
||||
let yCaret = this._yCaret;
|
||||
|
||||
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
|
||||
[xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
|
||||
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
|
||||
[xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
|
||||
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
|
||||
[xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
|
||||
|
||||
this.scrollContentsTo(xCaret, yCaret);
|
||||
},
|
||||
|
||||
_centerFromFocusPosition: function() {
|
||||
let xFocus = this._xFocus;
|
||||
let yFocus = this._yFocus;
|
||||
|
||||
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
|
||||
[xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
|
||||
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
|
||||
[xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
|
||||
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
|
||||
[xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
|
||||
|
||||
this.scrollContentsTo(xFocus, yFocus);
|
||||
},
|
||||
|
||||
_centerFromPointPush: function(xPoint, yPoint) {
|
||||
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
|
||||
let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
|
||||
let xPos = xRoi + widthRoi / 2;
|
||||
@ -1263,20 +1385,20 @@ const ZoomRegion = new Lang.Class({
|
||||
let xRoiRight = xRoi + widthRoi - cursorWidth;
|
||||
let yRoiBottom = yRoi + heightRoi - cursorHeight;
|
||||
|
||||
if (xMouse < xRoi)
|
||||
xPos -= (xRoi - xMouse);
|
||||
else if (xMouse > xRoiRight)
|
||||
xPos += (xMouse - xRoiRight);
|
||||
if (xPoint < xRoi)
|
||||
xPos -= (xRoi - xPoint);
|
||||
else if (xPoint > xRoiRight)
|
||||
xPos += (xPoint - xRoiRight);
|
||||
|
||||
if (yMouse < yRoi)
|
||||
yPos -= (yRoi - yMouse);
|
||||
else if (yMouse > yRoiBottom)
|
||||
yPos += (yMouse - yRoiBottom);
|
||||
if (yPoint < yRoi)
|
||||
yPos -= (yRoi - yPoint);
|
||||
else if (yPoint > yRoiBottom)
|
||||
yPos += (yPoint - yRoiBottom);
|
||||
|
||||
return [xPos, yPos];
|
||||
},
|
||||
|
||||
_centerFromMouseProportional: function(xMouse, yMouse) {
|
||||
_centerFromPointProportional: function(xPoint, yPoint) {
|
||||
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
|
||||
let halfScreenWidth = global.screen_width / 2;
|
||||
let halfScreenHeight = global.screen_height / 2;
|
||||
@ -1285,16 +1407,16 @@ const ZoomRegion = new Lang.Class({
|
||||
let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
|
||||
let xPadding = unscaledPadding / this._xMagFactor;
|
||||
let yPadding = unscaledPadding / this._yMagFactor;
|
||||
let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth; // -1 ... 1
|
||||
let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1
|
||||
let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding);
|
||||
let yPos = yMouse - yProportion * (heightRoi /2 - yPadding);
|
||||
let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth; // -1 ... 1
|
||||
let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1
|
||||
let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding);
|
||||
let yPos = yPoint - yProportion * (heightRoi /2 - yPadding);
|
||||
|
||||
return [xPos, yPos];
|
||||
},
|
||||
|
||||
_centerFromMouseCentered: function(xMouse, yMouse) {
|
||||
return [xMouse, yMouse];
|
||||
_centerFromPointCentered: function(xPoint, yPoint) {
|
||||
return [xPoint, yPoint];
|
||||
},
|
||||
|
||||
_screenToViewPort: function(screenX, screenY) {
|
||||
@ -1511,15 +1633,6 @@ const Crosshairs = new Lang.Class({
|
||||
this._vertBottomHair.set_opacity(opacity);
|
||||
},
|
||||
|
||||
/**
|
||||
* getOpacity:
|
||||
* Retriev how opaque the crosshairs are.
|
||||
* @return: A value between 0 (transparent) and 255 (opaque).
|
||||
*/
|
||||
getOpacity: function() {
|
||||
return this._horizLeftHair.get_opacity();
|
||||
},
|
||||
|
||||
/**
|
||||
* setLength:
|
||||
* Set the length of the vertical and horizontal lines in the crosshairs.
|
||||
@ -1563,15 +1676,6 @@ const Crosshairs = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* getClip:
|
||||
* Get the dimensions of the clip rectangle.
|
||||
* @return: An array of the form [width, height].
|
||||
*/
|
||||
getClip: function() {
|
||||
return this._clipSize;
|
||||
},
|
||||
|
||||
/**
|
||||
* show:
|
||||
* Show the crosshairs.
|
||||
@ -1667,23 +1771,10 @@ const MagShaderEffects = new Lang.Class({
|
||||
this._inverse.set_enabled(invertFlag);
|
||||
},
|
||||
|
||||
/**
|
||||
* getInvertLightness:
|
||||
* Report whether the inversion effect is enabled.
|
||||
* @return: Boolean.
|
||||
*/
|
||||
getInvertLightness: function() {
|
||||
return this._inverse.get_enabled();
|
||||
},
|
||||
|
||||
setColorSaturation: function(factor) {
|
||||
this._colorDesaturation.set_factor(1.0 - factor);
|
||||
},
|
||||
|
||||
getColorSaturation: function() {
|
||||
return 1.0 - this._colorDesaturation.get_factor();
|
||||
},
|
||||
|
||||
/**
|
||||
* setBrightness:
|
||||
* Set the brightness of the magnified view.
|
||||
@ -1708,24 +1799,6 @@ const MagShaderEffects = new Lang.Class({
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* getBrightness:
|
||||
* Retrieve current brightness of the magnified view.
|
||||
* @return: Object containing the brightness for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||
*/
|
||||
getBrightness: function() {
|
||||
let result = {};
|
||||
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||
result.r = bRed;
|
||||
result.g = bGreen;
|
||||
result.b = bBlue;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the contrast of the magnified view.
|
||||
* @contrast: Object containing the contrast for the red, green,
|
||||
@ -1750,21 +1823,4 @@ const MagShaderEffects = new Lang.Class({
|
||||
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve current contrast of the magnified view.
|
||||
* @return: Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
getContrast: function() {
|
||||
let resutl = {};
|
||||
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||
result.r = cRed;
|
||||
result.g = cGreen;
|
||||
result.b = cBlue;
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
@ -4,92 +4,94 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const MAG_SERVICE_NAME = 'org.gnome.Magnifier';
|
||||
const MAG_SERVICE_PATH = '/org/gnome/Magnifier';
|
||||
const ZOOM_SERVICE_NAME = 'org.gnome.Magnifier.ZoomRegion';
|
||||
const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
|
||||
|
||||
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
|
||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
|
||||
const MagnifierIface = <interface name={MAG_SERVICE_NAME}>
|
||||
<method name="setActive">
|
||||
<arg type="b" direction="in" />
|
||||
</method>
|
||||
<method name="isActive">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="showCursor" />
|
||||
<method name="hideCursor" />
|
||||
<method name="createZoomRegion">
|
||||
<arg type="d" direction="in" />
|
||||
<arg type="d" direction="in" />
|
||||
<arg type="ai" direction="in" />
|
||||
<arg type="ai" direction="in" />
|
||||
<arg type="o" direction="out" />
|
||||
</method>
|
||||
<method name="addZoomRegion">
|
||||
<arg type="o" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="getZoomRegions">
|
||||
<arg type="ao" direction="out" />
|
||||
</method>
|
||||
<method name="clearAllZoomRegions" />
|
||||
<method name="fullScreenCapable">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="setCrosswireSize">
|
||||
<arg type="i" direction="in" />
|
||||
</method>
|
||||
<method name="getCrosswireSize">
|
||||
<arg type="i" direction="out" />
|
||||
</method>
|
||||
<method name="setCrosswireLength">
|
||||
<arg type="i" direction="in" />
|
||||
</method>
|
||||
<method name="getCrosswireLength">
|
||||
<arg type="i" direction="out" />
|
||||
</method>
|
||||
<method name="setCrosswireClip">
|
||||
<arg type="b" direction="in" />
|
||||
</method>
|
||||
<method name="getCrosswireClip">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="setCrosswireColor">
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
<method name="getCrosswireColor">
|
||||
<arg type="u" direction="out" />
|
||||
</method>
|
||||
</interface>;
|
||||
const MagnifierIface = '<node> \
|
||||
<interface name="org.gnome.Magnifier"> \
|
||||
<method name="setActive"> \
|
||||
<arg type="b" direction="in" /> \
|
||||
</method> \
|
||||
<method name="isActive"> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<method name="showCursor" /> \
|
||||
<method name="hideCursor" /> \
|
||||
<method name="createZoomRegion"> \
|
||||
<arg type="d" direction="in" /> \
|
||||
<arg type="d" direction="in" /> \
|
||||
<arg type="ai" direction="in" /> \
|
||||
<arg type="ai" direction="in" /> \
|
||||
<arg type="o" direction="out" /> \
|
||||
</method> \
|
||||
<method name="addZoomRegion"> \
|
||||
<arg type="o" direction="in" /> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<method name="getZoomRegions"> \
|
||||
<arg type="ao" direction="out" /> \
|
||||
</method> \
|
||||
<method name="clearAllZoomRegions" /> \
|
||||
<method name="fullScreenCapable"> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<method name="setCrosswireSize"> \
|
||||
<arg type="i" direction="in" /> \
|
||||
</method> \
|
||||
<method name="getCrosswireSize"> \
|
||||
<arg type="i" direction="out" /> \
|
||||
</method> \
|
||||
<method name="setCrosswireLength"> \
|
||||
<arg type="i" direction="in" /> \
|
||||
</method> \
|
||||
<method name="getCrosswireLength"> \
|
||||
<arg type="i" direction="out" /> \
|
||||
</method> \
|
||||
<method name="setCrosswireClip"> \
|
||||
<arg type="b" direction="in" /> \
|
||||
</method> \
|
||||
<method name="getCrosswireClip"> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<method name="setCrosswireColor"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
<method name="getCrosswireColor"> \
|
||||
<arg type="u" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
|
||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
|
||||
const ZoomRegionIface = <interface name={ZOOM_SERVICE_NAME}>
|
||||
<method name="setMagFactor">
|
||||
<arg type="d" direction="in" />
|
||||
<arg type="d" direction="in" />
|
||||
</method>
|
||||
<method name="getMagFactor">
|
||||
<arg type="d" direction="out" />
|
||||
<arg type="d" direction="out" />
|
||||
</method>
|
||||
<method name="setRoi">
|
||||
<arg type="ai" direction="in" />
|
||||
</method>
|
||||
<method name="getRoi">
|
||||
<arg type="ai" direction="out" />
|
||||
</method>
|
||||
<method name="shiftContentsTo">
|
||||
<arg type="i" direction="in" />
|
||||
<arg type="i" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="moveResize">
|
||||
<arg type="ai" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
const ZoomRegionIface = '<node> \
|
||||
<interface name="org.gnome.Magnifier.ZoomRegion"> \
|
||||
<method name="setMagFactor"> \
|
||||
<arg type="d" direction="in" /> \
|
||||
<arg type="d" direction="in" /> \
|
||||
</method> \
|
||||
<method name="getMagFactor"> \
|
||||
<arg type="d" direction="out" /> \
|
||||
<arg type="d" direction="out" /> \
|
||||
</method> \
|
||||
<method name="setRoi"> \
|
||||
<arg type="ai" direction="in" /> \
|
||||
</method> \
|
||||
<method name="getRoi"> \
|
||||
<arg type="ai" direction="out" /> \
|
||||
</method> \
|
||||
<method name="shiftContentsTo"> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="b" direction="out" /> \
|
||||
</method> \
|
||||
<method name="moveResize"> \
|
||||
<arg type="ai" direction="in" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
// For making unique ZoomRegion DBus proxy object paths of the form:
|
||||
// '/org/gnome/Magnifier/ZoomRegion/zoomer0',
|
||||
|
@ -28,6 +28,7 @@ const LoginManager = imports.misc.loginManager;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
const Screencast = imports.ui.screencast;
|
||||
const ScreenShield = imports.ui.screenShield;
|
||||
const Scripting = imports.ui.scripting;
|
||||
const SessionMode = imports.ui.sessionMode;
|
||||
@ -59,6 +60,7 @@ let sessionMode = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
let screencastService = null;
|
||||
let modalCount = 0;
|
||||
let keybindingMode = Shell.KeyBindingMode.NONE;
|
||||
let modalActorFocusStack = [];
|
||||
@ -88,8 +90,12 @@ function _sessionUpdated() {
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
|
||||
if (!sessionMode.hasRunDialog && lookingGlass)
|
||||
lookingGlass.close();
|
||||
if (!sessionMode.hasRunDialog) {
|
||||
if (runDialog)
|
||||
runDialog.close();
|
||||
if (lookingGlass)
|
||||
lookingGlass.close();
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
@ -97,6 +103,9 @@ function start() {
|
||||
global.logError = window.log;
|
||||
global.log = window.log;
|
||||
|
||||
if (!Meta.is_wayland_compositor)
|
||||
Meta.is_wayland_compositor = function () { return false; };
|
||||
|
||||
// Chain up async errors reported from C
|
||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||
|
||||
@ -151,6 +160,7 @@ function _initializeUI() {
|
||||
// working until it's updated.
|
||||
uiGroup = layoutManager.uiGroup;
|
||||
|
||||
screencastService = new Screencast.ScreencastService();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
osdWindow = new OsdWindow.OsdWindow();
|
||||
@ -260,11 +270,7 @@ function loadTheme() {
|
||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||
let previousTheme = themeContext.get_theme();
|
||||
|
||||
let cssStylesheet = _defaultCssStylesheet;
|
||||
if (_cssStylesheet != null)
|
||||
cssStylesheet = _cssStylesheet;
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
|
||||
let theme = new St.Theme ({ application_stylesheet: _cssStylesheet,
|
||||
default_stylesheet: _defaultCssStylesheet });
|
||||
|
||||
if (previousTheme) {
|
||||
|
@ -79,6 +79,11 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
// modal dialogs are fixed width and grow vertically; set the request
|
||||
// mode accordingly so wrapped labels are handled correctly during
|
||||
// size requests.
|
||||
this.dialogLayout.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
|
||||
|
||||
if (params.styleClass != null)
|
||||
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||
|
||||
@ -95,7 +100,8 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this.dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
@ -103,8 +109,7 @@ const ModalDialog = new Lang.Class({
|
||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
vertical: false });
|
||||
this.dialogLayout.add(this.buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
{ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
|
||||
global.focus_manager.add_group(this.dialogLayout);
|
||||
|
@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -15,54 +16,56 @@ const MessageTray = imports.ui.messageTray;
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
let nextNotificationId = 1;
|
||||
|
||||
// Should really be defined in Gio.js
|
||||
const BusIface = <interface name="org.freedesktop.DBus">
|
||||
<method name="GetConnectionUnixProcessID">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="u" direction="out" />
|
||||
</method>
|
||||
</interface>;
|
||||
const BusIface = '<node> \
|
||||
<interface name="org.freedesktop.DBus"> \
|
||||
<method name="GetConnectionUnixProcessID"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="u" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
|
||||
function Bus() {
|
||||
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
|
||||
}
|
||||
|
||||
const NotificationDaemonIface = <interface name="org.freedesktop.Notifications">
|
||||
<method name="Notify">
|
||||
<arg type="s" direction="in"/>
|
||||
<arg type="u" direction="in"/>
|
||||
<arg type="s" direction="in"/>
|
||||
<arg type="s" direction="in"/>
|
||||
<arg type="s" direction="in"/>
|
||||
<arg type="as" direction="in"/>
|
||||
<arg type="a{sv}" direction="in"/>
|
||||
<arg type="i" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="CloseNotification">
|
||||
<arg type="u" direction="in"/>
|
||||
</method>
|
||||
<method name="GetCapabilities">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<method name="GetServerInformation">
|
||||
<arg type="s" direction="out"/>
|
||||
<arg type="s" direction="out"/>
|
||||
<arg type="s" direction="out"/>
|
||||
<arg type="s" direction="out"/>
|
||||
</method>
|
||||
<signal name="NotificationClosed">
|
||||
<arg type="u"/>
|
||||
<arg type="u"/>
|
||||
</signal>
|
||||
<signal name="ActionInvoked">
|
||||
<arg type="u"/>
|
||||
<arg type="s"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
const FdoNotificationsIface = '<node> \
|
||||
<interface name="org.freedesktop.Notifications"> \
|
||||
<method name="Notify"> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="u" direction="in"/> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="s" direction="in"/> \
|
||||
<arg type="as" direction="in"/> \
|
||||
<arg type="a{sv}" direction="in"/> \
|
||||
<arg type="i" direction="in"/> \
|
||||
<arg type="u" direction="out"/> \
|
||||
</method> \
|
||||
<method name="CloseNotification"> \
|
||||
<arg type="u" direction="in"/> \
|
||||
</method> \
|
||||
<method name="GetCapabilities"> \
|
||||
<arg type="as" direction="out"/> \
|
||||
</method> \
|
||||
<method name="GetServerInformation"> \
|
||||
<arg type="s" direction="out"/> \
|
||||
<arg type="s" direction="out"/> \
|
||||
<arg type="s" direction="out"/> \
|
||||
<arg type="s" direction="out"/> \
|
||||
</method> \
|
||||
<signal name="NotificationClosed"> \
|
||||
<arg type="u"/> \
|
||||
<arg type="u"/> \
|
||||
</signal> \
|
||||
<signal name="ActionInvoked"> \
|
||||
<arg type="u"/> \
|
||||
<arg type="s"/> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const NotificationClosedReason = {
|
||||
EXPIRED: 1,
|
||||
@ -103,131 +106,11 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'ibus-ui-gtk': 'keyboard'
|
||||
};
|
||||
|
||||
const NotificationGenericPolicy = new Lang.Class({
|
||||
Name: 'NotificationGenericPolicy',
|
||||
Extends: MessageTray.NotificationPolicy,
|
||||
const FdoNotificationDaemon = new Lang.Class({
|
||||
Name: 'FdoNotificationDaemon',
|
||||
|
||||
_init: function() {
|
||||
// Don't chain to parent, it would try setting
|
||||
// our properties to the defaults
|
||||
|
||||
this.id = 'generic';
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
store: function() { },
|
||||
|
||||
destroy: function() {
|
||||
this._masterSettings.run_dispose();
|
||||
},
|
||||
|
||||
_changed: function(settings, key) {
|
||||
this.emit('policy-changed', key);
|
||||
},
|
||||
|
||||
get enable() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get enableSound() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get showBanners() {
|
||||
return this._masterSettings.get_boolean('show-banners');
|
||||
},
|
||||
|
||||
get forceExpanded() {
|
||||
return false;
|
||||
},
|
||||
|
||||
get showInLockScreen() {
|
||||
return this._masterSettings.get_boolean('show-in-lock-screen');
|
||||
},
|
||||
|
||||
get detailsInLockScreen() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationApplicationPolicy = new Lang.Class({
|
||||
Name: 'NotificationApplicationPolicy',
|
||||
Extends: MessageTray.NotificationPolicy,
|
||||
|
||||
_init: function(id) {
|
||||
// Don't chain to parent, it would try setting
|
||||
// our properties to the defaults
|
||||
|
||||
this.id = id;
|
||||
this._canonicalId = this._canonicalizeId(id)
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
|
||||
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
|
||||
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
this._settings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
store: function() {
|
||||
this._settings.set_string('application-id', this.id + '.desktop');
|
||||
|
||||
let apps = this._masterSettings.get_strv('application-children');
|
||||
if (apps.indexOf(this._canonicalId) < 0) {
|
||||
apps.push(this._canonicalId);
|
||||
this._masterSettings.set_strv('application-children', apps);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._masterSettings.run_dispose();
|
||||
this._settings.run_dispose();
|
||||
},
|
||||
|
||||
_changed: function(settings, key) {
|
||||
this.emit('policy-changed', key);
|
||||
},
|
||||
|
||||
_canonicalizeId: function(id) {
|
||||
// Keys are restricted to lowercase alphanumeric characters and dash,
|
||||
// and two dashes cannot be in succession
|
||||
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
|
||||
},
|
||||
|
||||
get enable() {
|
||||
return this._settings.get_boolean('enable');
|
||||
},
|
||||
|
||||
get enableSound() {
|
||||
return this._settings.get_boolean('enable-sound-alerts');
|
||||
},
|
||||
|
||||
get showBanners() {
|
||||
return this._masterSettings.get_boolean('show-banners') &&
|
||||
this._settings.get_boolean('show-banners');
|
||||
},
|
||||
|
||||
get forceExpanded() {
|
||||
return this._settings.get_boolean('force-expanded');
|
||||
},
|
||||
|
||||
get showInLockScreen() {
|
||||
return this._masterSettings.get_boolean('show-in-lock-screen') &&
|
||||
this._settings.get_boolean('show-in-lock-screen');
|
||||
},
|
||||
|
||||
get detailsInLockScreen() {
|
||||
return this._settings.get_boolean('details-in-lock-screen');
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationDaemon = new Lang.Class({
|
||||
Name: 'NotificationDaemon',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
|
||||
|
||||
this._sources = [];
|
||||
@ -235,6 +118,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._notifications = {};
|
||||
this._busProxy = new Bus();
|
||||
|
||||
this._nextNotificationId = 1;
|
||||
|
||||
this._trayManager = new Shell.TrayManager();
|
||||
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
@ -333,7 +218,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
source.setTransient(isForTransientNotification);
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
@ -372,7 +257,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
hints['category'] == 'presence.offline')) {
|
||||
// Ignore replacesId since we already sent back a
|
||||
// NotificationClosed for that id.
|
||||
id = nextNotificationId++;
|
||||
id = this._nextNotificationId++;
|
||||
Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||
@ -396,12 +281,13 @@ const NotificationDaemon = new Lang.Class({
|
||||
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
|
||||
else if (hints['icon_data'] && !hints['image-path'])
|
||||
// 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,
|
||||
icon: icon,
|
||||
@ -415,7 +301,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
ndata.notification = this._notifications[replacesId].notification;
|
||||
} else {
|
||||
replacesId = 0;
|
||||
ndata.id = id = nextNotificationId++;
|
||||
ndata.id = id = this._nextNotificationId++;
|
||||
}
|
||||
this._notifications[id] = ndata;
|
||||
|
||||
@ -470,6 +356,19 @@ const NotificationDaemon = new Lang.Class({
|
||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||
},
|
||||
|
||||
_makeButton: function(id, label, useActionIcons) {
|
||||
let button = new St.Button({ can_focus: true });
|
||||
let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
|
||||
if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
|
||||
button.add_style_class_name('notification-icon-button');
|
||||
button.child = new St.Icon({ icon_name: iconName });
|
||||
} else {
|
||||
button.add_style_class_name('notification-button');
|
||||
button.label = label;
|
||||
}
|
||||
return button;
|
||||
},
|
||||
|
||||
_notifyForSource: function(source, ndata) {
|
||||
let [id, icon, summary, body, actions, hints, notification] =
|
||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||
@ -495,10 +394,6 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
this._emitNotificationClosed(ndata.id, notificationClosedReason);
|
||||
}));
|
||||
notification.connect('action-invoked', Lang.bind(this,
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
}
|
||||
|
||||
// Mark music notifications so they can be shown in the screen shield
|
||||
@ -532,18 +427,33 @@ const NotificationDaemon = new Lang.Class({
|
||||
soundName: hints['sound-name'] });
|
||||
notification.setImage(image);
|
||||
|
||||
let hasDefaultAction = false;
|
||||
|
||||
if (actions.length) {
|
||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
||||
let useActionIcons = (hints['action-icons'] == true);
|
||||
|
||||
for (let i = 0; i < actions.length - 1; i += 2) {
|
||||
if (actions[i] == 'default')
|
||||
notification.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._emitActionInvoked(ndata.id, "default");
|
||||
}));
|
||||
else
|
||||
notification.addButton(actions[i], actions[i + 1]);
|
||||
let [actionId, label] = [actions[i], actions[i+1]];
|
||||
if (actionId == 'default') {
|
||||
hasDefaultAction = true;
|
||||
} else {
|
||||
notification.addButton(this._makeButton(actionId, label, useActionIcons), Lang.bind(this, function() {
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDefaultAction) {
|
||||
notification.connect('clicked', Lang.bind(this, function() {
|
||||
this._emitActionInvoked(ndata.id, 'default');
|
||||
}));
|
||||
} else {
|
||||
notification.connect('clicked', Lang.bind(this, function() {
|
||||
source.open();
|
||||
}));
|
||||
}
|
||||
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
notification.setUrgency(MessageTray.Urgency.LOW);
|
||||
@ -636,8 +546,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const Source = new Lang.Class({
|
||||
Name: 'NotificationDaemonSource',
|
||||
const FdoNotificationDaemonSource = new Lang.Class({
|
||||
Name: 'FdoNotificationDaemonSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(title, pid, sender, trayIcon, appId) {
|
||||
@ -674,9 +584,9 @@ const Source = new Lang.Class({
|
||||
_createPolicy: function() {
|
||||
if (this.app) {
|
||||
let id = this.app.get_id().replace(/\.desktop$/,'');
|
||||
return new NotificationApplicationPolicy(id);
|
||||
return new MessageTray.NotificationApplicationPolicy(id);
|
||||
} else {
|
||||
return new NotificationGenericPolicy();
|
||||
return new MessageTray.NotificationGenericPolicy();
|
||||
}
|
||||
},
|
||||
|
||||
@ -734,7 +644,11 @@ const Source = new Lang.Class({
|
||||
return app;
|
||||
|
||||
if (this.trayIcon) {
|
||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
|
||||
app = Shell.AppSystem.get_default().lookup_startup_wmclass(this.trayIcon.wm_class);
|
||||
if (app != null)
|
||||
return app;
|
||||
|
||||
app = Shell.AppSystem.get_default().lookup_desktop_wmclass(this.trayIcon.wm_class);
|
||||
if (app != null)
|
||||
return app;
|
||||
}
|
||||
@ -748,22 +662,6 @@ const Source = new Lang.Class({
|
||||
return null;
|
||||
},
|
||||
|
||||
_setApp: function(appId) {
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = this._getApp(appId);
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
// Only override the icon if we were previously using
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this.trayIcon) {
|
||||
this.useNotificationIcon = false;
|
||||
this.iconUpdated();
|
||||
}
|
||||
},
|
||||
|
||||
setTitle: function(title) {
|
||||
// Do nothing if .app is set, we don't want to override the
|
||||
// app name with whatever is provided through libnotify (usually
|
||||
@ -774,9 +672,9 @@ const Source = new Lang.Class({
|
||||
this.parent(title);
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
this.destroyNonResidentNotifications();
|
||||
open: function() {
|
||||
this.openApp();
|
||||
this.destroyNonResidentNotifications();
|
||||
},
|
||||
|
||||
_lastNotificationRemoved: function() {
|
||||
@ -788,11 +686,8 @@ const Source = new Lang.Class({
|
||||
if (this.app == null)
|
||||
return;
|
||||
|
||||
let windows = this.app.get_windows();
|
||||
if (windows.length > 0) {
|
||||
let mostRecentWindow = windows[0];
|
||||
Main.activateWindow(mostRecentWindow);
|
||||
}
|
||||
this.app.activate();
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -819,3 +714,276 @@ const Source = new Lang.Class({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const GtkNotificationDaemonNotification = new Lang.Class({
|
||||
Name: 'GtkNotificationDaemonNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, notification) {
|
||||
this.parent(source);
|
||||
this._serialized = GLib.Variant.new('a{sv}', notification);
|
||||
|
||||
let { "title": title,
|
||||
"body": body,
|
||||
"icon": gicon,
|
||||
"urgent": urgent,
|
||||
"buttons": buttons,
|
||||
"default-action": defaultAction,
|
||||
"default-action-target": defaultActionTarget } = notification;
|
||||
|
||||
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
|
||||
: MessageTray.Urgency.NORMAL);
|
||||
|
||||
if (buttons) {
|
||||
buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
|
||||
this.addAction(button.label.unpack(),
|
||||
Lang.bind(this, this._onButtonClicked, button));
|
||||
}));
|
||||
}
|
||||
|
||||
this._defaultAction = defaultAction ? defaultAction.unpack() : null;
|
||||
this._defaultActionTarget = defaultActionTarget;
|
||||
|
||||
this.update(title.unpack(), body ? body.unpack() : null,
|
||||
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null });
|
||||
},
|
||||
|
||||
_activateAction: function(namespacedActionId, target) {
|
||||
if (namespacedActionId) {
|
||||
if (namespacedActionId.startsWith('app.')) {
|
||||
let actionId = namespacedActionId.slice('app.'.length);
|
||||
this.source.activateAction(actionId, target);
|
||||
}
|
||||
} else {
|
||||
this.source.open();
|
||||
}
|
||||
},
|
||||
|
||||
_onButtonClicked: function(button) {
|
||||
let { 'action': action, 'target': actionTarget } = button;
|
||||
this._activateAction(action.unpack(), actionTarget);
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this._activateAction(this._defaultAction, this._defaultActionTarget);
|
||||
this.parent();
|
||||
},
|
||||
|
||||
serialize: function() {
|
||||
return this._serialized;
|
||||
},
|
||||
});
|
||||
|
||||
const FdoApplicationIface = '<node> \
|
||||
<interface name="org.freedesktop.Application"> \
|
||||
<method name="ActivateAction"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="av" direction="in" /> \
|
||||
<arg type="a{sv}" direction="in" /> \
|
||||
</method> \
|
||||
<method name="Activate"> \
|
||||
<arg type="a{sv}" direction="in" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
|
||||
|
||||
function objectPathFromAppId(appId) {
|
||||
return '/' + appId.replace(/\./g, '/');
|
||||
}
|
||||
|
||||
function getPlatformData() {
|
||||
let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
|
||||
return { "desktop-startup-id": startupId };
|
||||
}
|
||||
|
||||
function InvalidAppError() {}
|
||||
|
||||
const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||
Name: 'GtkNotificationDaemonAppSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(appId) {
|
||||
this._appId = appId;
|
||||
this._objectPath = objectPathFromAppId(appId);
|
||||
|
||||
this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
|
||||
if (!this._app)
|
||||
throw new InvalidAppError();
|
||||
|
||||
this._notifications = {};
|
||||
|
||||
this.parent(this._app.get_name());
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
return this._app.create_icon_texture(size);
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new MessageTray.NotificationApplicationPolicy(this._appId);
|
||||
},
|
||||
|
||||
_createApp: function() {
|
||||
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
|
||||
},
|
||||
|
||||
activateAction: function(actionId, target) {
|
||||
let app = this._createApp();
|
||||
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
|
||||
},
|
||||
|
||||
open: function() {
|
||||
let app = this._createApp();
|
||||
app.ActivateRemote(getPlatformData());
|
||||
},
|
||||
|
||||
addNotification: function(notificationId, notificationParams, showBanner) {
|
||||
if (this._notifications[notificationId])
|
||||
this._notifications[notificationId].destroy();
|
||||
|
||||
let notification = new GtkNotificationDaemonNotification(this, notificationParams);
|
||||
notification.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._notifications[notificationId];
|
||||
}));
|
||||
this._notifications[notificationId] = notification;
|
||||
|
||||
if (showBanner)
|
||||
this.notify(notification);
|
||||
else
|
||||
this.pushNotification(notification);
|
||||
},
|
||||
|
||||
removeNotification: function(notificationId) {
|
||||
if (this._notifications[notificationId])
|
||||
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
||||
},
|
||||
|
||||
serialize: function() {
|
||||
let notifications = [];
|
||||
for (let notificationId in this._notifications) {
|
||||
let notification = this._notifications[notificationId];
|
||||
notifications.push([notificationId, notification.serialize()]);
|
||||
}
|
||||
return [this._appId, notifications];
|
||||
},
|
||||
});
|
||||
|
||||
const GtkNotificationsIface = '<node> \
|
||||
<interface name="org.gtk.Notifications"> \
|
||||
<method name="AddNotification"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="a{sv}" direction="in" /> \
|
||||
</method> \
|
||||
<method name="RemoveNotification"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="s" direction="in" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const GtkNotificationDaemon = new Lang.Class({
|
||||
Name: 'GtkNotificationDaemon',
|
||||
|
||||
_init: function() {
|
||||
this._sources = {};
|
||||
|
||||
this._loadNotifications();
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
|
||||
|
||||
Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
_ensureAppSource: function(appId) {
|
||||
if (this._sources[appId])
|
||||
return this._sources[appId];
|
||||
|
||||
let source = new GtkNotificationDaemonAppSource(appId);
|
||||
|
||||
source.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._sources[appId];
|
||||
this._saveNotifications();
|
||||
}));
|
||||
source.connect('count-updated', Lang.bind(this, this._saveNotifications));
|
||||
Main.messageTray.add(source);
|
||||
this._sources[appId] = source;
|
||||
return source;
|
||||
},
|
||||
|
||||
_loadNotifications: function() {
|
||||
this._isLoading = true;
|
||||
|
||||
let value = global.get_persistent_state('a(sa(sv))', 'notifications');
|
||||
if (value) {
|
||||
let sources = value.deep_unpack();
|
||||
sources.forEach(Lang.bind(this, function([appId, notifications]) {
|
||||
if (notifications.length == 0)
|
||||
return;
|
||||
|
||||
let source;
|
||||
try {
|
||||
source = this._ensureAppSource(appId);
|
||||
} catch(e if e instanceof InvalidAppError) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifications.forEach(function([notificationId, notification]) {
|
||||
source.addNotification(notificationId, notification.deep_unpack(), false);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
this._isLoading = false;
|
||||
},
|
||||
|
||||
_saveNotifications: function() {
|
||||
if (this._isLoading)
|
||||
return;
|
||||
|
||||
let sources = [];
|
||||
for (let appId in this._sources) {
|
||||
let source = this._sources[appId];
|
||||
sources.push(source.serialize());
|
||||
}
|
||||
|
||||
global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
|
||||
},
|
||||
|
||||
AddNotificationAsync: function(params, invocation) {
|
||||
let [appId, notificationId, notification] = params;
|
||||
|
||||
let source;
|
||||
try {
|
||||
source = this._ensureAppSource(appId);
|
||||
} catch(e if e instanceof InvalidAppError) {
|
||||
invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
|
||||
return;
|
||||
}
|
||||
|
||||
source.addNotification(notificationId, notification, true);
|
||||
|
||||
invocation.return_value(null);
|
||||
},
|
||||
|
||||
RemoveNotificationAsync: function(params, invocation) {
|
||||
let [appId, notificationId] = params;
|
||||
let source = this._sources[appId];
|
||||
if (source)
|
||||
source.removeNotification(notificationId);
|
||||
|
||||
invocation.return_value(null);
|
||||
},
|
||||
});
|
||||
|
||||
const NotificationDaemon = new Lang.Class({
|
||||
Name: 'NotificationDaemon',
|
||||
|
||||
_init: function() {
|
||||
this._fdoNotificationDaemon = new FdoNotificationDaemon();
|
||||
this._gtkNotificationDaemon = new GtkNotificationDaemon();
|
||||
},
|
||||
});
|
||||
|
@ -78,10 +78,8 @@ const ShellInfo = new Lang.Class({
|
||||
}
|
||||
|
||||
this._undoCallback = undoCallback;
|
||||
if (undoCallback) {
|
||||
notification.addButton('system-undo', _("Undo"));
|
||||
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
|
||||
}
|
||||
if (undoCallback)
|
||||
notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked));
|
||||
|
||||
this._source.notify(notification);
|
||||
}
|
||||
@ -148,7 +146,7 @@ const Overview = new Lang.Class({
|
||||
// Dash elements, or mouseover handlers in the workspaces.
|
||||
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||
reactive: true });
|
||||
this._stack.add_actor(this._coverPane);
|
||||
Main.layoutManager.overviewGroup.add_child(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||
|
||||
this._stack.add_actor(this._overview);
|
||||
@ -265,7 +263,7 @@ const Overview = new Lang.Class({
|
||||
// Create controls
|
||||
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
|
||||
this._dash = this._controls.dash;
|
||||
this._viewSelector = this._controls.viewSelector;
|
||||
this.viewSelector = this._controls.viewSelector;
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
|
||||
@ -285,11 +283,11 @@ const Overview = new Lang.Class({
|
||||
},
|
||||
|
||||
addSearchProvider: function(provider) {
|
||||
this._viewSelector.addSearchProvider(provider);
|
||||
this.viewSelector.addSearchProvider(provider);
|
||||
},
|
||||
|
||||
removeSearchProvider: function(provider) {
|
||||
this._viewSelector.removeSearchProvider(provider);
|
||||
this.viewSelector.removeSearchProvider(provider);
|
||||
},
|
||||
|
||||
//
|
||||
@ -512,17 +510,8 @@ const Overview = new Lang.Class({
|
||||
this.visibleTarget = true;
|
||||
this._activationTime = Date.now() / 1000;
|
||||
|
||||
// All the the actors in the window group are completely obscured,
|
||||
// hiding the group holding them while the Overview is displayed greatly
|
||||
// increases performance of the Overview especially when there are many
|
||||
// windows visible.
|
||||
//
|
||||
// If we switched to displaying the actors in the Overview rather than
|
||||
// clones of them, this would obviously no longer be necessary.
|
||||
//
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this._viewSelector.show();
|
||||
this.viewSelector.show();
|
||||
|
||||
this._stack.opacity = 0;
|
||||
Tweener.addTween(this._stack,
|
||||
@ -629,7 +618,7 @@ const Overview = new Lang.Class({
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = false;
|
||||
|
||||
this._viewSelector.zoomFromOverview();
|
||||
this.viewSelector.zoomFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._stack,
|
||||
@ -664,7 +653,7 @@ const Overview = new Lang.Class({
|
||||
// Re-enable unredirection
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
|
||||
this._viewSelector.hide();
|
||||
this.viewSelector.hide();
|
||||
this._desktopFade.hide();
|
||||
this._coverPane.hide();
|
||||
|
||||
|
@ -55,18 +55,21 @@ const SlideLayout = new Lang.Class({
|
||||
vfunc_allocate: function(container, box, flags) {
|
||||
let child = container.get_first_child();
|
||||
|
||||
let [, , natWidth, natHeight] = child.get_preferred_size();
|
||||
let availWidth = Math.round(box.x2 - box.x1);
|
||||
let availHeight = Math.round(box.y2 - box.y1);
|
||||
let [, natWidth] = child.get_preferred_width(availHeight);
|
||||
|
||||
// Align the actor inside the clipped box, as the actor's alignment
|
||||
// flags only determine what to do if the allocated box is bigger
|
||||
// than the actor's box.
|
||||
let realDirection = getRtlSlideDirection(this._direction, child);
|
||||
let translationX = (realDirection == SlideDirection.LEFT) ?
|
||||
(availWidth - natWidth) : (natWidth - availWidth);
|
||||
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
|
||||
|
||||
let actorBox = new Clutter.ActorBox({ x1: translationX,
|
||||
y1: 0,
|
||||
x2: child.x_expand ? availWidth : natWidth,
|
||||
y2: child.y_expand ? availHeight : natHeight });
|
||||
let actorBox = new Clutter.ActorBox();
|
||||
actorBox.x1 = alignX;
|
||||
actorBox.x2 = actorBox.x1 + child.x_expand ? availWidth : natWidth;
|
||||
actorBox.y1 = 0;
|
||||
actorBox.y2 = actorBox.y1 + availHeight;
|
||||
|
||||
child.allocate(actorBox, flags);
|
||||
},
|
||||
@ -207,6 +210,7 @@ const SlidingControl = new Lang.Class({
|
||||
|
||||
slideIn: function() {
|
||||
this.visible = true;
|
||||
this._updateTranslation();
|
||||
// we will update slideX and the translation from pageEmpty
|
||||
},
|
||||
|
||||
@ -235,17 +239,13 @@ const ThumbnailsSlider = new Lang.Class({
|
||||
|
||||
this._thumbnailsBox = thumbnailsBox;
|
||||
|
||||
// SlideLayout reads the actor's expand flags to decide
|
||||
// whether to allocate the natural size to its child, or the whole
|
||||
// available allocation
|
||||
this._thumbnailsBox.actor.y_expand = true;
|
||||
|
||||
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
|
||||
this.actor.reactive = true;
|
||||
this.actor.track_hover = true;
|
||||
this.actor.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
|
||||
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
|
||||
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
},
|
||||
@ -273,6 +273,13 @@ const ThumbnailsSlider = new Lang.Class({
|
||||
return alwaysZoomOut;
|
||||
},
|
||||
|
||||
_onOverviewShowing: function() {
|
||||
this.visible = true;
|
||||
this.layout.slideX = this.getSlide();
|
||||
this.actor.translation_x = this._getTranslation();
|
||||
this.slideIn();
|
||||
},
|
||||
|
||||
getNonExpandedWidth: function() {
|
||||
let child = this.actor.get_first_child();
|
||||
return child.get_theme_node().get_length('visible-width');
|
||||
@ -315,7 +322,6 @@ const DashSlider = new Lang.Class({
|
||||
// whether to allocate the natural size to its child, or the whole
|
||||
// available allocation
|
||||
this._dash.actor.x_expand = true;
|
||||
this._dash.actor.y_expand = true;
|
||||
|
||||
this.actor.x_align = Clutter.ActorAlign.START;
|
||||
this.actor.y_expand = true;
|
||||
@ -323,6 +329,7 @@ const DashSlider = new Lang.Class({
|
||||
this.actor.add_actor(this._dash.actor);
|
||||
|
||||
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
|
||||
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
@ -332,6 +339,13 @@ const DashSlider = new Lang.Class({
|
||||
return 0;
|
||||
},
|
||||
|
||||
_onOverviewShowing: function() {
|
||||
this.visible = true;
|
||||
this.layout.slideX = this.getSlide();
|
||||
this.actor.translation_x = this._getTranslation();
|
||||
this.slideIn();
|
||||
},
|
||||
|
||||
_onWindowDragBegin: function() {
|
||||
this.fadeHalf();
|
||||
},
|
||||
@ -487,6 +501,17 @@ const MessagesIndicator = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const ControlsLayout = new Lang.Class({
|
||||
Name: 'ControlsLayout',
|
||||
Extends: Clutter.BinLayout,
|
||||
Signals: { 'allocation-changed': { flags: GObject.SignalFlags.RUN_LAST } },
|
||||
|
||||
vfunc_allocate: function(container, box, flags) {
|
||||
this.parent(container, box, flags);
|
||||
this.emit('allocation-changed');
|
||||
}
|
||||
});
|
||||
|
||||
const ControlsManager = new Lang.Class({
|
||||
Name: 'ControlsManager',
|
||||
|
||||
@ -507,7 +532,8 @@ const ControlsManager = new Lang.Class({
|
||||
this._indicator = new MessagesIndicator(this.viewSelector);
|
||||
this.indicatorActor = this._indicator.actor;
|
||||
|
||||
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||
let layout = new ControlsLayout();
|
||||
this.actor = new St.Widget({ layout_manager: layout,
|
||||
reactive: true,
|
||||
x_expand: true, y_expand: true,
|
||||
clip_to_allocation: true });
|
||||
@ -522,7 +548,7 @@ const ControlsManager = new Lang.Class({
|
||||
expand: true });
|
||||
this._group.add_actor(this._thumbnailsSlider.actor);
|
||||
|
||||
this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
|
||||
layout.connect('allocation-changed', Lang.bind(this, this._updateWorkspacesGeometry));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
|
||||
Main.overview.connect('item-drag-begin', Lang.bind(this,
|
||||
|
257
js/ui/panel.js
@ -189,7 +189,6 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
|
||||
this.actor.reactive = false;
|
||||
this._targetIsCurrent = false;
|
||||
|
||||
this._container = new Shell.GenericContainer();
|
||||
bin.set_child(this._container);
|
||||
@ -207,20 +206,23 @@ const AppMenuButton = new Lang.Class({
|
||||
this._iconBox.connect('notify::allocation',
|
||||
Lang.bind(this, this._updateIconBoxClip));
|
||||
this._container.add_actor(this._iconBox);
|
||||
|
||||
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
this._container.add_actor(this._hbox);
|
||||
|
||||
this._label = new TextShadower();
|
||||
this._container.add_actor(this._label.actor);
|
||||
this._label.actor.y_align = Clutter.ActorAlign.CENTER;
|
||||
this._hbox.add_actor(this._label.actor);
|
||||
this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
|
||||
this._hbox.add_actor(this._arrow);
|
||||
|
||||
this._iconBottomClip = 0;
|
||||
|
||||
this._visible = !Main.overview.visible;
|
||||
if (!this._visible)
|
||||
this.actor.hide();
|
||||
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
this.show();
|
||||
}));
|
||||
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
|
||||
this.hide();
|
||||
}));
|
||||
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync));
|
||||
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync));
|
||||
|
||||
this._stop = true;
|
||||
|
||||
@ -243,13 +245,8 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._visible = true;
|
||||
this.actor.show();
|
||||
|
||||
if (!this._targetIsCurrent)
|
||||
return;
|
||||
|
||||
this.actor.reactive = true;
|
||||
|
||||
this.actor.show();
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
@ -263,11 +260,6 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
this._visible = false;
|
||||
this.actor.reactive = false;
|
||||
if (!this._targetIsCurrent) {
|
||||
this.actor.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
@ -286,9 +278,8 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
this._spinnerIcon = icon;
|
||||
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||
this._container.add_actor(this._spinner.actor);
|
||||
this._hbox.add_actor(this._spinner.actor);
|
||||
this._spinner.actor.hide();
|
||||
this._spinner.actor.lower_bottom();
|
||||
},
|
||||
|
||||
_onIconBoxStyleChanged: function() {
|
||||
@ -298,6 +289,9 @@ const AppMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_syncIcon: function() {
|
||||
if (!this._targetApp)
|
||||
return;
|
||||
|
||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
|
||||
this._iconBox.set_child(icon);
|
||||
},
|
||||
@ -324,7 +318,6 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._stop = true;
|
||||
this.actor.reactive = true;
|
||||
|
||||
if (this._spinner == null)
|
||||
return;
|
||||
@ -344,7 +337,6 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
startAnimation: function() {
|
||||
this._stop = false;
|
||||
this.actor.reactive = false;
|
||||
|
||||
if (this._spinner == null)
|
||||
return;
|
||||
@ -357,7 +349,7 @@ const AppMenuButton = new Lang.Class({
|
||||
let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight);
|
||||
alloc.min_size = minSize;
|
||||
alloc.natural_size = naturalSize;
|
||||
[minSize, naturalSize] = this._label.actor.get_preferred_width(forHeight);
|
||||
[minSize, naturalSize] = this._hbox.get_preferred_width(forHeight);
|
||||
alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2));
|
||||
alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2));
|
||||
},
|
||||
@ -366,7 +358,7 @@ const AppMenuButton = new Lang.Class({
|
||||
let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth);
|
||||
alloc.min_size = minSize;
|
||||
alloc.natural_size = naturalSize;
|
||||
[minSize, naturalSize] = this._label.actor.get_preferred_height(forWidth);
|
||||
[minSize, naturalSize] = this._hbox.get_preferred_height(forWidth);
|
||||
if (minSize > alloc.min_size)
|
||||
alloc.min_size = minSize;
|
||||
if (naturalSize > alloc.natural_size)
|
||||
@ -396,11 +388,10 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
let iconWidth = childBox.x2 - childBox.x1;
|
||||
|
||||
[minWidth, minHeight, naturalWidth, naturalHeight] = this._label.actor.get_preferred_size();
|
||||
[minWidth, naturalWidth] = this._hbox.get_preferred_width(-1);
|
||||
|
||||
yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2);
|
||||
childBox.y1 = yPadding;
|
||||
childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight);
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
|
||||
if (direction == Clutter.TextDirection.LTR) {
|
||||
childBox.x1 = Math.floor(iconWidth / 2);
|
||||
@ -409,24 +400,7 @@ const AppMenuButton = new Lang.Class({
|
||||
childBox.x2 = allocWidth - Math.floor(iconWidth / 2);
|
||||
childBox.x1 = Math.max(0, childBox.x2 - naturalWidth);
|
||||
}
|
||||
this._label.actor.allocate(childBox, flags);
|
||||
|
||||
if (this._spinner == null)
|
||||
return;
|
||||
|
||||
if (direction == Clutter.TextDirection.LTR) {
|
||||
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
|
||||
childBox.x2 = childBox.x1 + this._spinner.actor.width;
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y2 - 1;
|
||||
this._spinner.actor.allocate(childBox, flags);
|
||||
} else {
|
||||
childBox.x1 = -this._spinner.actor.width;
|
||||
childBox.x2 = childBox.x1 + this._spinner.actor.width;
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y2 - 1;
|
||||
this._spinner.actor.allocate(childBox, flags);
|
||||
}
|
||||
this._hbox.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_onAppStateChanged: function(appSys, app) {
|
||||
@ -458,96 +432,70 @@ const AppMenuButton = new Lang.Class({
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
_findTargetApp: function() {
|
||||
let workspace = global.screen.get_active_workspace();
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let focusedApp = tracker.focus_app;
|
||||
let lastStartedApp = null;
|
||||
let workspace = global.screen.get_active_workspace();
|
||||
if (focusedApp && focusedApp.is_on_workspace(workspace))
|
||||
return focusedApp;
|
||||
|
||||
for (let i = 0; i < this._startingApps.length; i++)
|
||||
if (this._startingApps[i].is_on_workspace(workspace))
|
||||
lastStartedApp = this._startingApps[i];
|
||||
return this._startingApps[i];
|
||||
|
||||
let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
|
||||
return null;
|
||||
},
|
||||
|
||||
if (targetApp == null) {
|
||||
if (!this._targetIsCurrent)
|
||||
return;
|
||||
_sync: function() {
|
||||
let targetApp = this._findTargetApp();
|
||||
|
||||
this.actor.reactive = false;
|
||||
this._targetIsCurrent = false;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor, { opacity: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetApp.is_on_workspace(workspace))
|
||||
return;
|
||||
|
||||
if (!this._targetIsCurrent) {
|
||||
this.actor.reactive = true;
|
||||
this._targetIsCurrent = true;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor, { opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
||||
if (targetApp == this._targetApp) {
|
||||
if (targetApp &&
|
||||
targetApp.get_state() != Shell.AppState.STARTING &&
|
||||
targetApp.get_state() != Shell.AppState.BUSY) {
|
||||
this.stopAnimation();
|
||||
this._maybeSetMenu();
|
||||
} else if (targetApp &&
|
||||
targetApp.get_state() == Shell.AppState.BUSY) {
|
||||
this.startAnimation();
|
||||
if (this._targetApp != targetApp) {
|
||||
if (this._appMenuNotifyId) {
|
||||
this._targetApp.disconnect(this._appMenuNotifyId);
|
||||
this._appMenuNotifyId = 0;
|
||||
}
|
||||
if (this._actionGroupNotifyId) {
|
||||
this._targetApp.disconnect(this._actionGroupNotifyId);
|
||||
this._actionGroupNotifyId = 0;
|
||||
}
|
||||
|
||||
this._targetApp = targetApp;
|
||||
|
||||
if (this._targetApp) {
|
||||
this._appMenuNotifyId = this._targetApp.connect('notify::menu', Lang.bind(this, this._sync));
|
||||
this._actionGroupNotifyId = this._targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
|
||||
this._label.setText(this._targetApp.get_name());
|
||||
this.actor.set_accessible_name(this._targetApp.get_name());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._spinner)
|
||||
this._spinner.actor.hide();
|
||||
if (this._iconBox.child != null)
|
||||
this._iconBox.child.destroy();
|
||||
this._iconBox.hide();
|
||||
this._label.setText('');
|
||||
let visible = (this._targetApp != null && !Main.overview.visibleTarget);
|
||||
if (visible)
|
||||
this.show();
|
||||
else
|
||||
this.hide();
|
||||
|
||||
if (this._appMenuNotifyId)
|
||||
this._targetApp.disconnect(this._appMenuNotifyId);
|
||||
if (this._actionGroupNotifyId)
|
||||
this._targetApp.disconnect(this._actionGroupNotifyId);
|
||||
if (targetApp) {
|
||||
this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
|
||||
this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
|
||||
} else {
|
||||
this._appMenuNotifyId = 0;
|
||||
this._actionGroupNotifyId = 0;
|
||||
}
|
||||
|
||||
this._targetApp = targetApp;
|
||||
this._label.setText(targetApp.get_name());
|
||||
this.setName(targetApp.get_name());
|
||||
|
||||
this._syncIcon();
|
||||
this._iconBox.show();
|
||||
|
||||
if (targetApp.get_state() == Shell.AppState.STARTING ||
|
||||
targetApp.get_state() == Shell.AppState.BUSY)
|
||||
let isBusy = (this._targetApp != null &&
|
||||
(this._targetApp.get_state() == Shell.AppState.STARTING ||
|
||||
this._targetApp.get_state() == Shell.AppState.BUSY));
|
||||
if (isBusy)
|
||||
this.startAnimation();
|
||||
else
|
||||
this._maybeSetMenu();
|
||||
this.stopAnimation();
|
||||
|
||||
this.actor.reactive = (visible && !isBusy);
|
||||
|
||||
this._syncIcon();
|
||||
this._maybeSetMenu();
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_maybeSetMenu: function() {
|
||||
let menu;
|
||||
|
||||
if (this._targetApp.action_group && this._targetApp.menu) {
|
||||
if (this._targetApp == null) {
|
||||
menu = null;
|
||||
} else if (this._targetApp.action_group && this._targetApp.menu) {
|
||||
if (this.menu instanceof RemoteMenu.RemoteMenu &&
|
||||
this.menu.actionGroup == this._targetApp.action_group)
|
||||
return;
|
||||
@ -559,7 +507,7 @@ const AppMenuButton = new Lang.Class({
|
||||
}));
|
||||
|
||||
} else {
|
||||
if (this.menu.isDummyQuitMenu)
|
||||
if (this.menu && this.menu.isDummyQuitMenu)
|
||||
return;
|
||||
|
||||
// fallback to older menu
|
||||
@ -571,7 +519,8 @@ const AppMenuButton = new Lang.Class({
|
||||
}
|
||||
|
||||
this.setMenu(menu);
|
||||
this._menuManager.addMenu(menu);
|
||||
if (menu)
|
||||
this._menuManager.addMenu(menu);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -616,7 +565,8 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
/* 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") });
|
||||
this._label = new St.Label({ text: _("Activities"),
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_actor(this._label);
|
||||
|
||||
this.actor.label_actor = this._label;
|
||||
@ -659,6 +609,7 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
_onButtonRelease: function() {
|
||||
Main.overview.toggle();
|
||||
this.menu.close();
|
||||
},
|
||||
|
||||
_onKeyRelease: function(actor, event) {
|
||||
@ -847,31 +798,65 @@ const PanelCorner = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const AggregateMenu = new Lang.Class({
|
||||
Name: 'AggregateMenu',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, _("Settings"), false);
|
||||
this.menu.actor.add_style_class_name('aggregate-menu');
|
||||
|
||||
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
|
||||
this.actor.add_child(this._indicators);
|
||||
|
||||
this._network = new imports.ui.status.network.NMApplet();
|
||||
if (Config.HAVE_BLUETOOTH) {
|
||||
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
|
||||
} else {
|
||||
this._bluetooth = null;
|
||||
}
|
||||
|
||||
this._power = new imports.ui.status.power.Indicator();
|
||||
this._rfkill = new imports.ui.status.rfkill.Indicator();
|
||||
this._volume = new imports.ui.status.volume.Indicator();
|
||||
this._brightness = new imports.ui.status.brightness.Indicator();
|
||||
this._system = new imports.ui.status.system.Indicator();
|
||||
this._screencast = new imports.ui.status.screencast.Indicator();
|
||||
|
||||
this._indicators.add_child(this._screencast.indicators);
|
||||
this._indicators.add_child(this._network.indicators);
|
||||
if (this._bluetooth) {
|
||||
this._indicators.add_child(this._bluetooth.indicators);
|
||||
}
|
||||
this._indicators.add_child(this._rfkill.indicators);
|
||||
this._indicators.add_child(this._volume.indicators);
|
||||
this._indicators.add_child(this._power.indicators);
|
||||
this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
|
||||
|
||||
this.menu.addMenuItem(this._volume.menu);
|
||||
this.menu.addMenuItem(this._brightness.menu);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addMenuItem(this._network.menu);
|
||||
if (this._bluetooth) {
|
||||
this.menu.addMenuItem(this._bluetooth.menu);
|
||||
}
|
||||
this.menu.addMenuItem(this._rfkill.menu);
|
||||
this.menu.addMenuItem(this._power.menu);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addMenuItem(this._system.menu);
|
||||
},
|
||||
});
|
||||
|
||||
const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||
'activities': ActivitiesButton,
|
||||
'aggregateMenu': AggregateMenu,
|
||||
'appMenu': AppMenuButton,
|
||||
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||
'system': imports.ui.status.system.Indicator,
|
||||
};
|
||||
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
|
||||
imports.ui.status.bluetooth.Indicator;
|
||||
|
||||
try {
|
||||
PANEL_ITEM_IMPLEMENTATIONS['network'] =
|
||||
imports.ui.status.network.NMApplet;
|
||||
} catch(e) {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
const Panel = new Lang.Class({
|
||||
Name: 'Panel',
|
||||
|
||||
|
@ -86,13 +86,8 @@ const ButtonBox = new Lang.Class({
|
||||
childBox.x2 = availWidth - this._minHPadding;
|
||||
}
|
||||
|
||||
if (natHeight <= availHeight) {
|
||||
childBox.y1 = Math.floor((availHeight - natHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + natHeight;
|
||||
} else {
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = availHeight;
|
||||
}
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = availHeight;
|
||||
|
||||
child.allocate(childBox, flags);
|
||||
},
|
||||
@ -106,6 +101,7 @@ const Button = new Lang.Class({
|
||||
this.parent({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
accessible_name: nameText ? nameText : "",
|
||||
accessible_role: Atk.Role.MENU });
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
@ -116,8 +112,6 @@ const Button = new Lang.Class({
|
||||
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
||||
else
|
||||
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
||||
|
||||
this.setName(nameText);
|
||||
},
|
||||
|
||||
setSensitive: function(sensitive) {
|
||||
@ -126,22 +120,6 @@ const Button = new Lang.Class({
|
||||
this.actor.track_hover = sensitive;
|
||||
},
|
||||
|
||||
setName: function(text) {
|
||||
if (text != null) {
|
||||
// This is the easiest way to provide a accessible name to
|
||||
// this widget. The label could be also used for other
|
||||
// purposes in the future.
|
||||
if (!this.label) {
|
||||
this.label = new St.Label({ text: text });
|
||||
this.actor.label_actor = this.label;
|
||||
} else
|
||||
this.label.text = text;
|
||||
} else {
|
||||
this.label = null;
|
||||
this.actor.label_actor = null;
|
||||
}
|
||||
},
|
||||
|
||||
setMenu: function(menu) {
|
||||
if (this.menu)
|
||||
this.menu.destroy();
|
||||
@ -233,51 +211,35 @@ const Button = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(Button.prototype);
|
||||
|
||||
/* SystemStatusButton:
|
||||
/* SystemIndicator:
|
||||
*
|
||||
* This class manages one System Status indicator (network, keyboard,
|
||||
* volume, bluetooth...), which is just a PanelMenuButton with an
|
||||
* icon.
|
||||
* This class manages one system indicator, which are the icons
|
||||
* that you see at the top right. A system indicator is composed
|
||||
* of an icon and a menu section, which will be composed into the
|
||||
* aggregate menu.
|
||||
*/
|
||||
const SystemStatusButton = new Lang.Class({
|
||||
Name: 'SystemStatusButton',
|
||||
Extends: Button,
|
||||
const SystemIndicator = new Lang.Class({
|
||||
Name: 'SystemIndicator',
|
||||
|
||||
_init: function(iconName, nameText) {
|
||||
this.parent(0.0, nameText);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||
this.actor.add_actor(this._box);
|
||||
|
||||
if (iconName)
|
||||
this.setIcon(iconName);
|
||||
_init: function() {
|
||||
this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box',
|
||||
reactive: true });
|
||||
this.indicators.hide();
|
||||
this.menu = new PopupMenu.PopupMenuSection();
|
||||
},
|
||||
|
||||
get icons() {
|
||||
return this._box.get_children();
|
||||
_syncIndicatorsVisible: function() {
|
||||
this.indicators.visible = this.indicators.get_children().some(function(actor) {
|
||||
return actor.visible;
|
||||
});
|
||||
},
|
||||
|
||||
addIcon: function(gicon) {
|
||||
let icon = new St.Icon({ gicon: gicon,
|
||||
style_class: 'system-status-icon' });
|
||||
this._box.add_actor(icon);
|
||||
|
||||
this.emit('icons-changed');
|
||||
|
||||
_addIndicator: function() {
|
||||
let icon = new St.Icon({ style_class: 'system-status-icon' });
|
||||
this.indicators.add_actor(icon);
|
||||
icon.connect('notify::visible', Lang.bind(this, this._syncIndicatorsVisible));
|
||||
this._syncIndicatorsVisible();
|
||||
return icon;
|
||||
},
|
||||
|
||||
setIcon: function(iconName) {
|
||||
if (!this.mainIcon)
|
||||
this.mainIcon = this.addIcon(null);
|
||||
this.mainIcon.icon_name = iconName;
|
||||
},
|
||||
|
||||
setGIcon: function(gicon) {
|
||||
if (this.mainIcon)
|
||||
this.mainIcon.gicon = gicon;
|
||||
else
|
||||
this.mainIcon = this.addIcon(gicon);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SystemIndicator.prototype);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
@ -41,7 +42,7 @@ const PointerWatcher = new Lang.Class({
|
||||
Name: 'PointerWatcher',
|
||||
|
||||
_init: function() {
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleMonitor = Meta.IdleMonitor.get_core();
|
||||
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
|
||||
this._watches = [];
|
||||
|
@ -35,6 +35,40 @@ function _ensureStyle(actor) {
|
||||
actor.ensure_style();
|
||||
}
|
||||
|
||||
function isPopupMenuItemVisible(child) {
|
||||
if (child._delegate instanceof PopupMenuSection)
|
||||
if (child._delegate.isEmpty())
|
||||
return false;
|
||||
return child.visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @side Side to which the arrow points.
|
||||
*/
|
||||
function unicodeArrow(side) {
|
||||
let arrowChar;
|
||||
switch (side) {
|
||||
case St.Side.TOP:
|
||||
arrowChar = '\u25B4';
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
arrowChar = '\u25B8';
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
arrowChar = '\u25BE';
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
arrowChar = '\u25C2';
|
||||
break;
|
||||
}
|
||||
|
||||
return new St.Label({ text: arrowChar,
|
||||
style_class: 'unicode-arrow',
|
||||
accessible_role: Atk.Role.ARROW,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
}
|
||||
|
||||
const PopupBaseMenuItem = new Lang.Class({
|
||||
Name: 'PopupBaseMenuItem',
|
||||
|
||||
@ -45,24 +79,19 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
style_class: null,
|
||||
can_focus: true
|
||||
});
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
|
||||
reactive: params.reactive,
|
||||
track_hover: params.reactive,
|
||||
can_focus: params.can_focus,
|
||||
accessible_role: Atk.Role.MENU_ITEM});
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'popup-menu-item',
|
||||
reactive: params.reactive,
|
||||
track_hover: params.reactive,
|
||||
can_focus: params.can_focus,
|
||||
accessible_role: Atk.Role.MENU_ITEM });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._parent = null;
|
||||
this._children = [];
|
||||
this._ornament = Ornament.NONE;
|
||||
this._ornamentLabel = new St.Label({ style_class: 'popup-menu-ornament' });
|
||||
this.actor.add_actor(this._ornamentLabel);
|
||||
this._columnWidths = null;
|
||||
this._spacing = 0;
|
||||
this.actor.add(this._ornamentLabel);
|
||||
|
||||
this._parent = null;
|
||||
this.active = false;
|
||||
this._activatable = params.reactive && params.activate;
|
||||
this._sensitive = true;
|
||||
@ -95,10 +124,6 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
this._parent = parent;
|
||||
},
|
||||
|
||||
_onStyleChanged: function (actor) {
|
||||
this._spacing = Math.round(actor.get_theme_node().get_length('spacing'));
|
||||
},
|
||||
|
||||
_onButtonReleaseEvent: function (actor, event) {
|
||||
this.activate(event);
|
||||
return true;
|
||||
@ -170,31 +195,6 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
this.emit('destroy');
|
||||
},
|
||||
|
||||
// adds an actor to the menu item; @params can contain
|
||||
// %expand (defaults to #false), and %align (defaults to
|
||||
// #St.Align.START)
|
||||
addActor: function(child, params) {
|
||||
params = Params.parse(params, { expand: false, align: St.Align.START });
|
||||
params.actor = child;
|
||||
this._children.push(params);
|
||||
this.actor.connect('destroy', Lang.bind(this, function () { this._removeChild(child); }));
|
||||
this.actor.add_actor(child);
|
||||
},
|
||||
|
||||
_removeChild: function(child) {
|
||||
for (let i = 0; i < this._children.length; i++) {
|
||||
if (this._children[i].actor == child) {
|
||||
this._children.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
removeActor: function(child) {
|
||||
this.actor.remove_actor(child);
|
||||
this._removeChild(child);
|
||||
},
|
||||
|
||||
setOrnament: function(ornament) {
|
||||
if (ornament == this._ornament)
|
||||
return;
|
||||
@ -211,132 +211,6 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
this._ornamentLabel.text = '';
|
||||
this.actor.remove_accessible_state(Atk.StateType.CHECKED);
|
||||
}
|
||||
},
|
||||
|
||||
// This returns column widths in logical order (i.e. from the dot
|
||||
// to the image), not in visual order (left to right)
|
||||
getColumnWidths: function() {
|
||||
let widths = [];
|
||||
for (let i = 0, col = 0; i < this._children.length; i++) {
|
||||
let child = this._children[i];
|
||||
let [min, natural] = child.actor.get_preferred_width(-1);
|
||||
widths[col++] = natural;
|
||||
}
|
||||
return widths;
|
||||
},
|
||||
|
||||
setColumnWidths: function(widths) {
|
||||
this._columnWidths = widths;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
let width = 0;
|
||||
if (this._columnWidths) {
|
||||
for (let i = 0; i < this._columnWidths.length; i++) {
|
||||
if (i > 0)
|
||||
width += this._spacing;
|
||||
width += this._columnWidths[i];
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._children.length; i++) {
|
||||
let child = this._children[i];
|
||||
if (i > 0)
|
||||
width += this._spacing;
|
||||
let [min, natural] = child.actor.get_preferred_width(-1);
|
||||
width += natural;
|
||||
}
|
||||
}
|
||||
alloc.min_size = alloc.natural_size = width;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let height = 0, x = 0, minWidth, childWidth;
|
||||
for (let i = 0; i < this._children.length; i++) {
|
||||
let child = this._children[i];
|
||||
if (this._columnWidths) {
|
||||
if (child.expand) {
|
||||
childWidth = 0;
|
||||
for (let j = i; j < this._columnWidths.length; j++)
|
||||
childWidth += this._columnWidths[j];
|
||||
} else
|
||||
childWidth = this._columnWidths[i];
|
||||
} else {
|
||||
if (child.expand)
|
||||
childWidth = forWidth - x;
|
||||
else
|
||||
[minWidth, childWidth] = child.actor.get_preferred_width(-1);
|
||||
}
|
||||
x += childWidth;
|
||||
|
||||
let [min, natural] = child.actor.get_preferred_height(childWidth);
|
||||
if (natural > height)
|
||||
height = natural;
|
||||
}
|
||||
alloc.min_size = alloc.natural_size = height;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let width = box.x2 - box.x1;
|
||||
let height = box.y2 - box.y1;
|
||||
let direction = this.actor.get_text_direction();
|
||||
|
||||
// The ornament is placed outside box
|
||||
// one quarter of padding from the border of the container
|
||||
// (so 3/4 from the inner border)
|
||||
// (padding is box.x1)
|
||||
let ornamentBox = new Clutter.ActorBox();
|
||||
let ornamentWidth = box.x1;
|
||||
|
||||
ornamentBox.x1 = 0;
|
||||
ornamentBox.x2 = ornamentWidth;
|
||||
ornamentBox.y1 = box.y1;
|
||||
ornamentBox.y2 = box.y2;
|
||||
|
||||
if (direction == Clutter.TextDirection.RTL) {
|
||||
ornamentBox.x1 += box.x2;
|
||||
ornamentBox.x2 += box.x2;
|
||||
}
|
||||
|
||||
this._ornamentLabel.allocate(ornamentBox, flags);
|
||||
|
||||
let x = box.x1;
|
||||
for (let i = 0, col = 0; i < this._children.length; i++) {
|
||||
let child = this._children[i];
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
let [minWidth, naturalWidth] = child.actor.get_preferred_width(-1);
|
||||
|
||||
let availWidth;
|
||||
if (child.expand) {
|
||||
availWidth = box.x2 - x;
|
||||
} else {
|
||||
if (this._columnWidths)
|
||||
availWidth = this._columnWidths[col++];
|
||||
else
|
||||
availWidth = naturalWidth;
|
||||
}
|
||||
|
||||
if (direction == Clutter.TextDirection.RTL)
|
||||
childBox.x1 = box.x1 + (box.x2 - x - availWidth);
|
||||
else
|
||||
childBox.x1 = x;
|
||||
childBox.x2 = childBox.x1 + availWidth;
|
||||
|
||||
let [minHeight, naturalHeight] = child.actor.get_preferred_height(childBox.x2 - childBox.x1);
|
||||
childBox.y1 = Math.round(box.y1 + (height - naturalHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + naturalHeight;
|
||||
|
||||
let [xAlign, yAlign] = St.get_align_factors(child.align,
|
||||
St.Align.MIDDLE);
|
||||
|
||||
// It's called "expand", but it's really more like "fill"
|
||||
let xFill = child.expand;
|
||||
child.actor.allocate_align_fill(childBox,
|
||||
xAlign, yAlign,
|
||||
xFill, true,
|
||||
flags);
|
||||
x += availWidth + this._spacing;
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PopupBaseMenuItem.prototype);
|
||||
@ -349,7 +223,7 @@ const PopupMenuItem = new Lang.Class({
|
||||
this.parent(params);
|
||||
|
||||
this.label = new St.Label({ text: text });
|
||||
this.addActor(this.label);
|
||||
this.actor.add_child(this.label);
|
||||
this.actor.label_actor = this.label
|
||||
}
|
||||
});
|
||||
@ -362,114 +236,12 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
||||
this.parent({ reactive: false,
|
||||
can_focus: false});
|
||||
|
||||
this._box = new St.BoxLayout();
|
||||
this.addActor(this._box, { expand: true });
|
||||
|
||||
this.label = new St.Label({ text: text || '' });
|
||||
this._box.add(this.label);
|
||||
this.actor.add(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
|
||||
this._box.add(this._separator.actor, { expand: true });
|
||||
}
|
||||
});
|
||||
|
||||
const PopupAlternatingMenuItemState = {
|
||||
DEFAULT: 0,
|
||||
ALTERNATIVE: 1
|
||||
}
|
||||
|
||||
const PopupAlternatingMenuItem = new Lang.Class({
|
||||
Name: 'PopupAlternatingMenuItem',
|
||||
Extends: PopupBaseMenuItem,
|
||||
|
||||
_init: function(text, alternateText, params) {
|
||||
this.parent(params);
|
||||
this.actor.add_style_class_name('popup-alternating-menu-item');
|
||||
|
||||
this._text = text;
|
||||
this._alternateText = alternateText;
|
||||
this.label = new St.Label({ text: text });
|
||||
this.state = PopupAlternatingMenuItemState.DEFAULT;
|
||||
this.addActor(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||
},
|
||||
|
||||
_onMapped: function() {
|
||||
if (this.actor.mapped) {
|
||||
this._capturedEventId = global.stage.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
this._updateStateFromModifiers();
|
||||
} else {
|
||||
if (this._capturedEventId != 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_setState: function(state) {
|
||||
if (this.state != state) {
|
||||
if (state == PopupAlternatingMenuItemState.ALTERNATIVE && !this._canAlternate())
|
||||
return;
|
||||
|
||||
this.state = state;
|
||||
this._updateLabel();
|
||||
}
|
||||
},
|
||||
|
||||
_updateStateFromModifiers: function() {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
let state;
|
||||
|
||||
if ((mods & Clutter.ModifierType.MOD1_MASK) == 0) {
|
||||
state = PopupAlternatingMenuItemState.DEFAULT;
|
||||
} else {
|
||||
state = PopupAlternatingMenuItemState.ALTERNATIVE;
|
||||
}
|
||||
|
||||
this._setState(state);
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() != Clutter.EventType.KEY_PRESS &&
|
||||
event.type() != Clutter.EventType.KEY_RELEASE)
|
||||
return false;
|
||||
|
||||
let key = event.get_key_symbol();
|
||||
|
||||
if (key == Clutter.KEY_Alt_L || key == Clutter.KEY_Alt_R)
|
||||
this._updateStateFromModifiers();
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_updateLabel: function() {
|
||||
if (this.state == PopupAlternatingMenuItemState.ALTERNATIVE) {
|
||||
this.actor.add_style_pseudo_class('alternate');
|
||||
this.label.set_text(this._alternateText);
|
||||
} else {
|
||||
this.actor.remove_style_pseudo_class('alternate');
|
||||
this.label.set_text(this._text);
|
||||
}
|
||||
},
|
||||
|
||||
_canAlternate: function() {
|
||||
if (this.state == PopupAlternatingMenuItemState.DEFAULT && !this._alternateText)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
updateText: function(text, alternateText) {
|
||||
this._text = text;
|
||||
this._alternateText = alternateText;
|
||||
|
||||
if (!this._canAlternate())
|
||||
this._setState(PopupAlternatingMenuItemState.DEFAULT);
|
||||
|
||||
this._updateLabel();
|
||||
this.actor.add(this._separator.actor, { expand: true });
|
||||
}
|
||||
});
|
||||
|
||||
@ -516,10 +288,10 @@ const PopupSwitchMenuItem = new Lang.Class({
|
||||
this.checkAccessibleState();
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.addActor(this.label);
|
||||
this.actor.add_child(this.label);
|
||||
|
||||
this._statusBin = new St.Bin({ x_align: St.Align.END });
|
||||
this.addActor(this._statusBin, { expand: true, align: St.Align.END });
|
||||
this.actor.add(this._statusBin, { expand: true, x_align: St.Align.END });
|
||||
|
||||
this._statusLabel = new St.Label({ text: '',
|
||||
style_class: 'popup-status-menu-item'
|
||||
@ -618,7 +390,6 @@ const PopupMenuBase = new Lang.Class({
|
||||
} else {
|
||||
this.box = new St.BoxLayout({ vertical: true });
|
||||
}
|
||||
this.box.connect_after('queue-relayout', Lang.bind(this, this._menuQueueRelayout));
|
||||
this.length = 0;
|
||||
|
||||
this.isOpen = false;
|
||||
@ -700,7 +471,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
let hasVisibleChildren = this.box.get_children().some(function(child) {
|
||||
if (child._delegate instanceof PopupSeparatorMenuItem)
|
||||
return false;
|
||||
return child.visible;
|
||||
return isPopupMenuItemVisible(child);
|
||||
});
|
||||
|
||||
return !hasVisibleChildren;
|
||||
@ -781,7 +552,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
let childBeforeIndex = index - 1;
|
||||
|
||||
while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible)
|
||||
while (childBeforeIndex >= 0 && !isPopupMenuItemVisible(children[childBeforeIndex]))
|
||||
childBeforeIndex--;
|
||||
|
||||
if (childBeforeIndex < 0
|
||||
@ -792,7 +563,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
let childAfterIndex = index + 1;
|
||||
|
||||
while (childAfterIndex < children.length && !children[childAfterIndex].visible)
|
||||
while (childAfterIndex < children.length && !isPopupMenuItemVisible(children[childAfterIndex]))
|
||||
childAfterIndex++;
|
||||
|
||||
if (childAfterIndex >= children.length
|
||||
@ -819,9 +590,6 @@ const PopupMenuBase = new Lang.Class({
|
||||
}
|
||||
|
||||
if (menuItem instanceof PopupMenuSection) {
|
||||
let activateId = menuItem.connect('activate', Lang.bind(this, function() {
|
||||
this.emit('activate');
|
||||
}));
|
||||
let activeChangeId = menuItem.connect('active-changed', Lang.bind(this, this._subMenuActiveChanged));
|
||||
|
||||
let parentOpenStateChangedId = this.connect('open-state-changed', function(self, open) {
|
||||
@ -838,7 +606,6 @@ const PopupMenuBase = new Lang.Class({
|
||||
}));
|
||||
|
||||
menuItem.connect('destroy', Lang.bind(this, function() {
|
||||
menuItem.disconnect(activateId);
|
||||
menuItem.disconnect(activeChangeId);
|
||||
this.disconnect(subMenuSensitiveChangedId);
|
||||
this.disconnect(parentOpenStateChangedId);
|
||||
@ -852,16 +619,12 @@ const PopupMenuBase = new Lang.Class({
|
||||
this.box.insert_child_below(menuItem.menu.actor, before_item);
|
||||
|
||||
this._connectItemSignals(menuItem);
|
||||
let subMenuActivateId = menuItem.connect('activate', Lang.bind(this, function() {
|
||||
this.emit('activate');
|
||||
}));
|
||||
let subMenuActiveChangeId = menuItem.menu.connect('active-changed', Lang.bind(this, this._subMenuActiveChanged));
|
||||
let closingId = this.connect('menu-closed', function() {
|
||||
menuItem.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
});
|
||||
|
||||
menuItem.connect('destroy', Lang.bind(this, function() {
|
||||
menuItem.menu.disconnect(subMenuActivateId);
|
||||
menuItem.menu.disconnect(subMenuActiveChangeId);
|
||||
this.disconnect(closingId);
|
||||
}));
|
||||
@ -872,7 +635,11 @@ const PopupMenuBase = new Lang.Class({
|
||||
// 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); }));
|
||||
let openStateChangeId = this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); }));
|
||||
let destroyId = menuItem.connect('destroy', Lang.bind(this, function() {
|
||||
this.disconnect(openStateChangeId);
|
||||
menuItem.disconnect(destroyId);
|
||||
}));
|
||||
} else if (menuItem instanceof PopupBaseMenuItem)
|
||||
this._connectItemSignals(menuItem);
|
||||
else
|
||||
@ -883,52 +650,6 @@ const PopupMenuBase = new Lang.Class({
|
||||
this.length++;
|
||||
},
|
||||
|
||||
getColumnWidths: function() {
|
||||
let columnWidths = [];
|
||||
let items = this.box.get_children();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (!items[i].visible)
|
||||
continue;
|
||||
|
||||
if (items[i]._delegate instanceof PopupSubMenu)
|
||||
continue;
|
||||
|
||||
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
|
||||
let itemColumnWidths = items[i]._delegate.getColumnWidths();
|
||||
for (let j = 0; j < itemColumnWidths.length; j++) {
|
||||
if (j >= columnWidths.length || itemColumnWidths[j] > columnWidths[j])
|
||||
columnWidths[j] = itemColumnWidths[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return columnWidths;
|
||||
},
|
||||
|
||||
setColumnWidths: function(widths) {
|
||||
let items = this.box.get_children();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i]._delegate instanceof PopupSubMenu)
|
||||
continue;
|
||||
|
||||
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase)
|
||||
items[i]._delegate.setColumnWidths(widths);
|
||||
}
|
||||
},
|
||||
|
||||
// Because of the above column-width funniness, we need to do a
|
||||
// queue-relayout on every item whenever the menu itself changes
|
||||
// size, to force clutter to drop its cached size requests. (The
|
||||
// menuitems will in turn call queue_relayout on their parent, the
|
||||
// menu, but that call will be a no-op since the menu already
|
||||
// has a relayout queued, so we won't get stuck in a loop.
|
||||
_menuQueueRelayout: function() {
|
||||
this.box.get_children().map(function (actor) { actor.queue_relayout(); });
|
||||
},
|
||||
|
||||
addActor: function(actor) {
|
||||
this.box.add(actor);
|
||||
},
|
||||
|
||||
_getMenuItems: function() {
|
||||
return this.box.get_children().map(function (actor) {
|
||||
return actor._delegate;
|
||||
@ -995,12 +716,7 @@ const PopupMenu = new Lang.Class({
|
||||
this.actor._delegate = this;
|
||||
this.actor.style_class = 'popup-menu-boxpointer';
|
||||
|
||||
this._boxWrapper = new Shell.GenericContainer();
|
||||
this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth));
|
||||
this._boxWrapper.connect('get-preferred-height', Lang.bind(this, this._boxGetPreferredHeight));
|
||||
this._boxWrapper.connect('allocate', Lang.bind(this, this._boxAllocate));
|
||||
this._boxPointer.bin.set_child(this._boxWrapper);
|
||||
this._boxWrapper.add_actor(this.box);
|
||||
this._boxPointer.bin.set_child(this.box);
|
||||
this.actor.add_style_class_name('popup-menu');
|
||||
|
||||
global.focus_manager.add_group(this.actor);
|
||||
@ -1016,22 +732,6 @@ const PopupMenu = new Lang.Class({
|
||||
this._openedSubMenu = submenu;
|
||||
},
|
||||
|
||||
_boxGetPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let columnWidths = this.getColumnWidths();
|
||||
this.setColumnWidths(columnWidths);
|
||||
|
||||
// Now they will request the right sizes
|
||||
[alloc.min_size, alloc.natural_size] = this.box.get_preferred_width(forHeight);
|
||||
},
|
||||
|
||||
_boxGetPreferredHeight: function (actor, forWidth, alloc) {
|
||||
[alloc.min_size, alloc.natural_size] = this.box.get_preferred_height(forWidth);
|
||||
},
|
||||
|
||||
_boxAllocate: function (actor, box, flags) {
|
||||
this.box.allocate(box, flags);
|
||||
},
|
||||
|
||||
setArrowOrigin: function(origin) {
|
||||
this._boxPointer.setArrowOrigin(origin);
|
||||
},
|
||||
@ -1105,7 +805,6 @@ const PopupSubMenu = new Lang.Class({
|
||||
this.parent(sourceActor);
|
||||
|
||||
this._arrow = sourceArrow;
|
||||
this._arrow.rotation_center_z_gravity = Clutter.Gravity.CENTER;
|
||||
|
||||
// Since a function of a submenu might be to provide a "More.." expander
|
||||
// with long content, we make it scrollable - the scrollbar will only take
|
||||
@ -1207,20 +906,20 @@ const PopupSubMenu = new Lang.Class({
|
||||
{ _arrow_rotation: 0,
|
||||
height: 0,
|
||||
time: 0.25,
|
||||
onUpdateScope: this,
|
||||
onUpdate: function() {
|
||||
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
|
||||
},
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
this.actor.hide();
|
||||
this.actor.set_height(-1);
|
||||
},
|
||||
onUpdateScope: this,
|
||||
onUpdate: function() {
|
||||
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._arrow.rotation_angle_z = 0;
|
||||
this.actor.hide();
|
||||
}
|
||||
} else {
|
||||
this._arrow.rotation_angle_z = 0;
|
||||
this.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
@ -1273,18 +972,34 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
|
||||
if (wantIcon) {
|
||||
this.icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
this.addActor(this.icon, { align: St.Align.MIDDLE });
|
||||
this.actor.add_child(this.icon);
|
||||
}
|
||||
|
||||
this.label = new St.Label({ text: text });
|
||||
this.addActor(this.label);
|
||||
this.label = new St.Label({ text: text,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_child(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.status = new St.Label({ style_class: 'popup-status-menu-item' });
|
||||
this.addActor(this.status, { align: St.Align.END });
|
||||
let expander = new St.Bin({ style_class: 'popup-menu-item-expander' });
|
||||
this.actor.add(expander, { expand: true });
|
||||
|
||||
this._triangle = new St.Label({ text: '\u25B8' });
|
||||
this.addActor(this._triangle, { align: St.Align.END });
|
||||
this.status = new St.Label({ style_class: 'popup-status-menu-item',
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_child(this.status);
|
||||
|
||||
this._triangle = unicodeArrow(St.Side.RIGHT);
|
||||
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
|
||||
|
||||
this._triangleBin = new St.Widget({ y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this._triangleBin.add_child(this._triangle);
|
||||
if (this._triangleBin.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._triangleBin.set_scale(-1.0, 1.0);
|
||||
|
||||
this.actor.add_child(this._triangleBin);
|
||||
this.actor.add_accessible_state (Atk.StateType.EXPANDABLE);
|
||||
|
||||
this.menu = new PopupSubMenu(this.actor, this._triangle);
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, this._subMenuOpenStateChanged));
|
||||
@ -1306,9 +1021,11 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
if (open) {
|
||||
this.actor.add_style_pseudo_class('open');
|
||||
this._getTopMenu()._setOpenedSubMenu(this.menu);
|
||||
this.actor.add_accessible_state (Atk.StateType.EXPANDED);
|
||||
} else {
|
||||
this.actor.remove_style_pseudo_class('open');
|
||||
this._getTopMenu()._setOpenedSubMenu(null);
|
||||
this.actor.remove_accessible_state (Atk.StateType.EXPANDED);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -120,7 +120,7 @@ const RemoteMenuItemMapper = new Lang.Class({
|
||||
|
||||
this.menuItem = new PopupMenu.PopupBaseMenuItem();
|
||||
this._label = new St.Label();
|
||||
this.menuItem.addActor(this._label);
|
||||
this.menuItem.actor.add_child(this._label);
|
||||
this.menuItem.actor.label_actor = this._label;
|
||||
|
||||
this.menuItem.connect('activate', Lang.bind(this, function() {
|
||||
|
@ -11,52 +11,56 @@ const Search = imports.ui.search;
|
||||
|
||||
const KEY_FILE_GROUP = 'Shell Search Provider';
|
||||
|
||||
const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
|
||||
<method name="GetInitialResultSet">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
<method name="GetSubsearchResultSet">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
<method name="GetResultMetas">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="aa{sv}" direction="out" />
|
||||
</method>
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
const SearchProviderIface = '<node> \
|
||||
<interface name="org.gnome.Shell.SearchProvider"> \
|
||||
<method name="GetInitialResultSet"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="as" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetSubsearchResultSet"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="as" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetResultMetas"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="aa{sv}" direction="out" /> \
|
||||
</method> \
|
||||
<method name="ActivateResult"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
|
||||
<method name="GetInitialResultSet">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
<method name="GetSubsearchResultSet">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="as" direction="out" />
|
||||
</method>
|
||||
<method name="GetResultMetas">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="aa{sv}" direction="out" />
|
||||
</method>
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
const SearchProvider2Iface = '<node> \
|
||||
<interface name="org.gnome.Shell.SearchProvider2"> \
|
||||
<method name="GetInitialResultSet"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="as" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetSubsearchResultSet"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="as" direction="out" /> \
|
||||
</method> \
|
||||
<method name="GetResultMetas"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="aa{sv}" direction="out" /> \
|
||||
</method> \
|
||||
<method name="ActivateResult"> \
|
||||
<arg type="s" direction="in" /> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
<method name="LaunchSearch"> \
|
||||
<arg type="as" direction="in" /> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
|
||||
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
|
||||
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
|
||||
|
||||
function loadRemoteSearchProviders(addProviderCallback) {
|
||||
let objectPaths = {};
|
||||
@ -172,12 +176,18 @@ function loadRemoteSearchProviders(addProviderCallback) {
|
||||
const RemoteSearchProvider = new Lang.Class({
|
||||
Name: 'RemoteSearchProvider',
|
||||
|
||||
_init: function(appInfo, dbusName, dbusPath, proxyType) {
|
||||
if (!proxyType)
|
||||
proxyType = SearchProviderProxy;
|
||||
_init: function(appInfo, dbusName, dbusPath, proxyInfo) {
|
||||
if (!proxyInfo)
|
||||
proxyInfo = SearchProviderProxyInfo;
|
||||
|
||||
this.proxy = new proxyType(Gio.DBus.session,
|
||||
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
|
||||
this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
|
||||
g_name: dbusName,
|
||||
g_object_path: dbusPath,
|
||||
g_interface_info: proxyInfo,
|
||||
g_interface_name: proxyInfo.name,
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
|
||||
|
||||
this.appInfo = appInfo;
|
||||
this.id = appInfo.get_id();
|
||||
@ -186,10 +196,6 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
},
|
||||
|
||||
_onProxyConstructed: function(proxy) {
|
||||
// Do nothing
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
let gicon;
|
||||
if (meta['icon']) {
|
||||
@ -207,40 +213,45 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
icon_size: size });
|
||||
},
|
||||
|
||||
filterResults: function(results, maxNumber) {
|
||||
if (results.length <= maxNumber)
|
||||
return results;
|
||||
|
||||
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
|
||||
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
|
||||
|
||||
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
if (error)
|
||||
return;
|
||||
this.searchSystem.setResults(this, results[0]);
|
||||
if (error) {
|
||||
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
|
||||
} else {
|
||||
this.searchSystem.setResults(this, results[0]);
|
||||
}
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
this.proxy.GetInitialResultSetRemote(terms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||
this.searchSystem.setResults(this, []);
|
||||
}
|
||||
this.proxy.GetInitialResultSetRemote(terms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||
this.searchSystem.setResults(this, []);
|
||||
}
|
||||
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
},
|
||||
|
||||
_getResultMetasFinished: function(results, error, callback) {
|
||||
if (error) {
|
||||
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
@ -265,14 +276,9 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
getResultMetas: function(ids, callback) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
this.proxy.GetResultMetasRemote(ids,
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
|
||||
callback([]);
|
||||
}
|
||||
this.proxy.GetResultMetasRemote(ids,
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
@ -292,7 +298,7 @@ const RemoteSearchProvider2 = new Lang.Class({
|
||||
Extends: RemoteSearchProvider,
|
||||
|
||||
_init: function(appInfo, dbusName, dbusPath) {
|
||||
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
|
||||
this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
|
||||
|
||||
this.canLaunchSearch = true;
|
||||
},
|
||||
|
@ -1,10 +1,12 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -17,12 +19,14 @@ const Background = imports.ui.background;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Hash = imports.misc.hash;
|
||||
const Layout = imports.ui.layout;
|
||||
const OVirt = imports.gdm.oVirt;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const SmartcardManager = imports.misc.smartcardManager;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
@ -49,12 +53,10 @@ const SUMMARY_ICON_SIZE = 48;
|
||||
// or when cancelling the dialog
|
||||
// - BACKGROUND_FADE_TIME is used when the background changes to crossfade to new background
|
||||
// - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking
|
||||
// - INITIAL_FADE_IN_TIME is used for the initial fade in at startup
|
||||
const STANDARD_FADE_TIME = 10;
|
||||
const MANUAL_FADE_TIME = 0.3;
|
||||
const BACKGROUND_FADE_TIME = 1.0;
|
||||
const CURTAIN_SLIDE_TIME = 0.3;
|
||||
const INITIAL_FADE_IN_TIME = 0.25;
|
||||
|
||||
const Clock = new Lang.Class({
|
||||
Name: 'ScreenShieldClock',
|
||||
@ -104,13 +106,14 @@ const NotificationsBox = new Lang.Class({
|
||||
this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box',
|
||||
visible: false });
|
||||
|
||||
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
|
||||
this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
this._notificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
scrollView.add_actor(this._notificationBox);
|
||||
this._scrollView.add_actor(this._notificationBox);
|
||||
|
||||
this.actor.add(this._musicBin);
|
||||
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
|
||||
this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
|
||||
|
||||
this._sources = new Hash.Map();
|
||||
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
|
||||
@ -236,7 +239,7 @@ const NotificationsBox = new Lang.Class({
|
||||
(source.unseenCount > (musicNotification ? 1 : 0));
|
||||
},
|
||||
|
||||
_sourceAdded: function(tray, source, dontUpdateVisibility) {
|
||||
_sourceAdded: function(tray, source, initial) {
|
||||
// Ignore transient sources
|
||||
if (source.isTransient)
|
||||
return;
|
||||
@ -283,8 +286,29 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
this._sources.set(source, obj);
|
||||
|
||||
if (!dontUpdateVisibility)
|
||||
if (!initial) {
|
||||
// block scrollbars while animating, if they're not needed now
|
||||
let boxHeight = this._notificationBox.height;
|
||||
if (this._scrollView.height >= boxHeight)
|
||||
this._scrollView.vscrollbar_policy = Gtk.PolicyType.NEVER;
|
||||
|
||||
let widget = obj.sourceBox;
|
||||
let [, natHeight] = widget.get_preferred_height(-1);
|
||||
widget.height = 0;
|
||||
Tweener.addTween(widget,
|
||||
{ height: natHeight,
|
||||
transition: 'easeOutQuad',
|
||||
time: 0.25,
|
||||
onComplete: function() {
|
||||
this._scrollView.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
|
||||
widget.set_height(-1);
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
this._updateVisibility();
|
||||
Shell.util_wake_up_screen();
|
||||
}
|
||||
},
|
||||
|
||||
_titleChanged: function(source, obj) {
|
||||
@ -306,7 +330,10 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
obj.sourceBox.visible = obj.visible &&
|
||||
(source.unseenCount > (obj.musicNotification ? 1 : 0));
|
||||
|
||||
this._updateVisibility();
|
||||
if (obj.sourceBox.visible)
|
||||
Shell.util_wake_up_screen();
|
||||
},
|
||||
|
||||
_visibleChanged: function(source, obj) {
|
||||
@ -320,6 +347,8 @@ const NotificationsBox = new Lang.Class({
|
||||
source.unseenCount > (obj.musicNotification ? 1 : 0);
|
||||
|
||||
this._updateVisibility();
|
||||
if (obj.sourceBox.visible)
|
||||
Shell.util_wake_up_screen();
|
||||
},
|
||||
|
||||
_detailedChanged: function(source, obj) {
|
||||
@ -489,16 +518,10 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._lockDialogGroup = new St.Widget({ x_expand: true,
|
||||
y_expand: true,
|
||||
opacity: 0,
|
||||
reactive: true,
|
||||
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
|
||||
name: 'lockDialogGroup' });
|
||||
|
||||
Tweener.addTween(this._lockDialogGroup,
|
||||
{ opacity: 255,
|
||||
time: INITIAL_FADE_IN_TIME,
|
||||
transition: 'easeInQuad',
|
||||
});
|
||||
|
||||
this.actor.add_actor(this._lockDialogGroup);
|
||||
this.actor.add_actor(this._lockScreenGroup);
|
||||
|
||||
@ -516,6 +539,20 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||
|
||||
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||
this._smartcardManager.connect('smartcard-inserted',
|
||||
Lang.bind(this, function(token) {
|
||||
if (this._isLocked && token.UsedToLogin)
|
||||
this._liftShield(true, 0);
|
||||
}));
|
||||
|
||||
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
|
||||
this._oVirtCredentialsManager.connect('user-authenticated',
|
||||
Lang.bind(this, function() {
|
||||
if (this._isLocked)
|
||||
this._liftShield(true, 0);
|
||||
}));
|
||||
|
||||
this._inhibitor = null;
|
||||
this._aboutToSuspend = false;
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
@ -542,13 +579,20 @@ const ScreenShield = new Lang.Class({
|
||||
this._becameActiveId = 0;
|
||||
this._lockTimeoutId = 0;
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ inhibitEvents: true,
|
||||
fadeInTime: STANDARD_FADE_TIME,
|
||||
fadeFactor: 1 });
|
||||
this._lightbox.connect('shown', Lang.bind(this, this._onLightboxShown));
|
||||
// The "long" lightbox is used for the longer (20 seconds) fade from session
|
||||
// to idle status, the "short" is used for quickly fading to black when locking
|
||||
// manually
|
||||
this._longLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ inhibitEvents: true,
|
||||
fadeFactor: 1 });
|
||||
this._longLightbox.connect('shown', Lang.bind(this, this._onLongLightboxShown));
|
||||
this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ inhibitEvents: true,
|
||||
fadeFactor: 1 });
|
||||
this._shortLightbox.connect('shown', Lang.bind(this, this._onShortLightboxShown));
|
||||
|
||||
this.idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this.idleMonitor = Meta.IdleMonitor.get_core();
|
||||
this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
|
||||
},
|
||||
|
||||
_createBackground: function(monitorIndex) {
|
||||
@ -561,7 +605,8 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
let bgManager = new Background.BackgroundManager({ container: widget,
|
||||
monitorIndex: monitorIndex,
|
||||
controlPosition: false });
|
||||
controlPosition: false,
|
||||
settingsSchema: SCREENSAVER_SCHEMA });
|
||||
|
||||
this._bgManagers.push(bgManager);
|
||||
|
||||
@ -581,13 +626,28 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
_liftShield: function(onPrimary, velocity) {
|
||||
if (this._isLocked) {
|
||||
this._ensureUnlockDialog(onPrimary, true /* allowCancel */);
|
||||
this._hideLockScreen(true /* animate */, velocity);
|
||||
if (this._ensureUnlockDialog(onPrimary, true /* allowCancel */))
|
||||
this._hideLockScreen(true /* animate */, velocity);
|
||||
} else {
|
||||
this.deactivate(true /* animate */);
|
||||
}
|
||||
},
|
||||
|
||||
_maybeCancelDialog: function() {
|
||||
if (!this._dialog)
|
||||
return;
|
||||
|
||||
this._dialog.cancel();
|
||||
if (this._isGreeter) {
|
||||
// LoginDialog.cancel() will grab the key focus
|
||||
// on its own, so ensure it stays on lock screen
|
||||
// instead
|
||||
this._lockScreenGroup.grab_key_focus();
|
||||
} else {
|
||||
this._dialog = null;
|
||||
}
|
||||
},
|
||||
|
||||
_becomeModal: function() {
|
||||
if (this._isModal)
|
||||
return true;
|
||||
@ -619,9 +679,9 @@ const ScreenShield = new Lang.Class({
|
||||
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
|
||||
return false;
|
||||
|
||||
this._ensureUnlockDialog(true, true);
|
||||
|
||||
if (GLib.unichar_isgraph(unichar))
|
||||
if (this._isLocked &&
|
||||
this._ensureUnlockDialog(true, true) &&
|
||||
GLib.unichar_isgraph(unichar))
|
||||
this._dialog.addCharacter(unichar);
|
||||
|
||||
this._liftShield(true, 0);
|
||||
@ -672,6 +732,8 @@ const ScreenShield = new Lang.Class({
|
||||
this.lock(true);
|
||||
} else {
|
||||
this._inhibitSuspend();
|
||||
|
||||
this._onUserBecameActive();
|
||||
}
|
||||
},
|
||||
|
||||
@ -743,13 +805,7 @@ const ScreenShield = new Lang.Class({
|
||||
onCompleteScope: this,
|
||||
});
|
||||
|
||||
// If we have a unlock dialog, cancel it
|
||||
if (this._dialog) {
|
||||
this._dialog.cancel();
|
||||
if (!this._isGreeter) {
|
||||
this._dialog = null;
|
||||
}
|
||||
}
|
||||
this._maybeCancelDialog();
|
||||
}
|
||||
},
|
||||
|
||||
@ -757,14 +813,9 @@ const ScreenShield = new Lang.Class({
|
||||
if (status != GnomeSession.PresenceStatus.IDLE)
|
||||
return;
|
||||
|
||||
if (this._dialog) {
|
||||
this._dialog.cancel();
|
||||
if (!this._isGreeter) {
|
||||
this._dialog = null;
|
||||
}
|
||||
}
|
||||
this._maybeCancelDialog();
|
||||
|
||||
if (this._lightbox.actor.visible ||
|
||||
if (this._longLightbox.actor.visible ||
|
||||
this._isActive) {
|
||||
// We're either shown and active, or in the process of
|
||||
// showing.
|
||||
@ -782,7 +833,7 @@ const ScreenShield = new Lang.Class({
|
||||
// screenshield. The user is probably very upset at this
|
||||
// point, but any application using global grabs is broken
|
||||
// Just tell him to stop using this app
|
||||
//
|
||||
//
|
||||
// XXX: another option is to kick the user into the gdm login
|
||||
// screen, where we're not affected by grabs
|
||||
Main.notifyError(_("Unable to lock"),
|
||||
@ -790,13 +841,9 @@ const ScreenShield = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this._lightbox.show();
|
||||
|
||||
if (this._activationTime == 0)
|
||||
this._activationTime = GLib.get_monotonic_time();
|
||||
|
||||
this._becameActiveId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onUserBecameActive));
|
||||
|
||||
let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
|
||||
|
||||
if (shouldLock) {
|
||||
@ -804,48 +851,59 @@ const ScreenShield = new Lang.Class({
|
||||
this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000,
|
||||
Lang.bind(this, function() {
|
||||
this._lockTimeoutId = 0;
|
||||
this.lock(true);
|
||||
this.lock(false);
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
|
||||
this._activateFade(this._longLightbox, STANDARD_FADE_TIME);
|
||||
},
|
||||
|
||||
_activateFade: function(lightbox, time) {
|
||||
lightbox.show(time);
|
||||
|
||||
if (this._becameActiveId == 0)
|
||||
this._becameActiveId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onUserBecameActive));
|
||||
},
|
||||
|
||||
_onUserBecameActive: function() {
|
||||
// This function gets called here when the user becomes active
|
||||
// after gnome-session changed the status to IDLE
|
||||
// There are four possibilities here:
|
||||
// - we're called when already locked; isActive and isLocked are true,
|
||||
// after we activated a lightbox
|
||||
// There are two possibilities here:
|
||||
// - we're called when already locked/active; isLocked or isActive is true,
|
||||
// we just go back to the lock screen curtain
|
||||
// - we're called before the lightbox is fully shown; at this point
|
||||
// isActive is false, so we just hide the ligthbox, reset the activationTime
|
||||
// and go back to the unlocked desktop
|
||||
// - we're called after showing the lightbox, but before the lock
|
||||
// delay; this is mostly like the case above, but isActive is true now
|
||||
// so we need to notify gnome-settings-daemon to go back to the normal
|
||||
// policies for blanking
|
||||
// (they're handled by the same code, and we emit one extra ActiveChanged
|
||||
// signal in the case above)
|
||||
// - we're called after showing the lightbox and after lock-delay; the
|
||||
// session is effectivelly locked now, it's time to build and show
|
||||
// the lock screen
|
||||
// (isActive == isLocked == true: normal case
|
||||
// isActive == false, isLocked == true: during the fade for manual locking
|
||||
// isActive == true, isLocked == false: after session idle, before lock-delay)
|
||||
// - we're called because the session is IDLE but before the lightbox
|
||||
// is fully shown; at this point isActive is false, so we just hide
|
||||
// the lightbox, reset the activationTime and go back to the unlocked
|
||||
// desktop
|
||||
// using deactivate() is a little of overkill, but it ensures we
|
||||
// don't forget of some bit like modal, DBus properties or idle watches
|
||||
//
|
||||
// Note: if the (long) lightbox is shown then we're necessarily
|
||||
// active, because we call activate() without animation.
|
||||
|
||||
this.idleMonitor.remove_watch(this._becameActiveId);
|
||||
this._becameActiveId = 0;
|
||||
|
||||
let lightboxWasShown = this._lightbox.shown;
|
||||
this._lightbox.hide();
|
||||
|
||||
// Shortcircuit in case the mouse was moved before the fade completed
|
||||
if (!lightboxWasShown) {
|
||||
if (this._isActive || this._isLocked) {
|
||||
this._longLightbox.hide();
|
||||
this._shortLightbox.hide();
|
||||
} else {
|
||||
this.deactivate(false);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_onLightboxShown: function() {
|
||||
_onLongLightboxShown: function() {
|
||||
this.activate(false);
|
||||
},
|
||||
|
||||
_onShortLightboxShown: function() {
|
||||
this._completeLockScreenShown();
|
||||
},
|
||||
|
||||
showDialog: function() {
|
||||
// Ensure that the stage window is mapped, before taking a grab
|
||||
// otherwise X errors out
|
||||
@ -862,8 +920,8 @@ const ScreenShield = new Lang.Class({
|
||||
this.actor.show();
|
||||
this._isGreeter = Main.sessionMode.isGreeter;
|
||||
this._isLocked = true;
|
||||
this._ensureUnlockDialog(true, true);
|
||||
this._hideLockScreen(false, 0);
|
||||
if (this._ensureUnlockDialog(true, true))
|
||||
this._hideLockScreen(false, 0);
|
||||
},
|
||||
|
||||
_hideLockScreenComplete: function() {
|
||||
@ -880,6 +938,8 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._lockScreenState = MessageTray.State.HIDING;
|
||||
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
|
||||
if (animate) {
|
||||
// Tween the lock screen out of screen
|
||||
// if velocity is not specified (i.e. we come here from pressing ESC),
|
||||
@ -892,7 +952,6 @@ const ScreenShield = new Lang.Class({
|
||||
velocity = Math.max(min_velocity, velocity);
|
||||
let time = (delta / velocity) / 1000;
|
||||
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: -h,
|
||||
time: time,
|
||||
@ -903,7 +962,7 @@ const ScreenShield = new Lang.Class({
|
||||
this._hideLockScreenComplete();
|
||||
}
|
||||
|
||||
global.stage.show_cursor();
|
||||
this._cursorTracker.set_pointer_visible(true);
|
||||
},
|
||||
|
||||
_ensureUnlockDialog: function(onPrimary, allowCancel) {
|
||||
@ -912,7 +971,7 @@ const ScreenShield = new Lang.Class({
|
||||
if (!constructor) {
|
||||
// This session mode has no locking capabilities
|
||||
this.deactivate(true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this._dialog = new constructor(this._lockDialogGroup);
|
||||
@ -924,19 +983,22 @@ const ScreenShield = new Lang.Class({
|
||||
// by the time we reach this...
|
||||
log('Could not open login dialog: failed to acquire grab');
|
||||
this.deactivate(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||
}
|
||||
|
||||
this._dialog.allowCancel = allowCancel;
|
||||
return true;
|
||||
},
|
||||
|
||||
_onUnlockFailed: function() {
|
||||
this._resetLockScreen(true, false);
|
||||
this._resetLockScreen({ animateLockScreen: true,
|
||||
fadeToBlack: false });
|
||||
},
|
||||
|
||||
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||
_resetLockScreen: function(params) {
|
||||
// Don't reset the lock screen unless it is completely hidden
|
||||
// This prevents the shield going down if the lock-delay timeout
|
||||
// fires while the user is dragging (which has the potential
|
||||
@ -951,7 +1013,9 @@ const ScreenShield = new Lang.Class({
|
||||
this._lockScreenGroup.show();
|
||||
this._lockScreenState = MessageTray.State.SHOWING;
|
||||
|
||||
if (animateLockScreen) {
|
||||
let fadeToBlack = params.fadeToBlack;
|
||||
|
||||
if (params.animateLockScreen) {
|
||||
this._lockScreenGroup.y = -global.screen_height;
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
@ -959,24 +1023,15 @@ const ScreenShield = new Lang.Class({
|
||||
time: MANUAL_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._lockScreenShown();
|
||||
this._lockScreenShown({ fadeToBlack: fadeToBlack,
|
||||
animateFade: true });
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
} else {
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this._lockScreenShown();
|
||||
}
|
||||
|
||||
if (animateLockDialog) {
|
||||
this._lockDialogGroup.opacity = 0;
|
||||
Tweener.removeTweens(this._lockDialogGroup);
|
||||
Tweener.addTween(this._lockDialogGroup,
|
||||
{ opacity: 255,
|
||||
time: MANUAL_FADE_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
} else {
|
||||
this._lockDialogGroup.opacity = 255;
|
||||
this._lockScreenShown({ fadeToBlack: fadeToBlack,
|
||||
animateFade: false });
|
||||
}
|
||||
|
||||
this._lockScreenGroup.grab_key_focus();
|
||||
@ -1032,7 +1087,7 @@ const ScreenShield = new Lang.Class({
|
||||
this._pauseArrowAnimation();
|
||||
},
|
||||
|
||||
_lockScreenShown: function() {
|
||||
_lockScreenShown: function(params) {
|
||||
if (this._dialog && !this._isGreeter) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
@ -1040,20 +1095,35 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._checkArrowAnimation();
|
||||
|
||||
let motionId = global.stage.connect('captured-event', function(stage, event) {
|
||||
let motionId = global.stage.connect('captured-event', Lang.bind(this, function(stage, event) {
|
||||
if (event.type() == Clutter.EventType.MOTION) {
|
||||
global.stage.show_cursor();
|
||||
this._cursorTracker.set_pointer_visible(true);
|
||||
global.stage.disconnect(motionId);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
global.stage.hide_cursor();
|
||||
}));
|
||||
this._cursorTracker.set_pointer_visible(false);
|
||||
|
||||
this._lockScreenState = MessageTray.State.SHOWN;
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this._lockScreenScrollCounter = 0;
|
||||
|
||||
if (params.fadeToBlack && params.animateFade) {
|
||||
// Take a beat
|
||||
|
||||
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
|
||||
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
|
||||
}));
|
||||
} else {
|
||||
if (params.fadeToBlack)
|
||||
this._activateFade(this._shortLightbox, 0);
|
||||
|
||||
this._completeLockScreenShown();
|
||||
}
|
||||
},
|
||||
|
||||
_completeLockScreenShown: function() {
|
||||
let prevIsActive = this._isActive;
|
||||
this._isActive = true;
|
||||
|
||||
@ -1121,12 +1191,15 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
deactivate: function(animate) {
|
||||
this._dialog.finish(Lang.bind(this, function() {
|
||||
this._finishDeactivate(animate);
|
||||
}));
|
||||
if (this._dialog)
|
||||
this._dialog.finish(Lang.bind(this, function() {
|
||||
this._continueDeactivate(animate);
|
||||
}));
|
||||
else
|
||||
this._continueDeactivate(animate);
|
||||
},
|
||||
|
||||
_finishDeactivate: function(animate) {
|
||||
_continueDeactivate: function(animate) {
|
||||
this._hideLockScreen(animate, 0);
|
||||
|
||||
if (this._hasLockScreen)
|
||||
@ -1137,6 +1210,20 @@ const ScreenShield = new Lang.Class({
|
||||
if (Main.sessionMode.currentMode == 'unlock-dialog')
|
||||
Main.sessionMode.popMode('unlock-dialog');
|
||||
|
||||
if (this._isGreeter) {
|
||||
// We don't want to "deactivate" any more than
|
||||
// this. In particular, we don't want to drop
|
||||
// the modal, hide ourselves or destroy the dialog
|
||||
// But we do want to set isActive to false, so that
|
||||
// gnome-session will reset the idle counter, and
|
||||
// gnome-settings-daemon will stop blanking the screen
|
||||
|
||||
this._activationTime = 0;
|
||||
this._isActive = false;
|
||||
this.emit('active-changed');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._dialog && !this._isGreeter)
|
||||
this._dialog.popModal();
|
||||
|
||||
@ -1156,12 +1243,13 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_completeDeactivate: function() {
|
||||
if (this._dialog && !this._isGreeter) {
|
||||
if (this._dialog) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
|
||||
this._lightbox.hide();
|
||||
this._longLightbox.hide();
|
||||
this._shortLightbox.hide();
|
||||
this.actor.hide();
|
||||
|
||||
if (this._becameActiveId != 0) {
|
||||
@ -1195,7 +1283,8 @@ const ScreenShield = new Lang.Class({
|
||||
Main.sessionMode.pushMode('unlock-dialog');
|
||||
}
|
||||
|
||||
this._resetLockScreen(animate, animate);
|
||||
this._resetLockScreen({ animateLockScreen: animate,
|
||||
fadeToBlack: true });
|
||||
global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true));
|
||||
|
||||
// We used to set isActive and emit active-changed here,
|
||||
@ -1224,7 +1313,14 @@ const ScreenShield = new Lang.Class({
|
||||
St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, '');
|
||||
St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, '');
|
||||
|
||||
this._isLocked = true;
|
||||
let userManager = AccountsService.UserManager.get_default();
|
||||
let user = userManager.get_user(GLib.get_user_name());
|
||||
|
||||
if (this._isGreeter)
|
||||
this._isLocked = true;
|
||||
else
|
||||
this._isLocked = user.password_mode != AccountsService.UserPasswordMode.NONE;
|
||||
|
||||
this.activate(animate);
|
||||
|
||||
this.emit('locked-changed');
|
||||
|
@ -4,31 +4,34 @@ const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Hash = imports.misc.hash;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
|
||||
<method name="Screencast">
|
||||
<arg type="s" direction="in" name="file_template"/>
|
||||
<arg type="a{sv}" direction="in" name="options"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="ScreencastArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="s" direction="in" name="file_template"/>
|
||||
<arg type="a{sv}" direction="in" name="options"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="StopScreencast">
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
</interface>;
|
||||
const ScreencastIface = '<node> \
|
||||
<interface name="org.gnome.Shell.Screencast"> \
|
||||
<method name="Screencast"> \
|
||||
<arg type="s" direction="in" name="file_template"/> \
|
||||
<arg type="a{sv}" direction="in" name="options"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
<arg type="s" direction="out" name="filename_used"/> \
|
||||
</method> \
|
||||
<method name="ScreencastArea"> \
|
||||
<arg type="i" direction="in" name="x"/> \
|
||||
<arg type="i" direction="in" name="y"/> \
|
||||
<arg type="i" direction="in" name="width"/> \
|
||||
<arg type="i" direction="in" name="height"/> \
|
||||
<arg type="s" direction="in" name="file_template"/> \
|
||||
<arg type="a{sv}" direction="in" name="options"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
<arg type="s" direction="out" name="filename_used"/> \
|
||||
</method> \
|
||||
<method name="StopScreencast"> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ScreencastService = new Lang.Class({
|
||||
Name: 'ScreencastService',
|
||||
@ -41,28 +44,34 @@ const ScreencastService = new Lang.Class({
|
||||
|
||||
this._recorders = new Hash.Map();
|
||||
|
||||
Main.sessionMode.connect('updated',
|
||||
Lang.bind(this, this._sessionModeChanged));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
},
|
||||
|
||||
get isRecording() {
|
||||
return this._recorders.size() > 0;
|
||||
},
|
||||
|
||||
_ensureRecorderForSender: function(sender) {
|
||||
let recorder = this._recorders.get(sender);
|
||||
if (!recorder) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
recorder = new Shell.Recorder({ stage: global.stage,
|
||||
screen: global.screen });
|
||||
recorder._watchNameId =
|
||||
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
|
||||
Lang.bind(this, this._onNameVanished));
|
||||
this._recorders.set(sender, recorder);
|
||||
this.emit('updated');
|
||||
}
|
||||
return recorder;
|
||||
},
|
||||
|
||||
_sessionModeChanged: function() {
|
||||
_sessionUpdated: function() {
|
||||
if (Main.sessionMode.allowScreencast)
|
||||
return;
|
||||
|
||||
for (let sender in this._recorders.keys())
|
||||
this._recorders.delete(sender);
|
||||
this.emit('updated');
|
||||
},
|
||||
|
||||
_onNameVanished: function(connection, name) {
|
||||
@ -77,6 +86,7 @@ const ScreencastService = new Lang.Class({
|
||||
Gio.bus_unwatch_name(recorder._watchNameId);
|
||||
recorder.close();
|
||||
this._recorders.delete(sender);
|
||||
this.emit('updated');
|
||||
|
||||
return true;
|
||||
},
|
||||
@ -138,3 +148,4 @@ const ScreencastService = new Lang.Class({
|
||||
invocation.return_value(GLib.Variant.new('(b)', [success]));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ScreencastService.prototype);
|
||||
|
@ -6,6 +6,7 @@ const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
@ -14,45 +15,47 @@ const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
</interface>;
|
||||
const ScreenshotIface = '<node> \
|
||||
<interface name="org.gnome.Shell.Screenshot"> \
|
||||
<method name="ScreenshotArea"> \
|
||||
<arg type="i" direction="in" name="x"/> \
|
||||
<arg type="i" direction="in" name="y"/> \
|
||||
<arg type="i" direction="in" name="width"/> \
|
||||
<arg type="i" direction="in" name="height"/> \
|
||||
<arg type="b" direction="in" name="flash"/> \
|
||||
<arg type="s" direction="in" name="filename"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
<arg type="s" direction="out" name="filename_used"/> \
|
||||
</method> \
|
||||
<method name="ScreenshotWindow"> \
|
||||
<arg type="b" direction="in" name="include_frame"/> \
|
||||
<arg type="b" direction="in" name="include_cursor"/> \
|
||||
<arg type="b" direction="in" name="flash"/> \
|
||||
<arg type="s" direction="in" name="filename"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
<arg type="s" direction="out" name="filename_used"/> \
|
||||
</method> \
|
||||
<method name="Screenshot"> \
|
||||
<arg type="b" direction="in" name="include_cursor"/> \
|
||||
<arg type="b" direction="in" name="flash"/> \
|
||||
<arg type="s" direction="in" name="filename"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
<arg type="s" direction="out" name="filename_used"/> \
|
||||
</method> \
|
||||
<method name="SelectArea"> \
|
||||
<arg type="i" direction="out" name="x"/> \
|
||||
<arg type="i" direction="out" name="y"/> \
|
||||
<arg type="i" direction="out" name="width"/> \
|
||||
<arg type="i" direction="out" name="height"/> \
|
||||
</method> \
|
||||
<method name="FlashArea"> \
|
||||
<arg type="i" direction="in" name="x"/> \
|
||||
<arg type="i" direction="in" name="y"/> \
|
||||
<arg type="i" direction="in" name="width"/> \
|
||||
<arg type="i" direction="in" name="height"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ScreenshotService = new Lang.Class({
|
||||
Name: 'ScreenshotService',
|
||||
@ -167,7 +170,7 @@ const SelectArea = new Lang.Class({
|
||||
if (!Main.pushModal(this._group) || this._group.visible)
|
||||
return;
|
||||
|
||||
global.set_cursor(Shell.Cursor.CROSSHAIR);
|
||||
global.screen.set_cursor(Meta.Cursor.CROSSHAIR);
|
||||
this._group.visible = true;
|
||||
},
|
||||
|
||||
@ -238,7 +241,7 @@ const SelectArea = new Lang.Class({
|
||||
function() {
|
||||
Main.popModal(this._group);
|
||||
this._group.destroy();
|
||||
global.unset_cursor();
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
|
||||
this.emit('finished', geometry);
|
||||
})
|
||||
|
@ -69,16 +69,18 @@ function waitLeisure() {
|
||||
};
|
||||
}
|
||||
|
||||
const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper">
|
||||
<method name="CreateWindow">
|
||||
<arg type="i" direction="in" />
|
||||
<arg type="i" direction="in" />
|
||||
<arg type="b" direction="in" />
|
||||
<arg type="b" direction="in" />
|
||||
</method>
|
||||
<method name="WaitWindows" />
|
||||
<method name="DestroyWindows" />
|
||||
</interface>;
|
||||
const PerfHelperIface = '<node> \
|
||||
<interface name="org.gnome.Shell.PerfHelper"> \
|
||||
<method name="CreateWindow"> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="b" direction="in" /> \
|
||||
<arg type="b" direction="in" /> \
|
||||
</method> \
|
||||
<method name="WaitWindows" /> \
|
||||
<method name="DestroyWindows" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
|
||||
function PerfHelper() {
|
||||
|
579
js/ui/search.js
@ -1,10 +1,26 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Separator = imports.ui.separator;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
|
||||
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const SearchSystem = new Lang.Class({
|
||||
Name: 'SearchSystem',
|
||||
|
||||
@ -82,24 +98,563 @@ const SearchSystem = new Lang.Class({
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let [provider, previousResults] = previousResultsArr[i];
|
||||
try {
|
||||
results.push([provider, []]);
|
||||
provider.getSubsearchResultSet(previousResults, terms);
|
||||
} catch (error) {
|
||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
||||
}
|
||||
results.push([provider, []]);
|
||||
provider.getSubsearchResultSet(previousResults, terms);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
try {
|
||||
results.push([provider, []]);
|
||||
provider.getInitialResultSet(terms);
|
||||
} catch (error) {
|
||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
||||
}
|
||||
results.push([provider, []]);
|
||||
provider.getInitialResultSet(terms);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
||||
|
||||
const MaxWidthBin = new Lang.Class({
|
||||
Name: 'MaxWidthBin',
|
||||
Extends: St.Bin,
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
let themeNode = this.get_theme_node();
|
||||
let maxWidth = themeNode.get_max_width();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let adjustedBox = box;
|
||||
|
||||
if (availWidth > maxWidth) {
|
||||
let excessWidth = availWidth - maxWidth;
|
||||
adjustedBox.x1 += Math.floor(excessWidth / 2);
|
||||
adjustedBox.x2 -= Math.floor(excessWidth / 2);
|
||||
}
|
||||
|
||||
this.parent(adjustedBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.terms = terms;
|
||||
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('clicked', Lang.bind(this, this.activate));
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResult = new Lang.Class({
|
||||
Name: 'ListSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
ICON_SIZE: 64,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'list-search-result';
|
||||
this.actor.x_fill = true;
|
||||
|
||||
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
// An icon for, or thumbnail of, content
|
||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||
if (icon) {
|
||||
content.add(icon);
|
||||
}
|
||||
|
||||
let details = new St.BoxLayout({ vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let title = new St.Label({ style_class: 'list-search-result-title',
|
||||
text: this.metaInfo['name'] })
|
||||
details.add(title, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.label_actor = title;
|
||||
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'list-search-result-description' });
|
||||
description.clutter_text.set_markup(this.metaInfo['description']);
|
||||
details.add(description, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const GridSearchResult = new Lang.Class({
|
||||
Name: 'GridSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'grid-search-result';
|
||||
|
||||
let content = provider.createResultObject(metaInfo, terms);
|
||||
let dragSource = null;
|
||||
|
||||
if (content == null) {
|
||||
let actor = new St.Bin();
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
actor.set_child(icon.actor);
|
||||
actor.label_actor = icon.label;
|
||||
dragSource = icon.icon;
|
||||
content = { actor: actor, icon: icon };
|
||||
} else {
|
||||
if (content._delegate && content._delegate.getDragActorSource)
|
||||
dragSource = content._delegate.getDragActorSource();
|
||||
}
|
||||
|
||||
this.actor.set_child(content.actor);
|
||||
this.actor.label_actor = content.actor.label_actor;
|
||||
this.icon = content.icon;
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-cancelled',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.cancelledItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
|
||||
if (!dragSource)
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
dragSource = content;
|
||||
this._dragActorSource = dragSource;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this._dragActorSource;
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch: function(params) {
|
||||
if (this.provider.dragActivateResult)
|
||||
this.provider.dragActivateResult(this.metaInfo.id, params);
|
||||
else
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
}
|
||||
});
|
||||
|
||||
const SearchResultsBase = new Lang.Class({
|
||||
Name: 'SearchResultsBase',
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
this._terms = [];
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
|
||||
this._resultDisplayBin = new St.Bin({ x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor.add(this._resultDisplayBin, { expand: true });
|
||||
|
||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||
this.actor.add(separator.actor);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
this._terms = [];
|
||||
},
|
||||
|
||||
_clearResultDisplay: function() {
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this._clearResultDisplay();
|
||||
this.actor.hide();
|
||||
},
|
||||
|
||||
_keyFocusIn: function(actor) {
|
||||
this.emit('key-focus-in', actor);
|
||||
},
|
||||
|
||||
_setMoreIconVisible: function(visible) {
|
||||
},
|
||||
|
||||
updateSearch: function(providerResults, terms, callback) {
|
||||
this._terms = terms;
|
||||
|
||||
if (providerResults.length == 0) {
|
||||
this._clearResultDisplay();
|
||||
this.actor.hide();
|
||||
callback();
|
||||
} else {
|
||||
let maxResults = this._getMaxDisplayedResults();
|
||||
let results = this.provider.filterResults(providerResults, maxResults);
|
||||
let hasMoreResults = results.length < providerResults.length;
|
||||
|
||||
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this.clear();
|
||||
|
||||
// To avoid CSS transitions causing flickering when
|
||||
// the first search result stays the same, we hide the
|
||||
// content while filling in the results.
|
||||
this.actor.hide();
|
||||
this._clearResultDisplay();
|
||||
this._renderResults(metas);
|
||||
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
|
||||
this.actor.show();
|
||||
callback();
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResults = new Lang.Class({
|
||||
Name: 'ListSearchResults',
|
||||
Extends: SearchResultsBase,
|
||||
|
||||
_init: function(provider) {
|
||||
this.parent(provider);
|
||||
|
||||
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||
this.providerIcon = new ProviderIcon(provider);
|
||||
this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
provider.launchSearch(this._terms);
|
||||
Main.overview.toggle();
|
||||
}));
|
||||
|
||||
this._container.add(this.providerIcon, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||
vertical: true });
|
||||
this._container.add(this._content, { expand: true });
|
||||
|
||||
this._resultDisplayBin.set_child(this._container);
|
||||
},
|
||||
|
||||
_setMoreIconVisible: function(visible) {
|
||||
this.providerIcon.moreIcon.visible = true;
|
||||
},
|
||||
|
||||
_getMaxDisplayedResults: function() {
|
||||
return MAX_LIST_SEARCH_RESULTS_ROWS;
|
||||
},
|
||||
|
||||
_renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
_clearResultDisplay: function () {
|
||||
this._content.destroy_all_children();
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this._content.get_n_children() > 0)
|
||||
return this._content.get_child_at_index(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ListSearchResults.prototype);
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
Extends: SearchResultsBase,
|
||||
|
||||
_init: function(provider) {
|
||||
this.parent(provider);
|
||||
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||
this._bin.set_child(this._grid.actor);
|
||||
|
||||
this._resultDisplayBin.set_child(this._bin);
|
||||
},
|
||||
|
||||
_getMaxDisplayedResults: function() {
|
||||
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
|
||||
},
|
||||
|
||||
_renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._grid.addItem(display);
|
||||
}
|
||||
},
|
||||
|
||||
_clearResultDisplay: function () {
|
||||
this._grid.removeAll();
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this._grid.visibleItemsCount() > 0)
|
||||
return this._grid.getItemAtIndex(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(GridSearchResults.prototype);
|
||||
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
vertical: true });
|
||||
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
child: this._content });
|
||||
|
||||
let scrollChild = new St.BoxLayout();
|
||||
scrollChild.add(this._contentBin, { expand: true });
|
||||
|
||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
overlay_scrollbars: true,
|
||||
style_class: 'search-display vfade' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this._scrollView.add_actor(scrollChild);
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this._scrollView.add_action(action);
|
||||
|
||||
this.actor.add(this._scrollView, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
||||
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this._content.add(this._statusBin, { expand: true });
|
||||
this._statusBin.add_actor(this._statusText);
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerDisplays = {};
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderDisplay(this._providers[i]);
|
||||
}
|
||||
|
||||
this._highlightDefault = false;
|
||||
this._defaultResult = null;
|
||||
},
|
||||
|
||||
_onPan: function(action) {
|
||||
let [dist, dx, dy] = action.get_motion_delta(0);
|
||||
let adjustment = this._scrollView.vscroll.adjustment;
|
||||
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
||||
return false;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(provider, actor) {
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
|
||||
},
|
||||
|
||||
createProviderDisplay: function(provider) {
|
||||
let providerDisplay = null;
|
||||
|
||||
if (provider.appInfo) {
|
||||
providerDisplay = new ListSearchResults(provider);
|
||||
} else {
|
||||
providerDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
|
||||
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._providerDisplays[provider.id] = providerDisplay;
|
||||
this._content.add(providerDisplay.actor);
|
||||
},
|
||||
|
||||
destroyProviderDisplay: function(provider) {
|
||||
this._providerDisplays[provider.id].destroy();
|
||||
delete this._providerDisplays[provider.id];
|
||||
},
|
||||
|
||||
_clearDisplay: function() {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let providerDisplay = this._providerDisplays[provider.id];
|
||||
providerDisplay.clear();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._searchSystem.reset();
|
||||
this._statusBin.hide();
|
||||
this._clearDisplay();
|
||||
this._defaultResult = null;
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
this.reset();
|
||||
this._statusText.set_text(_("Searching…"));
|
||||
this._statusBin.show();
|
||||
},
|
||||
|
||||
_maybeSetInitialSelection: function() {
|
||||
let newDefaultResult = null;
|
||||
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let display = this._providerDisplays[provider.id];
|
||||
|
||||
if (!display.actor.visible)
|
||||
continue;
|
||||
|
||||
let firstResult = display.getFirstResult();
|
||||
if (firstResult) {
|
||||
newDefaultResult = firstResult;
|
||||
break; // select this one!
|
||||
}
|
||||
}
|
||||
|
||||
if (newDefaultResult != this._defaultResult) {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(false);
|
||||
if (newDefaultResult)
|
||||
newDefaultResult.setSelected(this._highlightDefault);
|
||||
|
||||
this._defaultResult = newDefaultResult;
|
||||
}
|
||||
},
|
||||
|
||||
_updateStatusText: function () {
|
||||
let haveResults = false;
|
||||
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let display = this._providerDisplays[provider.id];
|
||||
if (display.getFirstResult()) {
|
||||
haveResults = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveResults) {
|
||||
this._statusText.set_text(_("No results."));
|
||||
this._statusBin.show();
|
||||
} else {
|
||||
this._statusBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateResults: function(searchSystem, results) {
|
||||
let terms = searchSystem.getTerms();
|
||||
let [provider, providerResults] = results;
|
||||
let display = this._providerDisplays[provider.id];
|
||||
|
||||
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
|
||||
this._maybeSetInitialSelection();
|
||||
this._updateStatusText();
|
||||
}));
|
||||
},
|
||||
|
||||
activateDefault: function() {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.activate();
|
||||
},
|
||||
|
||||
highlightDefault: function(highlight) {
|
||||
this._highlightDefault = highlight;
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(highlight);
|
||||
},
|
||||
|
||||
navigateFocus: function(direction) {
|
||||
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
|
||||
direction == (rtl ? Gtk.DirectionType.RIGHT
|
||||
: Gtk.DirectionType.LEFT) ||
|
||||
direction == Gtk.DirectionType.UP) {
|
||||
this.actor.navigate_focus(null, direction, false);
|
||||
return;
|
||||
}
|
||||
|
||||
let from = this._defaultResult ? this._defaultResult.actor : null;
|
||||
this.actor.navigate_focus(from, direction, false);
|
||||
}
|
||||
});
|
||||
|
||||
const ProviderIcon = new Lang.Class({
|
||||
Name: 'ProviderIcon',
|
||||
Extends: St.Button,
|
||||
|
||||
PROVIDER_ICON_SIZE: 48,
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.parent({ style_class: 'search-provider-icon',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
accessible_name: provider.appInfo.get_name(),
|
||||
track_hover: true });
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this.set_child(this._content);
|
||||
|
||||
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
|
||||
visible: false,
|
||||
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
|
||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
||||
gicon: provider.appInfo.get_icon() });
|
||||
this._content.add_actor(icon);
|
||||
this._content.add_actor(this.moreIcon);
|
||||
}
|
||||
});
|
||||
|
@ -1,564 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Separator = imports.ui.separator;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const MaxWidthBin = new Lang.Class({
|
||||
Name: 'MaxWidthBin',
|
||||
Extends: St.Bin,
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
let themeNode = this.get_theme_node();
|
||||
let maxWidth = themeNode.get_max_width();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let adjustedBox = box;
|
||||
|
||||
if (availWidth > maxWidth) {
|
||||
let excessWidth = availWidth - maxWidth;
|
||||
adjustedBox.x1 += Math.floor(excessWidth / 2);
|
||||
adjustedBox.x2 -= Math.floor(excessWidth / 2);
|
||||
}
|
||||
|
||||
this.parent(adjustedBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.terms = terms;
|
||||
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('clicked', Lang.bind(this, this.activate));
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResult = new Lang.Class({
|
||||
Name: 'ListSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
ICON_SIZE: 64,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'list-search-result';
|
||||
this.actor.x_fill = true;
|
||||
|
||||
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
// An icon for, or thumbnail of, content
|
||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||
if (icon) {
|
||||
content.add(icon);
|
||||
}
|
||||
|
||||
let details = new St.BoxLayout({ vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let title = new St.Label({ style_class: 'list-search-result-title',
|
||||
text: this.metaInfo['name'] })
|
||||
details.add(title, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.label_actor = title;
|
||||
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'list-search-result-description' });
|
||||
description.clutter_text.set_markup(this.metaInfo['description']);
|
||||
details.add(description, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const GridSearchResult = new Lang.Class({
|
||||
Name: 'GridSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'grid-search-result';
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
let dragSource = null;
|
||||
|
||||
if (content == null) {
|
||||
content = new St.Bin();
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
content.set_child(icon.actor);
|
||||
content.label_actor = icon.label;
|
||||
dragSource = icon.icon;
|
||||
} else {
|
||||
if (content._delegate && content._delegate.getDragActorSource)
|
||||
dragSource = content._delegate.getDragActorSource();
|
||||
}
|
||||
|
||||
this.actor.set_child(content);
|
||||
this.actor.label_actor = content.label_actor;
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-cancelled',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.cancelledItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
|
||||
if (!dragSource)
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
dragSource = content;
|
||||
this._dragActorSource = dragSource;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this._dragActorSource;
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch: function(params) {
|
||||
if (this.provider.dragActivateResult)
|
||||
this.provider.dragActivateResult(this.metaInfo.id, params);
|
||||
else
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
}
|
||||
});
|
||||
|
||||
const SearchResultsBase = new Lang.Class({
|
||||
Name: 'SearchResultsBase',
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
this._terms = [];
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
|
||||
this._resultDisplayBin = new St.Bin({ x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor.add(this._resultDisplayBin, { expand: true });
|
||||
|
||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||
this.actor.add(separator.actor);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
this._terms = [];
|
||||
},
|
||||
|
||||
_clearResultDisplay: function() {
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this._clearResultDisplay();
|
||||
this.actor.hide();
|
||||
},
|
||||
|
||||
_keyFocusIn: function(icon) {
|
||||
this.emit('key-focus-in', icon);
|
||||
},
|
||||
|
||||
_setMoreIconVisible: function(visible) {
|
||||
},
|
||||
|
||||
updateSearch: function(providerResults, terms, callback) {
|
||||
this._terms = terms;
|
||||
|
||||
if (providerResults.length == 0) {
|
||||
this._clearResultDisplay();
|
||||
this.actor.hide();
|
||||
callback();
|
||||
} else {
|
||||
let maxResults = this._getMaxDisplayedResults();
|
||||
let results = providerResults.slice(0, maxResults);
|
||||
let hasMoreResults = results.length < providerResults.length;
|
||||
|
||||
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this.clear();
|
||||
|
||||
// To avoid CSS transitions causing flickering when
|
||||
// the first search result stays the same, we hide the
|
||||
// content while filling in the results.
|
||||
this.actor.hide();
|
||||
this._clearResultDisplay();
|
||||
this._renderResults(metas);
|
||||
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
|
||||
this.actor.show();
|
||||
callback();
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResults = new Lang.Class({
|
||||
Name: 'ListSearchResults',
|
||||
Extends: SearchResultsBase,
|
||||
|
||||
_init: function(provider) {
|
||||
this.parent(provider);
|
||||
|
||||
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||
this.providerIcon = new ProviderIcon(provider);
|
||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
provider.launchSearch(this._terms);
|
||||
Main.overview.toggle();
|
||||
}));
|
||||
|
||||
this._container.add(this.providerIcon, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||
vertical: true });
|
||||
this._container.add(this._content, { expand: true });
|
||||
|
||||
this._resultDisplayBin.set_child(this._container);
|
||||
},
|
||||
|
||||
_setMoreIconVisible: function(visible) {
|
||||
this.providerIcon.moreIcon.visible = true;
|
||||
},
|
||||
|
||||
_getMaxDisplayedResults: function() {
|
||||
return MAX_LIST_SEARCH_RESULTS_ROWS;
|
||||
},
|
||||
|
||||
_renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
_clearResultDisplay: function () {
|
||||
this._content.destroy_all_children();
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this._content.get_n_children() > 0)
|
||||
return this._content.get_child_at_index(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ListSearchResults.prototype);
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
Extends: SearchResultsBase,
|
||||
|
||||
_init: function(provider) {
|
||||
this.parent(provider);
|
||||
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||
this._bin.set_child(this._grid.actor);
|
||||
|
||||
this._resultDisplayBin.set_child(this._bin);
|
||||
},
|
||||
|
||||
_getMaxDisplayedResults: function() {
|
||||
return this._grid.childrenInRow(this._bin.width) * this._grid.getRowLimit();
|
||||
},
|
||||
|
||||
_renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
_clearResultDisplay: function () {
|
||||
this._grid.removeAll();
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this._grid.visibleItemsCount() > 0)
|
||||
return this._grid.getItemAtIndex(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(GridSearchResults.prototype);
|
||||
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
vertical: true });
|
||||
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
child: this._content });
|
||||
|
||||
let scrollChild = new St.BoxLayout();
|
||||
scrollChild.add(this._contentBin, { expand: true });
|
||||
|
||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
overlay_scrollbars: true,
|
||||
style_class: 'search-display vfade' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this._scrollView.add_actor(scrollChild);
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this._scrollView.add_action(action);
|
||||
|
||||
this.actor.add(this._scrollView, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
||||
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this._content.add(this._statusBin, { expand: true });
|
||||
this._statusBin.add_actor(this._statusText);
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerDisplays = {};
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderDisplay(this._providers[i]);
|
||||
}
|
||||
|
||||
this._highlightDefault = false;
|
||||
this._defaultResult = null;
|
||||
},
|
||||
|
||||
_onPan: function(action) {
|
||||
let [dist, dx, dy] = action.get_motion_delta(0);
|
||||
let adjustment = this._scrollView.vscroll.adjustment;
|
||||
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
||||
return false;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(provider, icon) {
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
|
||||
},
|
||||
|
||||
createProviderDisplay: function(provider) {
|
||||
let providerDisplay = null;
|
||||
|
||||
if (provider.appInfo) {
|
||||
providerDisplay = new ListSearchResults(provider);
|
||||
} else {
|
||||
providerDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
|
||||
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._providerDisplays[provider.id] = providerDisplay;
|
||||
this._content.add(providerDisplay.actor);
|
||||
},
|
||||
|
||||
destroyProviderDisplay: function(provider) {
|
||||
this._providerDisplays[provider.id].destroy();
|
||||
delete this._providerDisplays[provider.id];
|
||||
},
|
||||
|
||||
_clearDisplay: function() {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let providerDisplay = this._providerDisplays[provider.id];
|
||||
providerDisplay.clear();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._searchSystem.reset();
|
||||
this._statusBin.hide();
|
||||
this._clearDisplay();
|
||||
this._defaultResult = null;
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
this.reset();
|
||||
this._statusText.set_text(_("Searching…"));
|
||||
this._statusBin.show();
|
||||
},
|
||||
|
||||
_maybeSetInitialSelection: function() {
|
||||
let newDefaultResult = null;
|
||||
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let display = this._providerDisplays[provider.id];
|
||||
|
||||
if (!display.actor.visible)
|
||||
continue;
|
||||
|
||||
let firstResult = display.getFirstResult();
|
||||
if (firstResult) {
|
||||
newDefaultResult = firstResult;
|
||||
break; // select this one!
|
||||
}
|
||||
}
|
||||
|
||||
if (newDefaultResult != this._defaultResult) {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(false);
|
||||
if (newDefaultResult)
|
||||
newDefaultResult.setSelected(this._highlightDefault);
|
||||
|
||||
this._defaultResult = newDefaultResult;
|
||||
}
|
||||
},
|
||||
|
||||
_updateStatusText: function () {
|
||||
let haveResults = false;
|
||||
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let display = this._providerDisplays[provider.id];
|
||||
if (display.getFirstResult()) {
|
||||
haveResults = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveResults) {
|
||||
this._statusText.set_text(_("No results."));
|
||||
this._statusBin.show();
|
||||
} else {
|
||||
this._statusBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateResults: function(searchSystem, results) {
|
||||
let terms = searchSystem.getTerms();
|
||||
let [provider, providerResults] = results;
|
||||
let display = this._providerDisplays[provider.id];
|
||||
|
||||
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
|
||||
this._maybeSetInitialSelection();
|
||||
this._updateStatusText();
|
||||
}));
|
||||
},
|
||||
|
||||
activateDefault: function() {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.activate();
|
||||
},
|
||||
|
||||
highlightDefault: function(highlight) {
|
||||
this._highlightDefault = highlight;
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(highlight);
|
||||
},
|
||||
|
||||
navigateFocus: function(direction) {
|
||||
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
|
||||
direction == (rtl ? Gtk.DirectionType.RIGHT
|
||||
: Gtk.DirectionType.LEFT) ||
|
||||
direction == Gtk.DirectionType.UP) {
|
||||
this.actor.navigate_focus(null, direction, false);
|
||||
return;
|
||||
}
|
||||
|
||||
let from = this._defaultResult ? this._defaultResult.actor : null;
|
||||
this.actor.navigate_focus(from, direction, false);
|
||||
}
|
||||
});
|
||||
|
||||
const ProviderIcon = new Lang.Class({
|
||||
Name: 'ProviderIcon',
|
||||
Extends: St.Button,
|
||||
|
||||
PROVIDER_ICON_SIZE: 48,
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.parent({ style_class: 'search-provider-icon',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
accessible_name: provider.appInfo.get_name(),
|
||||
track_hover: true });
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this.set_child(this._content);
|
||||
|
||||
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
|
||||
visible: false,
|
||||
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
|
||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
||||
gicon: provider.appInfo.get_icon() });
|
||||
this._content.add_actor(icon);
|
||||
this._content.add_actor(this.moreIcon);
|
||||
}
|
||||
});
|
@ -21,6 +21,7 @@ const _modes = {
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowScreencast: false,
|
||||
enabledExtensions: [],
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
@ -48,8 +49,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11yGreeter', 'display', 'keyboard',
|
||||
'volume', 'battery', 'powerMenu']
|
||||
right: ['a11yGreeter', 'keyboard', 'aggregateMenu'],
|
||||
},
|
||||
panelStyle: 'login-screen'
|
||||
},
|
||||
@ -62,7 +62,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: [],
|
||||
center: [],
|
||||
right: ['lockScreen']
|
||||
right: ['aggregateMenu']
|
||||
},
|
||||
panelStyle: 'lock-screen'
|
||||
},
|
||||
@ -74,7 +74,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: [],
|
||||
center: [],
|
||||
right: ['a11y', 'keyboard', 'lockScreen']
|
||||
right: ['a11y', 'keyboard', 'aggregateMenu']
|
||||
},
|
||||
panelStyle: 'unlock-screen'
|
||||
},
|
||||
@ -84,6 +84,7 @@ const _modes = {
|
||||
showCalendarEvents: true,
|
||||
allowSettings: true,
|
||||
allowExtensions: true,
|
||||
allowScreencast: true,
|
||||
hasRunDialog: true,
|
||||
hasWorkspaces: true,
|
||||
hasWindows: true,
|
||||
@ -92,12 +93,11 @@ const _modes = {
|
||||
isPrimary: true,
|
||||
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
|
||||
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
|
||||
'keyring', 'recorder', 'autorunManager', 'automountManager'],
|
||||
'keyring', 'autorunManager', 'automountManager'],
|
||||
panel: {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'system']
|
||||
right: ['a11y', 'keyboard', 'aggregateMenu']
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -185,10 +185,6 @@ const SessionMode = new Lang.Class({
|
||||
return this._modeStack[this._modeStack.length - 1];
|
||||
},
|
||||
|
||||
get allowScreencast() {
|
||||
return this.components.indexOf('recorder') != -1;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let params = this._modes[this.currentMode];
|
||||
let defaults;
|
||||
|
@ -12,58 +12,66 @@ const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Hash = imports.misc.hash;
|
||||
const Main = imports.ui.main;
|
||||
const Screencast = imports.ui.screencast;
|
||||
const Screenshot = imports.ui.screenshot;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<method name="Eval">
|
||||
<arg type="s" direction="in" name="script" />
|
||||
<arg type="b" direction="out" name="success" />
|
||||
<arg type="s" direction="out" name="result" />
|
||||
</method>
|
||||
<method name="FocusSearch"/>
|
||||
<method name="ShowOSD">
|
||||
<arg type="a{sv}" direction="in" name="params"/>
|
||||
</method>
|
||||
<method name="GrabAccelerator">
|
||||
<arg type="s" direction="in" name="accelerator"/>
|
||||
<arg type="u" direction="in" name="flags"/>
|
||||
<arg type="u" direction="out" name="action"/>
|
||||
</method>
|
||||
<method name="GrabAccelerators">
|
||||
<arg type="a(su)" direction="in" name="accelerators"/>
|
||||
<arg type="au" direction="out" name="actions"/>
|
||||
</method>
|
||||
<method name="UngrabAccelerator">
|
||||
<arg type="u" direction="in" name="action"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<signal name="AcceleratorActivated">
|
||||
<arg name="action" type="u" />
|
||||
<arg name="deviceid" type="u" />
|
||||
<arg name="timestamp" type="u" />
|
||||
</signal>
|
||||
<property name="Mode" type="s" access="read" />
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
</interface>;
|
||||
const GnomeShellIface = '<node> \
|
||||
<interface name="org.gnome.Shell"> \
|
||||
<method name="Eval"> \
|
||||
<arg type="s" direction="in" name="script" /> \
|
||||
<arg type="b" direction="out" name="success" /> \
|
||||
<arg type="s" direction="out" name="result" /> \
|
||||
</method> \
|
||||
<method name="FocusSearch"/> \
|
||||
<method name="ShowOSD"> \
|
||||
<arg type="a{sv}" direction="in" name="params"/> \
|
||||
</method> \
|
||||
<method name="FocusApp"> \
|
||||
<arg type="s" direction="in" name="id"/> \
|
||||
</method> \
|
||||
<method name="ShowApplications" /> \
|
||||
<method name="GrabAccelerator"> \
|
||||
<arg type="s" direction="in" name="accelerator"/> \
|
||||
<arg type="u" direction="in" name="flags"/> \
|
||||
<arg type="u" direction="out" name="action"/> \
|
||||
</method> \
|
||||
<method name="GrabAccelerators"> \
|
||||
<arg type="a(su)" direction="in" name="accelerators"/> \
|
||||
<arg type="au" direction="out" name="actions"/> \
|
||||
</method> \
|
||||
<method name="UngrabAccelerator"> \
|
||||
<arg type="u" direction="in" name="action"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
</method> \
|
||||
<signal name="AcceleratorActivated"> \
|
||||
<arg name="action" type="u" /> \
|
||||
<arg name="deviceid" type="u" /> \
|
||||
<arg name="timestamp" type="u" /> \
|
||||
</signal> \
|
||||
<property name="Mode" type="s" access="read" /> \
|
||||
<property name="OverviewActive" type="b" access="readwrite" /> \
|
||||
<property name="ShellVersion" type="s" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
|
||||
<method name="Lock">
|
||||
</method>
|
||||
<method name="GetActive">
|
||||
<arg name="active" direction="out" type="b" />
|
||||
</method>
|
||||
<method name="SetActive">
|
||||
<arg name="value" direction="in" type="b" />
|
||||
</method>
|
||||
<method name="GetActiveTime">
|
||||
<arg name="value" direction="out" type="u" />
|
||||
</method>
|
||||
<signal name="ActiveChanged">
|
||||
<arg name="new_value" type="b" />
|
||||
</signal>
|
||||
</interface>;
|
||||
const ScreenSaverIface = '<node> \
|
||||
<interface name="org.gnome.ScreenSaver"> \
|
||||
<method name="Lock"> \
|
||||
</method> \
|
||||
<method name="GetActive"> \
|
||||
<arg name="active" direction="out" type="b" /> \
|
||||
</method> \
|
||||
<method name="SetActive"> \
|
||||
<arg name="value" direction="in" type="b" /> \
|
||||
</method> \
|
||||
<method name="GetActiveTime"> \
|
||||
<arg name="value" direction="out" type="u" /> \
|
||||
</method> \
|
||||
<signal name="ActiveChanged"> \
|
||||
<arg name="new_value" type="b" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const GnomeShell = new Lang.Class({
|
||||
Name: 'GnomeShellDBus',
|
||||
@ -73,7 +81,6 @@ const GnomeShell = new Lang.Class({
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||
|
||||
this._extensionsService = new GnomeShellExtensions();
|
||||
this._screencastService = new Screencast.ScreencastService();
|
||||
this._screenshotService = new Screenshot.ScreenshotService();
|
||||
|
||||
this._grabbedAccelerators = new Hash.Map();
|
||||
@ -137,6 +144,15 @@ const GnomeShell = new Lang.Class({
|
||||
Main.osdWindow.show();
|
||||
},
|
||||
|
||||
FocusApp: function(id) {
|
||||
this.ShowApplications();
|
||||
Main.overview.viewSelector.appDisplay.selectApp(id);
|
||||
},
|
||||
|
||||
ShowApplications: function() {
|
||||
Main.overview.viewSelector.showApps();
|
||||
},
|
||||
|
||||
GrabAcceleratorAsync: function(params, invocation) {
|
||||
let [accel, flags] = params;
|
||||
let sender = invocation.get_sender();
|
||||
@ -234,41 +250,43 @@ const GnomeShell = new Lang.Class({
|
||||
ShellVersion: Config.PACKAGE_VERSION
|
||||
});
|
||||
|
||||
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
|
||||
<method name="ListExtensions">
|
||||
<arg type="a{sa{sv}}" direction="out" name="extensions" />
|
||||
</method>
|
||||
<method name="GetExtensionInfo">
|
||||
<arg type="s" direction="in" name="extension" />
|
||||
<arg type="a{sv}" direction="out" name="info" />
|
||||
</method>
|
||||
<method name="GetExtensionErrors">
|
||||
<arg type="s" direction="in" name="extension" />
|
||||
<arg type="as" direction="out" name="errors" />
|
||||
</method>
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
<arg type="s" name="error"/>
|
||||
</signal>
|
||||
<method name="InstallRemoteExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="s" direction="out" name="result"/>
|
||||
</method>
|
||||
<method name="UninstallExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="LaunchExtensionPrefs">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<method name="ReloadExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<method name="CheckForUpdates">
|
||||
</method>
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
</interface>;
|
||||
const GnomeShellExtensionsIface = '<node> \
|
||||
<interface name="org.gnome.Shell.Extensions"> \
|
||||
<method name="ListExtensions"> \
|
||||
<arg type="a{sa{sv}}" direction="out" name="extensions" /> \
|
||||
</method> \
|
||||
<method name="GetExtensionInfo"> \
|
||||
<arg type="s" direction="in" name="extension" /> \
|
||||
<arg type="a{sv}" direction="out" name="info" /> \
|
||||
</method> \
|
||||
<method name="GetExtensionErrors"> \
|
||||
<arg type="s" direction="in" name="extension" /> \
|
||||
<arg type="as" direction="out" name="errors" /> \
|
||||
</method> \
|
||||
<signal name="ExtensionStatusChanged"> \
|
||||
<arg type="s" name="uuid"/> \
|
||||
<arg type="i" name="state"/> \
|
||||
<arg type="s" name="error"/> \
|
||||
</signal> \
|
||||
<method name="InstallRemoteExtension"> \
|
||||
<arg type="s" direction="in" name="uuid"/> \
|
||||
<arg type="s" direction="out" name="result"/> \
|
||||
</method> \
|
||||
<method name="UninstallExtension"> \
|
||||
<arg type="s" direction="in" name="uuid"/> \
|
||||
<arg type="b" direction="out" name="success"/> \
|
||||
</method> \
|
||||
<method name="LaunchExtensionPrefs"> \
|
||||
<arg type="s" direction="in" name="uuid"/> \
|
||||
</method> \
|
||||
<method name="ReloadExtension"> \
|
||||
<arg type="s" direction="in" name="uuid"/> \
|
||||
</method> \
|
||||
<method name="CheckForUpdates"> \
|
||||
</method> \
|
||||
<property name="ShellVersion" type="s" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const GnomeShellExtensions = new Lang.Class({
|
||||
Name: 'GnomeShellExtensionsDBus',
|
||||
|
@ -521,36 +521,38 @@ const ShellProcessesDialog = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(ShellProcessesDialog.prototype);
|
||||
|
||||
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
|
||||
<method name="AskPassword">
|
||||
<arg type="s" direction="in" name="object_id"/>
|
||||
<arg type="s" direction="in" name="message"/>
|
||||
<arg type="s" direction="in" name="icon_name"/>
|
||||
<arg type="s" direction="in" name="default_user"/>
|
||||
<arg type="s" direction="in" name="default_domain"/>
|
||||
<arg type="u" direction="in" name="flags"/>
|
||||
<arg type="u" direction="out" name="response"/>
|
||||
<arg type="a{sv}" direction="out" name="response_details"/>
|
||||
</method>
|
||||
<method name="AskQuestion">
|
||||
<arg type="s" direction="in" name="object_id"/>
|
||||
<arg type="s" direction="in" name="message"/>
|
||||
<arg type="s" direction="in" name="icon_name"/>
|
||||
<arg type="as" direction="in" name="choices"/>
|
||||
<arg type="u" direction="out" name="response"/>
|
||||
<arg type="a{sv}" direction="out" name="response_details"/>
|
||||
</method>
|
||||
<method name="ShowProcesses">
|
||||
<arg type="s" direction="in" name="object_id"/>
|
||||
<arg type="s" direction="in" name="message"/>
|
||||
<arg type="s" direction="in" name="icon_name"/>
|
||||
<arg type="ai" direction="in" name="application_pids"/>
|
||||
<arg type="as" direction="in" name="choices"/>
|
||||
<arg type="u" direction="out" name="response"/>
|
||||
<arg type="a{sv}" direction="out" name="response_details"/>
|
||||
</method>
|
||||
<method name="Close"/>
|
||||
</interface>;
|
||||
const GnomeShellMountOpIface = '<node> \
|
||||
<interface name="org.Gtk.MountOperationHandler"> \
|
||||
<method name="AskPassword"> \
|
||||
<arg type="s" direction="in" name="object_id"/> \
|
||||
<arg type="s" direction="in" name="message"/> \
|
||||
<arg type="s" direction="in" name="icon_name"/> \
|
||||
<arg type="s" direction="in" name="default_user"/> \
|
||||
<arg type="s" direction="in" name="default_domain"/> \
|
||||
<arg type="u" direction="in" name="flags"/> \
|
||||
<arg type="u" direction="out" name="response"/> \
|
||||
<arg type="a{sv}" direction="out" name="response_details"/> \
|
||||
</method> \
|
||||
<method name="AskQuestion"> \
|
||||
<arg type="s" direction="in" name="object_id"/> \
|
||||
<arg type="s" direction="in" name="message"/> \
|
||||
<arg type="s" direction="in" name="icon_name"/> \
|
||||
<arg type="as" direction="in" name="choices"/> \
|
||||
<arg type="u" direction="out" name="response"/> \
|
||||
<arg type="a{sv}" direction="out" name="response_details"/> \
|
||||
</method> \
|
||||
<method name="ShowProcesses"> \
|
||||
<arg type="s" direction="in" name="object_id"/> \
|
||||
<arg type="s" direction="in" name="message"/> \
|
||||
<arg type="s" direction="in" name="icon_name"/> \
|
||||
<arg type="ai" direction="in" name="application_pids"/> \
|
||||
<arg type="as" direction="in" name="choices"/> \
|
||||
<arg type="u" direction="out" name="response"/> \
|
||||
<arg type="a{sv}" direction="out" name="response_details"/> \
|
||||
</method> \
|
||||
<method name="Close"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ShellMountOperationType = {
|
||||
NONE: 0,
|
||||
|
@ -5,6 +5,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||
|
||||
@ -19,14 +20,26 @@ const Slider = new Lang.Class({
|
||||
|
||||
this.actor = new St.DrawingArea({ style_class: 'slider',
|
||||
can_focus: true,
|
||||
reactive: true });
|
||||
reactive: true,
|
||||
accessible_role: Atk.Role.SLIDER });
|
||||
this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this.onKeyPressEvent));
|
||||
|
||||
this._releaseId = this._motionId = 0;
|
||||
this._dragging = false;
|
||||
|
||||
this._customAccessible = St.GenericAccessible.new_for_actor(this.actor);
|
||||
this.actor.set_accessible(this._customAccessible);
|
||||
|
||||
this._customAccessible.connect('get-current-value', Lang.bind(this, this._getCurrentValue));
|
||||
this._customAccessible.connect('get-minimum-value', Lang.bind(this, this._getMinimumValue));
|
||||
this._customAccessible.connect('get-maximum-value', Lang.bind(this, this._getMaximumValue));
|
||||
this._customAccessible.connect('get-minimum-increment', Lang.bind(this, this._getMinimumIncrement));
|
||||
this._customAccessible.connect('set-current-value', Lang.bind(this, this._setCurrentValue));
|
||||
|
||||
this.connect('value-changed', Lang.bind(this, this._valueChanged));
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
@ -168,12 +181,12 @@ const Slider = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function (actor, event) {
|
||||
onKeyPressEvent: function (actor, event) {
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
|
||||
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
|
||||
this._value = Math.max(0, Math.min(this._value + delta, 1));
|
||||
this._slider.queue_repaint();
|
||||
this.actor.queue_repaint();
|
||||
this.emit('value-changed', this._value);
|
||||
this.emit('drag-end');
|
||||
return true;
|
||||
@ -202,6 +215,30 @@ const Slider = new Lang.Class({
|
||||
this.emit('value-changed', this._value);
|
||||
},
|
||||
|
||||
_getCurrentValue: function (actor) {
|
||||
return this._value;
|
||||
},
|
||||
|
||||
_getMinimumValue: function (actor) {
|
||||
return 0;
|
||||
},
|
||||
|
||||
_getMaximumValue: function (actor) {
|
||||
return 1;
|
||||
},
|
||||
|
||||
_getMinimumIncrement: function (actor) {
|
||||
return 0.1;
|
||||
},
|
||||
|
||||
_setCurrentValue: function (actor, value) {
|
||||
this._value = value;
|
||||
},
|
||||
|
||||
_valueChanged: function (slider, value, property) {
|
||||
this._customAccessible.notify ("accessible-value");
|
||||
},
|
||||
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
@ -1,39 +1,54 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
|
||||
const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
|
||||
const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y';
|
||||
const KEY_ALWAYS_SHOW = 'always-show-universal-access-status';
|
||||
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const A11Y_KEYBOARD_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
|
||||
const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
|
||||
const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
|
||||
|
||||
const DPI_FACTOR_LARGE = 1.25;
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
|
||||
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
const KEY_VISUAL_BELL = 'visual-bell';
|
||||
const DPI_FACTOR_LARGE = 1.25;
|
||||
|
||||
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
|
||||
const KEY_GTK_THEME = 'gtk-theme';
|
||||
const KEY_ICON_THEME = 'icon-theme';
|
||||
const KEY_WM_THEME = 'theme';
|
||||
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
|
||||
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
const KEY_VISUAL_BELL = 'visual-bell';
|
||||
|
||||
const HIGH_CONTRAST_THEME = 'HighContrast';
|
||||
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
|
||||
const KEY_GTK_THEME = 'gtk-theme';
|
||||
const KEY_ICON_THEME = 'icon-theme';
|
||||
const KEY_WM_THEME = 'theme';
|
||||
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
|
||||
|
||||
const HIGH_CONTRAST_THEME = 'HighContrast';
|
||||
|
||||
const ATIndicator = new Lang.Class({
|
||||
Name: 'ATIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
|
||||
this.parent(0.0, _("Accessibility"));
|
||||
|
||||
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
|
||||
icon_name: 'preferences-desktop-accessibility-symbolic' }));
|
||||
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
|
||||
|
||||
this.actor.add_child(this._hbox);
|
||||
|
||||
this._a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
||||
this._a11ySettings.connect('changed::' + KEY_ALWAYS_SHOW, Lang.bind(this, this._queueSyncMenuVisibility));
|
||||
|
||||
let highContrast = this._buildHCItem();
|
||||
this.menu.addMenuItem(highContrast);
|
||||
@ -56,16 +71,16 @@ const ATIndicator = new Lang.Class({
|
||||
let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL);
|
||||
this.menu.addMenuItem(visualBell);
|
||||
|
||||
let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_SCHEMA, KEY_STICKY_KEYS_ENABLED);
|
||||
let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_KEYBOARD_SCHEMA, KEY_STICKY_KEYS_ENABLED);
|
||||
this.menu.addMenuItem(stickyKeys);
|
||||
|
||||
let slowKeys = this._buildItem(_("Slow Keys"), A11Y_SCHEMA, KEY_SLOW_KEYS_ENABLED);
|
||||
let slowKeys = this._buildItem(_("Slow Keys"), A11Y_KEYBOARD_SCHEMA, KEY_SLOW_KEYS_ENABLED);
|
||||
this.menu.addMenuItem(slowKeys);
|
||||
|
||||
let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_SCHEMA, KEY_BOUNCE_KEYS_ENABLED);
|
||||
let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_KEYBOARD_SCHEMA, KEY_BOUNCE_KEYS_ENABLED);
|
||||
this.menu.addMenuItem(bounceKeys);
|
||||
|
||||
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
|
||||
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_KEYBOARD_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
|
||||
this.menu.addMenuItem(mouseKeys);
|
||||
|
||||
this._syncMenuVisibility();
|
||||
@ -74,9 +89,10 @@ const ATIndicator = new Lang.Class({
|
||||
_syncMenuVisibility: function() {
|
||||
this._syncMenuVisibilityIdle = 0;
|
||||
|
||||
let alwaysShow = this._a11ySettings.get_boolean(KEY_ALWAYS_SHOW);
|
||||
let items = this.menu._getMenuItems();
|
||||
|
||||
this.actor.visible = items.some(function(f) { return !!f.state; });
|
||||
this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
|
||||
|
||||
return false;
|
||||
},
|
||||
|
@ -9,16 +9,18 @@ const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'BTIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
|
||||
this.parent();
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'bluetooth-active-symbolic';
|
||||
|
||||
// The Bluetooth menu only appears when Bluetooth is in use,
|
||||
// so just statically build it with a "Turn Off" menu item.
|
||||
@ -28,6 +30,7 @@ const Indicator = new Lang.Class({
|
||||
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||
}));
|
||||
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
this._applet = new GnomeBluetoothApplet.Applet();
|
||||
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
|
||||
@ -47,17 +50,17 @@ const Indicator = new Lang.Class({
|
||||
let nDevices = connectedDevices.length;
|
||||
|
||||
let on = nDevices > 0;
|
||||
this.mainIcon.visible = on;
|
||||
this.actor.visible = on;
|
||||
this._indicator.visible = on;
|
||||
this._item.actor.visible = on;
|
||||
|
||||
if (on)
|
||||
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
|
||||
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
|
||||
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
|
||||
this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-bluetooth-panel');
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
@ -102,8 +105,8 @@ const AuthNotification = new Lang.Class({
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
|
||||
this.addButton('allow', _("Allow"));
|
||||
this.addButton('deny', _("Deny"));
|
||||
this.addAction('allow', _("Allow"));
|
||||
this.addAction('deny', _("Deny"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'allow')
|
||||
@ -130,9 +133,9 @@ const AuthServiceNotification = new Lang.Class({
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
|
||||
|
||||
this.addButton('always-grant', _("Always grant access"));
|
||||
this.addButton('grant', _("Grant this time only"));
|
||||
this.addButton('reject', _("Reject"));
|
||||
this.addAction('always-grant', _("Always grant access"));
|
||||
this.addAction('grant', _("Grant this time only"));
|
||||
this.addAction('reject', _("Reject"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
@ -169,8 +172,8 @@ const ConfirmNotification = new Lang.Class({
|
||||
this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
|
||||
|
||||
/* Translators: this is the verb, not the noun */
|
||||
this.addButton('matches', _("Matches"));
|
||||
this.addButton('does-not-match', _("Does not match"));
|
||||
this.addAction('matches', _("Matches"));
|
||||
this.addAction('does-not-match', _("Does not match"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'matches')
|
||||
@ -204,47 +207,48 @@ const PinNotification = new Lang.Class({
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Return) {
|
||||
if (this._canActivateOkButton())
|
||||
this.emit('action-invoked', 'ok');
|
||||
this._ok();
|
||||
return true;
|
||||
} else if (key == Clutter.KEY_Escape) {
|
||||
this.emit('action-invoked', 'cancel');
|
||||
this._cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this.addActor(this._entry);
|
||||
|
||||
this.addButton('ok', _("OK"));
|
||||
this.addButton('cancel', _("Cancel"));
|
||||
let okButton = this.addAction(_("OK"), Lang.bind(this, this._ok));
|
||||
this.addAction(_("Cancel"), Lang.bind(this, this._cancel));
|
||||
|
||||
this.setButtonSensitive('ok', this._canActivateOkButton());
|
||||
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
|
||||
function() {
|
||||
this.setButtonSensitive('ok', this._canActivateOkButton());
|
||||
}));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'ok') {
|
||||
if (this._numeric) {
|
||||
let num = parseInt(this._entry.text);
|
||||
if (isNaN(num)) {
|
||||
// user reply was empty, or was invalid
|
||||
// cancel the operation
|
||||
num = -1;
|
||||
}
|
||||
this._applet.agent_reply_passkey(this._devicePath, num);
|
||||
} else
|
||||
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
|
||||
} else {
|
||||
if (this._numeric)
|
||||
this._applet.agent_reply_passkey(this._devicePath, -1);
|
||||
else
|
||||
this._applet.agent_reply_pincode(this._devicePath, null);
|
||||
}
|
||||
this.destroy();
|
||||
okButton.reactive = this._canActivateOkButton();
|
||||
this._entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
|
||||
okButton.reactive = this._canActivateOkButton();
|
||||
}));
|
||||
},
|
||||
|
||||
_ok: function() {
|
||||
if (this._numeric) {
|
||||
let num = parseInt(this._entry.text);
|
||||
if (isNaN(num)) {
|
||||
// user reply was empty, or was invalid
|
||||
// cancel the operation
|
||||
num = -1;
|
||||
}
|
||||
this._applet.agent_reply_passkey(this._devicePath, num);
|
||||
} else {
|
||||
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
|
||||
}
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_cancel: function() {
|
||||
if (this._numeric)
|
||||
this._applet.agent_reply_passkey(this._devicePath, -1);
|
||||
else
|
||||
this._applet.agent_reply_pincode(this._devicePath, null);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_canActivateOkButton: function() {
|
||||
// PINs have a fixed length of 6
|
||||
if (this._numeric)
|
||||
|
70
js/ui/status/brightness.js
Normal file
@ -0,0 +1,70 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Slider = imports.ui.slider;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
|
||||
const BrightnessInterface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Power.Screen"> \
|
||||
<property name="Brightness" type="i" access="readwrite"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'BrightnessIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent('display-brightness-symbolic');
|
||||
this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
}));
|
||||
|
||||
this._item = new PopupMenu.PopupBaseMenuItem({ activate: false });
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
this._slider = new Slider.Slider(0);
|
||||
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
||||
this._slider.actor.accessible_name = _("Brightness");
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'display-brightness-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._item.actor.add(icon);
|
||||
this._item.actor.add(this._slider.actor, { expand: true });
|
||||
this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||
this._slider.startDragging(event);
|
||||
}));
|
||||
this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
|
||||
return this._slider.onKeyPressEvent(actor, event);
|
||||
}));
|
||||
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value) {
|
||||
let percent = value * 100;
|
||||
this._proxy.Brightness = percent;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let visible = this._proxy.Brightness >= 0;
|
||||
this._item.actor.visible = visible;
|
||||
if (visible)
|
||||
this._slider.setValue(this._proxy.Brightness / 100.0);
|
||||
},
|
||||
});
|
@ -9,6 +9,7 @@ const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Gettext = imports.gettext;
|
||||
|
||||
try {
|
||||
var IBus = imports.gi.IBus;
|
||||
@ -40,12 +41,13 @@ const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
|
||||
|
||||
const KeyboardManagerInterface =
|
||||
<interface name="org.gnome.SettingsDaemon.Keyboard">
|
||||
<method name="SetInputSource">
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
const KeyboardManagerInterface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Keyboard"> \
|
||||
<method name="SetInputSource"> \
|
||||
<arg type="u" direction="in" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
|
||||
|
||||
@ -202,8 +204,8 @@ const LayoutMenuItem = new Lang.Class({
|
||||
|
||||
this.label = new St.Label({ text: displayName });
|
||||
this.indicator = new St.Label({ text: shortName });
|
||||
this.addActor(this.label);
|
||||
this.addActor(this.indicator);
|
||||
this.actor.add(this.label, { expand: true });
|
||||
this.actor.add(this.indicator);
|
||||
this.actor.label_actor = this.label;
|
||||
}
|
||||
});
|
||||
@ -336,7 +338,12 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
||||
this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
|
||||
this._container.connect('allocate', Lang.bind(this, this._containerAllocate));
|
||||
this.actor.add_actor(this._container);
|
||||
|
||||
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
this._hbox.add_child(this._container);
|
||||
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
|
||||
|
||||
this.actor.add_child(this._hbox);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
// All valid input sources currently in the gsettings
|
||||
@ -535,8 +542,12 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||
if (engineDesc) {
|
||||
let language = IBus.get_language_name(engineDesc.get_language());
|
||||
let longName = engineDesc.get_longname();
|
||||
let textdomain = engineDesc.get_textdomain();
|
||||
if (textdomain != '')
|
||||
longName = Gettext.dgettext(textdomain, longName);
|
||||
exists = true;
|
||||
displayName = language + ' (' + engineDesc.get_longname() + ')';
|
||||
displayName = '%s (%s)'.format(language, longName);
|
||||
shortName = this._makeEngineShortName(engineDesc);
|
||||
}
|
||||
}
|
||||
@ -896,7 +907,7 @@ const InputSourceIndicator = new Lang.Class({
|
||||
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
is.indicatorLabel.allocate_align_fill(box, 0.5, 0.5, false, false, flags);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,62 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const VolumeMenu = imports.ui.status.volume;
|
||||
|
||||
const FakeStatusIcon = new Lang.Class({
|
||||
Name: 'FakeStatusIcon',
|
||||
|
||||
_init: function(button) {
|
||||
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||
this._button = button;
|
||||
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
|
||||
this._button.actor.bind_property('visible', this.actor, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._reconstructIcons();
|
||||
},
|
||||
|
||||
_reconstructIcons: function() {
|
||||
this.actor.destroy_all_children();
|
||||
this._button.icons.forEach(Lang.bind(this, function(icon) {
|
||||
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
|
||||
icon.bind_property('gicon', newIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
icon.bind_property('visible', newIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this.actor.add_actor(newIcon);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LockScreenMenuIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent(null, _("Volume, network, battery"));
|
||||
this._box.style_class = 'lock-screen-status-button-box';
|
||||
|
||||
this._volumeControl = VolumeMenu.getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
|
||||
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
|
||||
this._box.add_child(this._volume.actor);
|
||||
|
||||
// Network may not exist if the user doesn't have NetworkManager
|
||||
if (Main.panel.statusArea.network) {
|
||||
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
|
||||
this._box.add_child(this._network.actor);
|
||||
}
|
||||
|
||||
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
|
||||
this._box.add_child(this._battery.actor);
|
||||
}
|
||||
});
|
@ -16,7 +16,6 @@ const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ModemManager = imports.misc.modemManager;
|
||||
const Util = imports.misc.util;
|
||||
@ -73,6 +72,27 @@ function ssidToLabel(ssid) {
|
||||
return label;
|
||||
}
|
||||
|
||||
function ensureActiveConnectionProps(active, settings) {
|
||||
if (!active._connection) {
|
||||
active._connection = settings.get_connection_by_path(active.connection);
|
||||
|
||||
// This list is guaranteed to have only one device in it.
|
||||
let device = active.get_devices()[0]._delegate;
|
||||
active._primaryDevice = device;
|
||||
}
|
||||
}
|
||||
|
||||
function createSettingsAction(label, device) {
|
||||
let item = new PopupMenu.PopupMenuItem(label);
|
||||
|
||||
item.connect('activate', function() {
|
||||
Util.spawnApp(['gnome-control-center', 'network', 'show-device',
|
||||
device.get_path()]);
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
const NMConnectionItem = new Lang.Class({
|
||||
Name: 'NMConnectionItem',
|
||||
|
||||
@ -97,14 +117,14 @@ const NMConnectionItem = new Lang.Class({
|
||||
},
|
||||
|
||||
getName: function() {
|
||||
return this.connection.get_id();
|
||||
return this._connection.get_id();
|
||||
},
|
||||
|
||||
isActive: function() {
|
||||
if (this._activeConnection == null)
|
||||
return false;
|
||||
|
||||
return this._activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED;
|
||||
return this._activeConnection.state <= NetworkManager.ActiveConnectionState.ACTIVATED;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
@ -141,7 +161,7 @@ const NMConnectionItem = new Lang.Class({
|
||||
this._activeConnection = activeConnection;
|
||||
|
||||
if (this._activeConnection)
|
||||
this._activeConnectionChangedId = this._activeConnection.connect('state-changed',
|
||||
this._activeConnectionChangedId = this._activeConnection.connect('notify::state',
|
||||
Lang.bind(this, this._connectionStateChanged));
|
||||
|
||||
this._sync();
|
||||
@ -170,8 +190,7 @@ const NMConnectionSection = new Lang.Class({
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.statusItem.destroy();
|
||||
this.section.destroy();
|
||||
this.item.destroy();
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
@ -182,12 +201,19 @@ const NMConnectionSection = new Lang.Class({
|
||||
|
||||
this.item.status.text = this._getStatus();
|
||||
this.item.icon.icon_name = this._getMenuIcon();
|
||||
this.item.label.text = this._getDescription();
|
||||
|
||||
// desc can be undefined at cold-plug, before we called
|
||||
// NMGtk.disambiguate_device_names() at least once
|
||||
let desc = this._getDescription();
|
||||
if (desc)
|
||||
this.item.label.text = desc;
|
||||
else
|
||||
this.item.label.text = '';
|
||||
},
|
||||
|
||||
_getStatus: function() {
|
||||
let values = this._connectionItems.values();
|
||||
for (let i = 0; i < values; i++) {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let item = values[i];
|
||||
if (item.isActive())
|
||||
return item.getName();
|
||||
@ -237,7 +263,7 @@ const NMConnectionSection = new Lang.Class({
|
||||
this.emit('activation-failed', reason);
|
||||
}));
|
||||
|
||||
let pos = Util.insertSorted(this._connections, connection, this._connectionSortFunction);
|
||||
let pos = Util.insertSorted(this._connections, connection, Lang.bind(this, this._connectionSortFunction));
|
||||
this._labelSection.addMenuItem(item.labelItem, pos);
|
||||
this._switchSection.addMenuItem(item.switchItem, pos);
|
||||
this._connectionItems.set(connection.get_uuid(), item);
|
||||
@ -261,12 +287,12 @@ const NMConnectionDevice = new Lang.Class({
|
||||
Extends: NMConnectionSection,
|
||||
Abstract: true,
|
||||
|
||||
_init: function(client, device) {
|
||||
_init: function(client, device, settings) {
|
||||
this.parent(client);
|
||||
this._device = device;
|
||||
this._settings = settings;
|
||||
|
||||
this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect));
|
||||
this.item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
|
||||
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged));
|
||||
@ -294,6 +320,7 @@ const NMConnectionDevice = new Lang.Class({
|
||||
this._activeConnection = this._device.active_connection;
|
||||
|
||||
if (this._activeConnection) {
|
||||
ensureActiveConnectionProps(this._activeConnection, this._settings);
|
||||
let item = this._connectionItems.get(this._activeConnection._connection.get_uuid());
|
||||
item.setActiveConnection(this._activeConnection);
|
||||
}
|
||||
@ -345,12 +372,13 @@ const NMConnectionDevice = new Lang.Class({
|
||||
|
||||
_getStatus: function() {
|
||||
if (!this._device)
|
||||
return null;
|
||||
return '';
|
||||
|
||||
switch(this._device.state) {
|
||||
case NetworkManager.DeviceState.DISCONNECTED:
|
||||
return _("Off");
|
||||
case NetworkManager.DeviceState.ACTIVATED:
|
||||
return null;
|
||||
return this.parent();
|
||||
case NetworkManager.DeviceState.UNMANAGED:
|
||||
/* Translators: this is for network devices that are physically present but are not
|
||||
under NetworkManager's control (and thus cannot be used in the menu) */
|
||||
@ -392,8 +420,11 @@ const NMDeviceModem = new Lang.Class({
|
||||
Extends: NMConnectionDevice,
|
||||
category: NMConnectionCategory.WWAN,
|
||||
|
||||
_init: function(client, device) {
|
||||
this.parent(client, device);
|
||||
_init: function(client, device, settings) {
|
||||
this.parent(client, device, settings);
|
||||
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
|
||||
|
||||
this._mobileDevice = null;
|
||||
|
||||
let capabilities = device.current_capabilities;
|
||||
@ -407,16 +438,7 @@ const NMDeviceModem = new Lang.Class({
|
||||
this._mobileDevice = new ModemManager.ModemGsm(device.udi);
|
||||
|
||||
if (this._mobileDevice) {
|
||||
this._operatorNameId = this._mobileDevice.connect('notify::operator-name', Lang.bind(this, function() {
|
||||
if (this._operatorItem) {
|
||||
let name = this._mobileDevice.operator_name;
|
||||
if (name) {
|
||||
this._operatorItem.label.text = name;
|
||||
this._operatorItem.actor.show();
|
||||
} else
|
||||
this._operatorItem.actor.hide();
|
||||
}
|
||||
}));
|
||||
this._operatorNameId = this._mobileDevice.connect('notify::operator-name', Lang.bind(this, this._sync));
|
||||
this._signalQualityId = this._mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() {
|
||||
this.emit('icon-changed');
|
||||
}));
|
||||
@ -441,6 +463,20 @@ const NMDeviceModem = new Lang.Class({
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_getStatus: function() {
|
||||
if (!this._client.wwan_hardware_enabled)
|
||||
return _("Hardware Disabled");
|
||||
else if (!this._client.wwan_enabled)
|
||||
/* Translators: this is for a network device that cannot be activated
|
||||
because it's disabled by rfkill (airplane mode) */
|
||||
return _("Disabled");
|
||||
else if (this._device.state == NetworkManager.DeviceState.ACTIVATED &&
|
||||
this._mobileDevice && this._mobileDevice.operator_name)
|
||||
return this._mobileDevice.operator_name;
|
||||
else
|
||||
return this.parent();
|
||||
},
|
||||
|
||||
_getMenuIcon: function() {
|
||||
if (this._device.active_connection)
|
||||
return this.getIndicatorIcon();
|
||||
@ -470,6 +506,12 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
Extends: NMConnectionDevice,
|
||||
category: NMConnectionCategory.WWAN,
|
||||
|
||||
_init: function(client, device, settings) {
|
||||
this.parent(client, device, settings);
|
||||
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
|
||||
},
|
||||
|
||||
_autoConnect: function() {
|
||||
// FIXME: DUN devices are configured like modems, so
|
||||
// We need to spawn the mobile wizard
|
||||
@ -507,44 +549,56 @@ const NMWirelessDialogItem = new Lang.Class({
|
||||
this._network = network;
|
||||
this._ap = network.accessPoints[0];
|
||||
|
||||
this.actor = new St.Button({ style_class: 'nm-dialog-item',
|
||||
can_focus: true,
|
||||
x_fill: true });
|
||||
this.actor = new St.BoxLayout({ style_class: 'nm-dialog-item',
|
||||
can_focus: true,
|
||||
reactive: true });
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, function() {
|
||||
this.emit('selected');
|
||||
}));
|
||||
this.actor.connect('clicked', Lang.bind(this, function() {
|
||||
let action = new Clutter.ClickAction();
|
||||
action.connect('clicked', Lang.bind(this, function() {
|
||||
this.actor.grab_key_focus();
|
||||
}));
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'nm-dialog-item-box' });
|
||||
this.actor.set_child(this._content);
|
||||
this.actor.add_action(action);
|
||||
|
||||
let title = ssidToLabel(this._ap.get_ssid());
|
||||
this._label = new St.Label({ text: title });
|
||||
|
||||
this.actor.label_actor = this._label;
|
||||
this._content.add(this._label, { expand: true, x_align: St.Align.START });
|
||||
this.actor.add(this._label, { x_align: St.Align.START });
|
||||
|
||||
this._selectedIcon = new St.Icon({ style_class: 'nm-dialog-icon',
|
||||
icon_name: 'object-select-symbolic' });
|
||||
this.actor.add(this._selectedIcon);
|
||||
|
||||
this._icons = new St.BoxLayout({ style_class: 'nm-dialog-icons' });
|
||||
this._content.add(this._icons, { x_fill: false, x_align: St.Align.END });
|
||||
this.actor.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||
|
||||
this._secureIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
|
||||
if (this._ap._secType != NMAccessPointSecurity.NONE)
|
||||
this._secureIcon.icon_name = 'network-wireless-encrypted-symbolic';
|
||||
this._icons.add_actor(this._secureIcon);
|
||||
|
||||
this._signalIcon = new St.Icon({ icon_name: this._getIcon(),
|
||||
style_class: 'nm-dialog-icon' });
|
||||
this._signalIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
|
||||
this._icons.add_actor(this._signalIcon);
|
||||
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this._signalIcon.icon_name = this._getSignalIcon();
|
||||
},
|
||||
|
||||
updateBestAP: function(ap) {
|
||||
this._ap = ap;
|
||||
this._signalIcon.icon_name = this._getIcon();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_getIcon: function() {
|
||||
setActive: function(isActive) {
|
||||
this._selectedIcon.opacity = isActive ? 255 : 0;
|
||||
},
|
||||
|
||||
_getSignalIcon: function() {
|
||||
if (this._ap.mode == NM80211Mode.ADHOC)
|
||||
return 'network-workgroup-symbolic';
|
||||
else
|
||||
@ -573,6 +627,7 @@ const NMWirelessDialog = new Lang.Class({
|
||||
|
||||
this._apAddedId = device.connect('access-point-added', Lang.bind(this, this._accessPointAdded));
|
||||
this._apRemovedId = device.connect('access-point-removed', Lang.bind(this, this._accessPointRemoved));
|
||||
this._activeApChangedId = device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged));
|
||||
|
||||
// accessPointAdded will also create dialog items
|
||||
let accessPoints = device.get_access_points() || [ ];
|
||||
@ -581,6 +636,7 @@ const NMWirelessDialog = new Lang.Class({
|
||||
}));
|
||||
|
||||
this._selectedNetwork = null;
|
||||
this._activeApChanged();
|
||||
this._updateSensitivity();
|
||||
},
|
||||
|
||||
@ -589,21 +645,44 @@ const NMWirelessDialog = new Lang.Class({
|
||||
GObject.Object.prototype.disconnect.call(this._device, this._apAddedId);
|
||||
this._apAddedId = 0;
|
||||
}
|
||||
|
||||
if (this._apRemovedId) {
|
||||
GObject.Object.prototype.disconnect.call(this._device, this._apRemovedId);
|
||||
this._apRemovedId = 0;
|
||||
}
|
||||
if (this._activeApChangedId) {
|
||||
GObject.Object.prototype.disconnect.call(this._device, this._activeApChangedId);
|
||||
this._activeApChangedId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_activeApChanged: function() {
|
||||
if (this._activeNetwork)
|
||||
this._activeNetwork.item.setActive(false);
|
||||
|
||||
this._activeNetwork = null;
|
||||
if (this._device.active_access_point) {
|
||||
let idx = this._findNetwork(this._device.active_access_point);
|
||||
if (idx >= 0)
|
||||
this._activeNetwork = this._networks[idx];
|
||||
}
|
||||
|
||||
if (this._activeNetwork)
|
||||
this._activeNetwork.item.setActive(true);
|
||||
this._updateSensitivity();
|
||||
},
|
||||
|
||||
_updateSensitivity: function() {
|
||||
let connectSensitive = this._selectedNetwork != null;
|
||||
let connectSensitive = this._selectedNetwork && (this._selectedNetwork != this._activeNetwork);
|
||||
this._connectButton.reactive = connectSensitive;
|
||||
this._connectButton.can_focus = connectSensitive;
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
this._noNetworksLabel.visible = (this._networks.length == 0);
|
||||
},
|
||||
|
||||
_buildLayout: function() {
|
||||
let headline = new St.BoxLayout({ style_class: 'nm-dialog-header-hbox' });
|
||||
|
||||
@ -624,12 +703,24 @@ const NMWirelessDialog = new Lang.Class({
|
||||
this.contentLayout.style_class = 'nm-dialog-content';
|
||||
this.contentLayout.add(headline);
|
||||
|
||||
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
|
||||
this._itemBox = new St.BoxLayout({ vertical: true });
|
||||
this._scrollView = new St.ScrollView({ style_class: 'nm-dialog-scroll-view' });
|
||||
this._scrollView.set_x_expand(true);
|
||||
this._scrollView.set_y_expand(true);
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
this._scrollView.add_actor(this._itemBox);
|
||||
this.contentLayout.add(this._scrollView);
|
||||
this._stack.add_child(this._scrollView);
|
||||
|
||||
this._noNetworksLabel = new St.Label({ style_class: 'no-networks-label',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
text: _("No Networks") });
|
||||
this._stack.add_child(this._noNetworksLabel);
|
||||
|
||||
this.contentLayout.add(this._stack, { expand: true });
|
||||
|
||||
this._disconnectButton = this.addButton({ action: Lang.bind(this, this.close),
|
||||
label: _("Cancel"),
|
||||
@ -824,6 +915,8 @@ const NMWirelessDialog = new Lang.Class({
|
||||
this._createNetworkItem(network);
|
||||
this._itemBox.insert_child_at_index(network.item.actor, newPos);
|
||||
}
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_accessPointRemoved: function(device, accessPoint) {
|
||||
@ -844,6 +937,8 @@ const NMWirelessDialog = new Lang.Class({
|
||||
network.item.updateBestAP(network.accessPoints[0]);
|
||||
this._resortItems();
|
||||
}
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_resortItems: function() {
|
||||
@ -860,17 +955,18 @@ const NMWirelessDialog = new Lang.Class({
|
||||
|
||||
_selectNetwork: function(network) {
|
||||
if (this._selectedNetwork)
|
||||
this._selectedNetwork.item.actor.checked = false;
|
||||
this._selectedNetwork.item.actor.remove_style_pseudo_class('selected');
|
||||
|
||||
this._selectedNetwork = network;
|
||||
this._updateSensitivity();
|
||||
|
||||
if (this._selectedNetwork)
|
||||
this._selectedNetwork.item.actor.checked = true;
|
||||
this._selectedNetwork.item.actor.add_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
_createNetworkItem: function(network) {
|
||||
network.item = new NMWirelessDialogItem(network);
|
||||
network.item.setActive(network == this._selectedNetwork);
|
||||
network.item.connect('selected', Lang.bind(this, function() {
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, network.item.actor);
|
||||
this._selectNetwork(network);
|
||||
@ -896,9 +992,10 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this._toggleItem.connect('activate', Lang.bind(this, this._toggleWifi));
|
||||
this.item.menu.addMenuItem(this._toggleItem);
|
||||
|
||||
this.item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Wi-Fi Settings"), device));
|
||||
|
||||
this._wirelessEnabledChangedId = this._device.connect('notify::wireless-enabled', Lang.bind(this, this._sync));
|
||||
this._wirelessEnabledChangedId = this._client.connect('notify::wireless-enabled', Lang.bind(this, this._sync));
|
||||
this._wirelessHwEnabledChangedId = this._client.connect('notify::wireless-hardware-enabled', Lang.bind(this, this._sync));
|
||||
this._activeApChangedId = this._device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged));
|
||||
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
|
||||
@ -918,6 +1015,14 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this._activeAccessPoint.disconnect(this._strengthChangedId);
|
||||
this._strengthChangedId = 0;
|
||||
}
|
||||
if (this._wirelessEnabledChangedId) {
|
||||
this._client.disconnect(this._wirelessEnabledChangedId);
|
||||
this._wirelessEnabledChangedId = 0;
|
||||
}
|
||||
if (this._wirelessHwEnabledChangedId) {
|
||||
this._client.disconnect(this._wirelessHwEnabledChangedId);
|
||||
this._wirelessHwEnabledChangedId = 0;
|
||||
}
|
||||
|
||||
this.item.destroy();
|
||||
},
|
||||
@ -976,6 +1081,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
_sync: function() {
|
||||
this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On");
|
||||
this._toggleItem.actor.visible = this._client.wireless_hardware_enabled;
|
||||
|
||||
this.item.status.text = this._getStatus();
|
||||
this.item.icon.icon_name = this._getMenuIcon();
|
||||
@ -989,10 +1095,17 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
_getStatus: function() {
|
||||
let ap = this._device.active_access_point;
|
||||
if (!ap)
|
||||
return _("Off"); // XXX -- interpret actual status
|
||||
|
||||
return ssidToLabel(ap.get_ssid());
|
||||
if (ap)
|
||||
return ssidToLabel(ap.get_ssid());
|
||||
else if (!this._client.wireless_hardware_enabled)
|
||||
return _("Hardware Disabled");
|
||||
else if (!this._client.wireless_enabled)
|
||||
return _("Off");
|
||||
else if (this._device.state == NetworkManager.DeviceState.DISCONNECTED)
|
||||
return _("Not Connected");
|
||||
else
|
||||
return '';
|
||||
},
|
||||
|
||||
_getMenuIcon: function() {
|
||||
@ -1003,7 +1116,8 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
|
||||
getIndicatorIcon: function() {
|
||||
if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
|
||||
this._device.state < NetworkManager.DeviceState.ACTIVATED)
|
||||
return 'network-wireless-acquiring-symbolic';
|
||||
|
||||
let ap = this._device.active_access_point;
|
||||
@ -1027,7 +1141,7 @@ const NMVPNConnectionItem = new Lang.Class({
|
||||
if (this._activeConnection == null)
|
||||
return false;
|
||||
|
||||
return this._activeConnection.vpn_state == NetworkManager.VPNConnectionState.ACTIVATED;
|
||||
return this._activeConnection.vpn_state != NetworkManager.VPNConnectionState.DISCONNECTED;
|
||||
},
|
||||
|
||||
_getStatus: function() {
|
||||
@ -1061,6 +1175,7 @@ const NMVPNConnectionItem = new Lang.Class({
|
||||
this.emit('activation-failed', reason);
|
||||
}
|
||||
|
||||
this.emit('icon-changed');
|
||||
this.parent();
|
||||
},
|
||||
|
||||
@ -1081,7 +1196,7 @@ const NMVPNConnectionItem = new Lang.Class({
|
||||
|
||||
getIndicatorIcon: function() {
|
||||
if (this._activeConnection) {
|
||||
if (this._activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
if (this._activeConnection.vpn_state < NetworkManager.VPNConnectionState.ACTIVATED)
|
||||
return 'network-vpn-acquiring-symbolic';
|
||||
else
|
||||
return 'network-vpn-symbolic';
|
||||
@ -1123,14 +1238,14 @@ const NMVPNSection = new Lang.Class({
|
||||
this._client.deactivate_connection(activeConnection);
|
||||
},
|
||||
|
||||
addActiveConnection: function(activeConnection) {
|
||||
let item = this._connectionItems.get(activeConnection._connection.get_uuid());
|
||||
item.setActiveConnection(activeConnection);
|
||||
},
|
||||
|
||||
removeActiveConnection: function(activeConnection) {
|
||||
let item = this._connectionItems.get(activeConnection._connection.get_uuid());
|
||||
item.setActiveConnection(null);
|
||||
setActiveConnections: function(vpnConnections) {
|
||||
this._connectionItems.values().forEach(function(item) {
|
||||
item.setActiveConnection(null);
|
||||
});
|
||||
vpnConnections.forEach(Lang.bind(this, function(a) {
|
||||
let item = this._connectionItems.get(a._connection.get_uuid());
|
||||
item.setActiveConnection(a);
|
||||
}));
|
||||
},
|
||||
|
||||
_makeConnectionItem: function(connection) {
|
||||
@ -1152,13 +1267,13 @@ Signals.addSignalMethods(NMVPNSection.prototype);
|
||||
|
||||
const NMApplet = new Lang.Class({
|
||||
Name: 'NMApplet',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent('network-offline-symbolic', _('Network'));
|
||||
this.parent();
|
||||
|
||||
this._vpnIcon = this.addIcon(null);
|
||||
this._vpnIcon.hide();
|
||||
this._primaryIndicator = this._addIndicator();
|
||||
this._vpnIndicator = this._addIndicator();
|
||||
|
||||
// Device types
|
||||
this._dtypes = { };
|
||||
@ -1201,28 +1316,27 @@ const NMApplet = new Lang.Class({
|
||||
this._mainConnection = null;
|
||||
this._mainConnectionIconChangedId = 0;
|
||||
|
||||
this._notification = null;
|
||||
|
||||
this._nmDevices = [];
|
||||
this._devices = { };
|
||||
|
||||
this._section = new PopupMenu.PopupMenuSection();
|
||||
this.menu.addMenuItem(this._section);
|
||||
|
||||
this._devices.wireless = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
};
|
||||
this._section.addMenuItem(this._devices.wireless.section);
|
||||
this.menu.addMenuItem(this._devices.wireless.section);
|
||||
|
||||
this._devices.wwan = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
};
|
||||
this._section.addMenuItem(this._devices.wwan.section);
|
||||
this.menu.addMenuItem(this._devices.wwan.section);
|
||||
|
||||
this._vpnSection = new NMVPNSection(this._client);
|
||||
this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
|
||||
this._vpnSection.connect('icon-changed', Lang.bind(this, this._updateIcon));
|
||||
this._section.addMenuItem(this._vpnSection.item);
|
||||
this.menu.addMenuItem(this._vpnSection.item);
|
||||
|
||||
this._readConnections();
|
||||
this._readDevices();
|
||||
@ -1231,7 +1345,9 @@ const NMApplet = new Lang.Class({
|
||||
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::active-connections', Lang.bind(this, this._syncActiveConnections));
|
||||
this._client.connect('notify::primary-connection', Lang.bind(this, this._syncMainConnection));
|
||||
this._client.connect('notify::activating-connection', Lang.bind(this, this._syncMainConnection));
|
||||
this._client.connect('notify::active-connections', Lang.bind(this, this._syncVPNConnections));
|
||||
this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
|
||||
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
|
||||
this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
|
||||
@ -1242,14 +1358,14 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
this._section.setSensitive(sensitive);
|
||||
this.menu.setSensitive(sensitive);
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Network Manager"),
|
||||
'network-transmit-receive');
|
||||
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
|
||||
this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
|
||||
|
||||
this._source.connect('destroy', Lang.bind(this, function() {
|
||||
this._source = null;
|
||||
@ -1266,32 +1382,29 @@ const NMApplet = new Lang.Class({
|
||||
this._syncDeviceNames();
|
||||
},
|
||||
|
||||
_notifyForDevice: function(device, iconName, title, text, urgency) {
|
||||
if (device._notification)
|
||||
device._notification.destroy();
|
||||
_notify: function(iconName, title, text, urgency) {
|
||||
if (this._notification)
|
||||
this._notification.destroy();
|
||||
|
||||
/* must call after destroying previous notification,
|
||||
or this._source will be cleared */
|
||||
this._ensureSource();
|
||||
|
||||
let gicon = new Gio.ThemedIcon({ name: iconName });
|
||||
device._notification = new MessageTray.Notification(this._source, title, text,
|
||||
{ gicon: gicon });
|
||||
device._notification.setUrgency(urgency);
|
||||
device._notification.setTransient(true);
|
||||
device._notification.connect('destroy', function() {
|
||||
device._notification = null;
|
||||
this._notification = new MessageTray.Notification(this._source, title, text, { gicon: gicon });
|
||||
this._notification.setUrgency(urgency);
|
||||
this._notification.setTransient(true);
|
||||
this._notification.connect('destroy', function() {
|
||||
this._notification = null;
|
||||
});
|
||||
this._source.notify(device._notification);
|
||||
this._source.notify(this._notification);
|
||||
},
|
||||
|
||||
_onActivationFailed: function(device, reason) {
|
||||
// XXX: nm-applet has no special text depending on reason
|
||||
// but I'm not sure of this generic message
|
||||
this._notifyForDevice(device, 'network-error-symbolic',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
this._notify('network-error-symbolic',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
},
|
||||
|
||||
_syncDeviceNames: function() {
|
||||
@ -1364,135 +1477,63 @@ const NMApplet = new Lang.Class({
|
||||
devices.splice(pos, 1);
|
||||
},
|
||||
|
||||
_getSupportedActiveConnections: function() {
|
||||
let activeConnections = this._client.get_active_connections() || [ ];
|
||||
let supportedConnections = [];
|
||||
_getMainConnection: function() {
|
||||
let connection;
|
||||
|
||||
for (let i = 0; i < activeConnections.length; i++) {
|
||||
let devices = activeConnections[i].get_devices();
|
||||
if (!devices || !devices[0])
|
||||
continue;
|
||||
// Ignore connections via unrecognized device types
|
||||
if (!this._dtypes[devices[0].device_type])
|
||||
continue;
|
||||
|
||||
// Ignore slave connections
|
||||
let connectionPath = activeConnections[i].connection;
|
||||
let connection = this._settings.get_connection_by_path(connectionPath);
|
||||
|
||||
// connection might be null, if libnm-glib fails to create
|
||||
// the object due to version incompatibility, or if the
|
||||
// connection is not visible to the current user
|
||||
if (connection && this._ignoreConnection(connection))
|
||||
continue;
|
||||
|
||||
supportedConnections.push(activeConnections[i]);
|
||||
connection = this._client.get_primary_connection();
|
||||
if (connection) {
|
||||
ensureActiveConnectionProps(connection, this._settings);
|
||||
return connection;
|
||||
}
|
||||
return supportedConnections;
|
||||
|
||||
connection = this._client.get_activating_connection();
|
||||
if (connection) {
|
||||
ensureActiveConnectionProps(connection, this._settings);
|
||||
return connection;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_syncActiveConnections: function() {
|
||||
let closedConnections = [ ];
|
||||
let newActiveConnections = this._getSupportedActiveConnections();
|
||||
for (let i = 0; i < this._activeConnections.length; i++) {
|
||||
let a = this._activeConnections[i];
|
||||
if (newActiveConnections.indexOf(a) == -1) // connection is removed
|
||||
closedConnections.push(a);
|
||||
}
|
||||
|
||||
for (let i = 0; i < closedConnections.length; i++) {
|
||||
let a = closedConnections[i];
|
||||
if (a._type == NetworkManager.SETTING_VPN_SETTING_NAME)
|
||||
this._vpnSection.removeActiveConnection(a);
|
||||
if (a._inited) {
|
||||
a.disconnect(a._notifyStateId);
|
||||
a.disconnect(a._notifyDefaultId);
|
||||
a.disconnect(a._notifyDefault6Id);
|
||||
a._inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
_syncMainConnection: function() {
|
||||
if (this._mainConnectionIconChangedId > 0) {
|
||||
this._mainConnection._primaryDevice.disconnect(this._mainConnectionIconChangedId);
|
||||
this._mainConnectionIconChangedId = 0;
|
||||
}
|
||||
|
||||
this._activeConnections = newActiveConnections;
|
||||
this._mainConnection = null;
|
||||
|
||||
let activating = null;
|
||||
let default_ip4 = null;
|
||||
let default_ip6 = null;
|
||||
let active_any = null;
|
||||
for (let i = 0; i < this._activeConnections.length; i++) {
|
||||
let a = this._activeConnections[i];
|
||||
|
||||
if (!a._inited) {
|
||||
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._syncActiveConnections));
|
||||
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._syncActiveConnections));
|
||||
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActivated));
|
||||
|
||||
a._inited = true;
|
||||
}
|
||||
|
||||
if (!a._connection) {
|
||||
a._connection = this._settings.get_connection_by_path(a.connection);
|
||||
|
||||
if (a._connection) {
|
||||
a._type = a._connection._type;
|
||||
a._section = this._ctypes[a._type];
|
||||
} else {
|
||||
a._connection = null;
|
||||
a._type = null;
|
||||
a._section = null;
|
||||
log('Cannot find connection for active (or connection cannot be read)');
|
||||
}
|
||||
}
|
||||
|
||||
if (a['default'])
|
||||
default_ip4 = a;
|
||||
if (a.default6)
|
||||
default_ip6 = a;
|
||||
|
||||
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
activating = a;
|
||||
else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED)
|
||||
active_any = a;
|
||||
|
||||
if (!a._primaryDevice) {
|
||||
if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) {
|
||||
// This list is guaranteed to have one device in it.
|
||||
a._primaryDevice = a.get_devices()[0]._delegate;
|
||||
} else {
|
||||
a._primaryDevice = this._vpnSection;
|
||||
this._vpnSection.addActiveConnection(a);
|
||||
}
|
||||
|
||||
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED
|
||||
&& a._primaryDevice && a._primaryDevice._notification) {
|
||||
a._primaryDevice._notification.destroy();
|
||||
a._primaryDevice._notification = null;
|
||||
}
|
||||
}
|
||||
if (this._mainConnectionStateChangedId > 0) {
|
||||
this._mainConnection.disconnect(this._mainConnectionStateChangedId);
|
||||
this._mainConnectionStateChangedId = 0;
|
||||
}
|
||||
|
||||
this._mainConnection = activating || default_ip4 || default_ip6 || active_any || null;
|
||||
this._mainConnection = this._getMainConnection();
|
||||
|
||||
if (this._mainConnection) {
|
||||
let dev = this._mainConnection._primaryDevice;
|
||||
this._mainConnectionIconChangedId = dev.connect('icon-changed', Lang.bind(this, this._updateIcon));
|
||||
this._updateIcon();
|
||||
if (this._mainConnection._primaryDevice)
|
||||
this._mainConnectionIconChangedId = this._mainConnection._primaryDevice.connect('icon-changed', Lang.bind(this, this._updateIcon));
|
||||
this._mainConnectionStateChangedId = this._mainConnection.connect('notify::state', Lang.bind(this, this._mainConnectionStateChanged));
|
||||
this._mainConnectionStateChanged();
|
||||
}
|
||||
|
||||
this._updateIcon();
|
||||
},
|
||||
|
||||
_notifyActivated: function(activeConnection) {
|
||||
if (activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED
|
||||
&& activeConnection._primaryDevice && activeConnection._primaryDevice._notification) {
|
||||
activeConnection._primaryDevice._notification.destroy();
|
||||
activeConnection._primaryDevice._notification = null;
|
||||
}
|
||||
_syncVPNConnections: function() {
|
||||
let activeConnections = this._client.get_active_connections() || [];
|
||||
let vpnConnections = activeConnections.filter(function(a) {
|
||||
return (a instanceof NMClient.VPNConnection);
|
||||
});
|
||||
vpnConnections.forEach(Lang.bind(this, function(a) {
|
||||
ensureActiveConnectionProps(a, this._settings);
|
||||
}));
|
||||
this._vpnSection.setActiveConnections(vpnConnections);
|
||||
|
||||
this._syncActiveConnections();
|
||||
this._updateIcon();
|
||||
},
|
||||
|
||||
_mainConnectionStateChanged: function() {
|
||||
if (this._mainConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED && this._notification)
|
||||
this._notification.destroy();
|
||||
},
|
||||
|
||||
_ignoreConnection: function(connection) {
|
||||
@ -1529,7 +1570,6 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
_newConnection: function(settings, connection) {
|
||||
this._addConnection(connection);
|
||||
this._syncActiveConnections();
|
||||
},
|
||||
|
||||
_connectionRemoved: function(connection) {
|
||||
@ -1572,35 +1612,30 @@ const NMApplet = new Lang.Class({
|
||||
this._vpnSection.checkConnection(connection);
|
||||
} else {
|
||||
let devices = this._devices[section].devices;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
devices[i].checkConnection(connection);
|
||||
}
|
||||
devices.forEach(function(wrapper) {
|
||||
if (wrapper instanceof NMConnectionSection)
|
||||
wrapper.checkConnection(connection);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_syncNMState: function() {
|
||||
this.mainIcon.visible = this._client.manager_running;
|
||||
this.actor.visible = this.mainIcon.visible;
|
||||
|
||||
this._syncActiveConnections();
|
||||
this._section.actor.visible = this._client.networking_enabled;
|
||||
this.indicators.visible = this._client.manager_running;
|
||||
this.menu.actor.visible = this._client.networking_enabled;
|
||||
},
|
||||
|
||||
_updateIcon: function() {
|
||||
let hasApIcon = false;
|
||||
let hasMobileIcon = false;
|
||||
|
||||
if (!this._client.networking_enabled || !this._mainConnection) {
|
||||
this.setIcon('network-offline-symbolic');
|
||||
this._primaryIndicator.icon_name = 'network-offline-symbolic';
|
||||
this._primaryIndicator.visible = true;
|
||||
} else {
|
||||
let dev = this._mainConnection._primaryDevice;
|
||||
if (!dev) {
|
||||
log('Active connection with no primary device?');
|
||||
return;
|
||||
}
|
||||
this.setIcon(dev.getIndicatorIcon());
|
||||
this._primaryIndicator.visible = (dev != null);
|
||||
if (dev)
|
||||
this._primaryIndicator.icon_name = dev.getIndicatorIcon();
|
||||
}
|
||||
|
||||
this._vpnIcon.icon_name = this._vpnSection.getIndicatorIcon();
|
||||
this._vpnIndicator.icon_name = this._vpnSection.getIndicatorIcon();
|
||||
this._vpnIndicator.visible = (this._vpnIndicator.icon_name != '');
|
||||
}
|
||||
});
|
||||
|
@ -8,29 +8,33 @@ const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
const BUS_NAME = 'org.freedesktop.UPower';
|
||||
const OBJECT_PATH = '/org/freedesktop/UPower/devices/DisplayDevice';
|
||||
|
||||
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
|
||||
<method name="GetDevices">
|
||||
<arg type="a(susdut)" direction="out" />
|
||||
</method>
|
||||
<method name="GetPrimaryDevice">
|
||||
<arg type="(susdut)" direction="out" />
|
||||
</method>
|
||||
<property name="Icon" type="s" access="read" />
|
||||
</interface>;
|
||||
const DisplayDeviceInterface = '<node> \
|
||||
<interface name="org.freedesktop.UPower.Device"> \
|
||||
<property name="Type" type="u" access="read"/> \
|
||||
<property name="State" type="u" access="read"/> \
|
||||
<property name="Percentage" type="d" access="read"/> \
|
||||
<property name="TimeToEmpty" type="x" access="read"/> \
|
||||
<property name="TimeToFull" type="x" access="read"/> \
|
||||
<property name="IsPresent" type="b" access="read"/> \
|
||||
<property name="IconName" type="s" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(PowerManagerInterface);
|
||||
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'PowerIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent('battery-missing-symbolic', _("Battery"));
|
||||
this.parent();
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
this._indicator = this._addIndicator();
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
@ -41,7 +45,7 @@ const Indicator = new Lang.Class({
|
||||
this._sync();
|
||||
}));
|
||||
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem("", true);
|
||||
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
@ -54,11 +58,18 @@ const Indicator = new Lang.Class({
|
||||
this.menu.setSensitive(sensitive);
|
||||
},
|
||||
|
||||
_statusForDevice: function(device) {
|
||||
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||
_getStatus: function() {
|
||||
let seconds = 0;
|
||||
|
||||
if (state == UPower.DeviceState.FULLY_CHARGED)
|
||||
if (this._proxy.State == UPower.DeviceState.FULLY_CHARGED)
|
||||
return _("Fully Charged");
|
||||
else if (this._proxy.State == UPower.DeviceState.CHARGING)
|
||||
seconds = this._proxy.TimeToFull;
|
||||
else if (this._proxy.State == UPower.DeviceState.DISCHARGING)
|
||||
seconds = this._proxy.TimeToEmpty;
|
||||
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
|
||||
else
|
||||
return _("Estimating…");
|
||||
|
||||
let time = Math.round(seconds / 60);
|
||||
if (time == 0) {
|
||||
@ -70,54 +81,43 @@ const Indicator = new Lang.Class({
|
||||
let minutes = time % 60;
|
||||
let hours = Math.floor(time / 60);
|
||||
|
||||
if (state == UPower.DeviceState.DISCHARGING) {
|
||||
if (this._proxy.State == UPower.DeviceState.DISCHARGING) {
|
||||
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
|
||||
return _("%d\u2236%d Remaining (%d%%)".format(hours, minutes, percentage));
|
||||
return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, this._proxy.Percentage);
|
||||
}
|
||||
|
||||
if (state == UPower.DeviceState.CHARGING) {
|
||||
if (this._proxy.State == UPower.DeviceState.CHARGING) {
|
||||
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
|
||||
return _("%d\u2236%d Until Full (%d%%)".format(hours, minutes, percentage));
|
||||
return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, this._proxy.Percentage);
|
||||
}
|
||||
|
||||
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
|
||||
return _("Estimating…");
|
||||
},
|
||||
|
||||
_syncStatusLabel: function() {
|
||||
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
|
||||
if (error) {
|
||||
this._item.actor.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let [device] = result;
|
||||
let [device_id, device_type] = device;
|
||||
if (device_type == UPower.DeviceKind.BATTERY) {
|
||||
this._item.status.text = this._statusForDevice(device);
|
||||
this._item.actor.show();
|
||||
} else {
|
||||
this._item.actor.hide();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_syncIcon: function() {
|
||||
let icon = this._proxy.Icon;
|
||||
let hasIcon = false;
|
||||
|
||||
if (icon) {
|
||||
let gicon = Gio.icon_new_for_string(icon);
|
||||
this.setGIcon(gicon);
|
||||
this._item.icon.gicon = gicon;
|
||||
hasIcon = true;
|
||||
}
|
||||
this.mainIcon.visible = hasIcon;
|
||||
this.actor.visible = hasIcon;
|
||||
return null;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this._syncIcon();
|
||||
this._syncStatusLabel();
|
||||
}
|
||||
// Do we have batteries or a UPS?
|
||||
let visible = this._proxy.IsPresent;
|
||||
if (visible) {
|
||||
this._item.actor.show();
|
||||
} else {
|
||||
// If there's no battery, then we use the power icon.
|
||||
this._item.actor.hide();
|
||||
this._indicator.icon_name = 'system-shutdown-symbolic';
|
||||
return;
|
||||
}
|
||||
|
||||
// The icons
|
||||
let icon = this._proxy.IconName;
|
||||
this._indicator.icon_name = icon;
|
||||
this._item.icon.icon_name = icon;
|
||||
|
||||
// The status label
|
||||
this._item.status.text = this._getStatus();
|
||||
|
||||
// The sub-menu heading
|
||||
if (this._proxy.Type == UPower.DeviceKind.UPS)
|
||||
this._item.label.text = _("UPS");
|
||||
else
|
||||
this._item.label.text = _("Battery");
|
||||
},
|
||||
});
|
||||
|
60
js/ui/status/rfkill.js
Normal file
@ -0,0 +1,60 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
|
||||
|
||||
const RfkillManagerInterface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
|
||||
<property name="AirplaneMode" type="b" access="readwrite" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'RfkillIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
}));
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'airplane-mode-symbolic';
|
||||
this._indicator.hide();
|
||||
|
||||
// The menu only appears when airplane mode is on, so just
|
||||
// statically build it as if it was on, rather than dynamically
|
||||
// changing the menu contents.
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true);
|
||||
this._item.icon.icon_name = 'airplane-mode-symbolic';
|
||||
this._item.status.text = _("On");
|
||||
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
|
||||
this._proxy.AirplaneMode = false;
|
||||
}));
|
||||
this._item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let airplaneMode = this._proxy.AirplaneMode;
|
||||
this._indicator.visible = airplaneMode;
|
||||
this._item.actor.visible = airplaneMode;
|
||||
},
|
||||
});
|
26
js/ui/status/screencast.js
Normal file
@ -0,0 +1,26 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'ScreencastIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'media-record-symbolic';
|
||||
this._indicator.add_style_class_name('screencast-indicator');
|
||||
this._sync();
|
||||
|
||||
Main.screencastService.connect('updated', Lang.bind(this, this._sync));
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this._indicator.visible = Main.screencastService.isRecording;
|
||||
},
|
||||
});
|
@ -1,24 +1,20 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
@ -28,33 +24,82 @@ const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
const AltSwitcher = new Lang.Class({
|
||||
Name: 'AltSwitcher',
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Remote" type="b" access="read"/>
|
||||
<property name="Class" type="s" access="read"/>
|
||||
<property name="Type" type="s" access="read"/>
|
||||
<property name="State" type="s" access="read"/>
|
||||
</interface>;
|
||||
_init: function(standard, alternate) {
|
||||
this._standard = standard;
|
||||
this._standard.connect('notify::visible', Lang.bind(this, this._sync));
|
||||
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
this._alternate = alternate;
|
||||
this._alternate.connect('notify::visible', Lang.bind(this, this._sync));
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
|
||||
this.actor = new St.Bin();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let childToShow = null;
|
||||
|
||||
if (this._standard.visible && this._alternate.visible) {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
let altPressed = (mods & Clutter.ModifierType.MOD1_MASK) != 0;
|
||||
childToShow = altPressed ? this._alternate : this._standard;
|
||||
} else if (this._standard.visible) {
|
||||
childToShow = this._standard;
|
||||
} else if (this._alternate.visible) {
|
||||
childToShow = this._alternate;
|
||||
}
|
||||
|
||||
if (this.actor.get_child() != childToShow) {
|
||||
this.actor.set_child(childToShow);
|
||||
|
||||
// The actors might respond to hover, so
|
||||
// sync the pointer to make sure they update.
|
||||
global.sync_pointer();
|
||||
}
|
||||
|
||||
this.actor.visible = (childToShow != null);
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
if (type == Clutter.EventType.KEY_PRESS || type == Clutter.EventType.KEY_RELEASE) {
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Alt_L || key == Clutter.KEY_Alt_R)
|
||||
this._sync();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'SystemIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent('system-shutdown-symbolic', _("System"));
|
||||
this.parent();
|
||||
|
||||
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
||||
this._orientationSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.peripherals.touchscreen' });
|
||||
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._haveShutdown = true;
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._haveShutdown = true;
|
||||
this._haveSuspend = true;
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
|
||||
@ -89,18 +134,46 @@ const Indicator = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveSuspend();
|
||||
}));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
this._orientationSettings.connect('changed::orientation-lock',
|
||||
Lang.bind(this, this._updateOrientationLock));
|
||||
this._orientationExists = false;
|
||||
Gio.DBus.session.watch_name('org.gnome.SettingsDaemon.Orientation',
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
Lang.bind(this, function() {
|
||||
this._orentationExists = true;
|
||||
this._updateOrientationLock();
|
||||
}),
|
||||
Lang.bind(this, function() {
|
||||
this._orentationExists = false;
|
||||
this._updateOrientationLock();
|
||||
}));
|
||||
this._updateOrientationLock();
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_updateActionsVisibility: function() {
|
||||
let visible = (this._settingsAction.visible ||
|
||||
this._orientationLockAction.visible ||
|
||||
this._lockScreenAction.visible ||
|
||||
this._altSwitcher.actor.visible);
|
||||
|
||||
this._actionsItem.actor.visible = visible;
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this._updateLockScreen();
|
||||
this._updatePowerOff();
|
||||
this._updateSuspend();
|
||||
this._updateMultiUser();
|
||||
this._settingsAction.visible = Main.sessionMode.allowSettings;
|
||||
this._updateActionsVisibility();
|
||||
},
|
||||
|
||||
_updateMultiUser: function() {
|
||||
@ -135,6 +208,17 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_updateSwitchUserSubMenu: function() {
|
||||
this._switchUserSubMenu.label.text = this._user.get_real_name();
|
||||
let clutterText = this._switchUserSubMenu.label.clutter_text;
|
||||
|
||||
// XXX -- for some reason, the ClutterText's width changes
|
||||
// rapidly unless we force a relayout of the actor. Probably
|
||||
// a size cache issue or something. Moving this to be a layout
|
||||
// manager would be a much better idea.
|
||||
clutterText.get_allocation_box();
|
||||
|
||||
let layout = clutterText.get_layout();
|
||||
if (layout.is_ellipsized())
|
||||
this._switchUserSubMenu.label.text = this._user.get_user_name();
|
||||
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
@ -145,28 +229,52 @@ const Indicator = new Lang.Class({
|
||||
let gicon = new Gio.FileIcon({ file: file });
|
||||
this._switchUserSubMenu.icon.gicon = gicon;
|
||||
} else {
|
||||
this._switchUserSubMenu.icon_name = 'avatar-default-symbolic';
|
||||
this._switchUserSubMenu.icon.icon_name = 'avatar-default-symbolic';
|
||||
}
|
||||
},
|
||||
|
||||
_updateOrientationLock: function() {
|
||||
this._orientationLockAction.visible = this._orientationExists;
|
||||
|
||||
let locked = this._orientationSettings.get_boolean('orientation-lock');
|
||||
let icon = this._orientationLockAction.child;
|
||||
icon.icon_name = locked ? 'rotation-locked-symbolic' : 'rotation-allowed-symbolic';
|
||||
|
||||
this._updateActionsVisibility();
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
this._lockScreenAction.visible = showLock && allowLockScreen && LoginManager.canLock();
|
||||
this._updateActionsVisibility();
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._session.CanShutdownRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._haveShutdown = result[0];
|
||||
this._updatePowerOff();
|
||||
}
|
||||
}));
|
||||
this._session.CanShutdownRemote(Lang.bind(this, function(result, error) {
|
||||
if (error)
|
||||
return;
|
||||
|
||||
this._haveShutdown = result[0];
|
||||
this._updatePowerOff();
|
||||
}));
|
||||
},
|
||||
|
||||
_updatePowerOff: function() {
|
||||
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked;
|
||||
this._updateActionsVisibility();
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._updateSuspend();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspend: function() {
|
||||
this._suspendAction.visible = this._haveSuspend && !Main.sessionMode.isLocked;
|
||||
this._updateActionsVisibility();
|
||||
},
|
||||
|
||||
_createActionButton: function(iconName, accessibleName) {
|
||||
@ -185,6 +293,15 @@ const Indicator = new Lang.Class({
|
||||
this._switchUserSubMenu = new PopupMenu.PopupSubMenuMenuItem('', true);
|
||||
this._switchUserSubMenu.icon.style_class = 'system-switch-user-submenu-icon';
|
||||
|
||||
// Since the label of the switch user submenu depends on the width of
|
||||
// the popup menu, and we can't easily connect on allocation-changed
|
||||
// or notify::width without creating layout cycles, simply update the
|
||||
// label whenever the menu is opened.
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen)
|
||||
this._updateSwitchUserSubMenu();
|
||||
}));
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
this._switchUserSubMenu.menu.addMenuItem(item);
|
||||
@ -202,24 +319,31 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
let hbox = new St.BoxLayout({ style_class: 'system-menu-actions-box' });
|
||||
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
|
||||
can_focus: false });
|
||||
|
||||
this._settingsAction = this._createActionButton('preferences-system-symbolic', _("Settings"));
|
||||
this._settingsAction.connect('clicked', Lang.bind(this, this._onSettingsClicked));
|
||||
hbox.add(this._settingsAction, { expand: true, x_fill: false });
|
||||
item.actor.add(this._settingsAction, { expand: true, x_fill: false });
|
||||
|
||||
this._orientationLockAction = this._createActionButton('', _("Orientation Lock"));
|
||||
this._orientationLockAction.connect('clicked', Lang.bind(this, this._onOrientationLockClicked));
|
||||
item.actor.add(this._orientationLockAction, { expand: true, x_fill: false });
|
||||
|
||||
this._lockScreenAction = this._createActionButton('changes-prevent-symbolic', _("Lock"));
|
||||
this._lockScreenAction.connect('clicked', Lang.bind(this, this._onLockScreenClicked));
|
||||
hbox.add(this._lockScreenAction, { expand: true, x_fill: false });
|
||||
item.actor.add(this._lockScreenAction, { expand: true, x_fill: false });
|
||||
|
||||
this._suspendAction = this._createActionButton('media-playback-pause-symbolic', _("Suspend"));
|
||||
this._suspendAction.connect('clicked', Lang.bind(this, this._onSuspendClicked));
|
||||
|
||||
this._powerOffAction = this._createActionButton('system-shutdown-symbolic', _("Power Off"));
|
||||
this._powerOffAction.connect('clicked', Lang.bind(this, this._onPowerOffClicked));
|
||||
hbox.add(this._powerOffAction, { expand: true, x_fill: false });
|
||||
|
||||
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
|
||||
can_focus: false });
|
||||
item.addActor(hbox, { expand: true });
|
||||
this._altSwitcher = new AltSwitcher(this._powerOffAction, this._suspendAction);
|
||||
item.actor.add(this._altSwitcher.actor, { expand: true, x_fill: false });
|
||||
|
||||
this._actionsItem = item;
|
||||
this.menu.addMenuItem(item);
|
||||
},
|
||||
|
||||
@ -230,6 +354,13 @@ const Indicator = new Lang.Class({
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_onOrientationLockClicked: function() {
|
||||
this.menu.itemActivated();
|
||||
let locked = this._orientationSettings.get_boolean('orientation-lock');
|
||||
this._orientationSettings.set_boolean('orientation-lock', !locked);
|
||||
this._updateOrientationLock();
|
||||
},
|
||||
|
||||
_onLockScreenClicked: function() {
|
||||
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
@ -241,7 +372,11 @@ const Indicator = new Lang.Class({
|
||||
Main.overview.hide();
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.lock(false);
|
||||
Gdm.goto_login_session_sync(null);
|
||||
|
||||
Clutter.threads_add_repaint_func_full(Clutter.RepaintFlags.POST_PAINT, function() {
|
||||
Gdm.goto_login_session_sync(null);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
@ -249,110 +384,14 @@ const Indicator = new Lang.Class({
|
||||
this._session.LogoutRemote(0);
|
||||
},
|
||||
|
||||
_openSessionWarnDialog: function(sessions) {
|
||||
let dialog = new ModalDialog.ModalDialog();
|
||||
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
|
||||
text: _("Other users are logged in.") });
|
||||
dialog.contentLayout.add(subjectLabel, { y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
|
||||
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
|
||||
descriptionLabel.clutter_text.line_wrap = true;
|
||||
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
|
||||
y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
|
||||
scrollView.add_style_class_name('vfade');
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
|
||||
|
||||
let userList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(userList);
|
||||
|
||||
for (let i = 0; i < sessions.length; i++) {
|
||||
let session = sessions[i];
|
||||
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
|
||||
vertical: false });
|
||||
let avatar = new UserWidget.Avatar(session.user);
|
||||
avatar.update();
|
||||
userEntry.add(avatar.actor);
|
||||
|
||||
let userLabelText = "";;
|
||||
let userName = session.user.get_real_name() ?
|
||||
session.user.get_real_name() : session.username;
|
||||
|
||||
if (session.info.remote)
|
||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||
userLabelText = _("%s (remote)").format(userName);
|
||||
else if (session.info.type == "tty")
|
||||
/* Translators: Console here refers to a tty like a VT console */
|
||||
userLabelText = _("%s (console)").format(userName);
|
||||
else
|
||||
userLabelText = userName;
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||
vertical: true });
|
||||
textLayout.add(new St.Label({ text: userLabelText }),
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
userEntry.add(textLayout, { expand: true });
|
||||
userList.add(userEntry, { x_fill: true });
|
||||
}
|
||||
|
||||
let cancelButton = { label: _("Cancel"),
|
||||
action: function() { dialog.close(); },
|
||||
key: Clutter.Escape };
|
||||
|
||||
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
|
||||
dialog.close();
|
||||
this._session.ShutdownRemote();
|
||||
}), default: true };
|
||||
|
||||
dialog.setButtons([cancelButton, powerOffButton]);
|
||||
|
||||
dialog.open();
|
||||
},
|
||||
|
||||
_onPowerOffClicked: function() {
|
||||
this.menu.itemActivated();
|
||||
Main.overview.hide();
|
||||
this._loginManager.listSessions(Lang.bind(this, function(result) {
|
||||
let sessions = [];
|
||||
let n = 0;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||
let proxy = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
sessionPath);
|
||||
this._session.ShutdownRemote(0);
|
||||
},
|
||||
|
||||
if (proxy.Class != 'user')
|
||||
continue;
|
||||
|
||||
if (proxy.State == 'closing')
|
||||
continue;
|
||||
|
||||
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||
continue;
|
||||
|
||||
sessions.push({ user: this._userManager.get_user(userName),
|
||||
username: userName,
|
||||
info: { type: proxy.Type,
|
||||
remote: proxy.Remote }
|
||||
});
|
||||
|
||||
// limit the number of entries
|
||||
n++;
|
||||
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||
break;
|
||||
}
|
||||
|
||||
if (n != 0)
|
||||
this._openSessionWarnDialog(sessions);
|
||||
else
|
||||
this._session.ShutdownRemote();
|
||||
}));
|
||||
}
|
||||
_onSuspendClicked: function() {
|
||||
this.menu.itemActivated();
|
||||
this._loginManager.suspend();
|
||||
},
|
||||
});
|
||||
|
@ -39,13 +39,14 @@ const StreamSlider = new Lang.Class({
|
||||
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
|
||||
this.item.addActor(this._icon, { align: St.Align.MIDDLE });
|
||||
this.item.addActor(this._slider.actor, { expand: true });
|
||||
|
||||
this.item.actor.add(this._icon);
|
||||
this.item.actor.add(this._slider.actor, { expand: true });
|
||||
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||
this._slider.startDragging(event);
|
||||
}));
|
||||
this.item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
|
||||
return this._slider.onKeyPressEvent(actor, event);
|
||||
}));
|
||||
|
||||
this._stream = null;
|
||||
},
|
||||
@ -156,6 +157,11 @@ const OutputStreamSlider = new Lang.Class({
|
||||
Name: 'OutputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_init: function(control) {
|
||||
this.parent(control);
|
||||
this._slider.actor.accessible_name = _("Volume");
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
@ -204,6 +210,7 @@ const InputStreamSlider = new Lang.Class({
|
||||
|
||||
_init: function(control) {
|
||||
this.parent(control);
|
||||
this._slider.actor.accessible_name = _("Microphone");
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
this._icon.icon_name = 'audio-input-microphone-symbolic';
|
||||
@ -296,22 +303,29 @@ const VolumeMenu = new Lang.Class({
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'VolumeIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent('audio-volume-muted-symbolic', _("Volume"));
|
||||
this.parent();
|
||||
|
||||
this._primaryIndicator = this._addIndicator();
|
||||
|
||||
this._control = getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu(this._control);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
|
||||
let icon = this._volumeMenu.getIcon();
|
||||
this.actor.visible = (icon != null);
|
||||
this.setIcon(icon);
|
||||
|
||||
if (icon != null) {
|
||||
this.indicators.show();
|
||||
this._primaryIndicator.icon_name = icon;
|
||||
} else {
|
||||
this.indicators.hide();
|
||||
}
|
||||
}));
|
||||
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
this.indicators.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
|
@ -9,6 +9,7 @@ const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
@ -33,6 +34,7 @@ const UnlockDialog = new Lang.Class({
|
||||
_init: function(parentActor) {
|
||||
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
|
||||
style_class: 'login-dialog',
|
||||
layout_manager: new Clutter.BoxLayout(),
|
||||
visible: false });
|
||||
|
||||
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
@ -42,15 +44,17 @@ const UnlockDialog = new Lang.Class({
|
||||
this._userName = GLib.get_user_name();
|
||||
this._user = this._userManager.get_user(this._userName);
|
||||
|
||||
this._promptBox = new St.BoxLayout({ vertical: true });
|
||||
this._promptBox = new St.BoxLayout({ vertical: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
this.actor.add_child(this._promptBox);
|
||||
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
|
||||
align_axis: Clutter.AlignAxis.BOTH,
|
||||
factor: 0.5 }));
|
||||
|
||||
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
|
||||
this._authPrompt.connect('failed', Lang.bind(this, this._fail));
|
||||
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._authPrompt.setUser(this._user);
|
||||
this._authPrompt.setPasswordChar('\u25cf');
|
||||
this._authPrompt.nextButton.label = _("Unlock");
|
||||
|
||||
@ -74,12 +78,12 @@ const UnlockDialog = new Lang.Class({
|
||||
this._otherUserButton = null;
|
||||
}
|
||||
|
||||
this._authPrompt.begin({ userName: this._userName });
|
||||
this._authPrompt.reset();
|
||||
this._updateSensitivity(true);
|
||||
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic');
|
||||
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleMonitor = Meta.IdleMonitor.get_core();
|
||||
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
||||
},
|
||||
|
||||
@ -92,15 +96,25 @@ const UnlockDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
_fail: function() {
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
_escape: function() {
|
||||
if (this.allowCancel) {
|
||||
this._authPrompt.cancel();
|
||||
this.emit('failed');
|
||||
_onReset: function(authPrompt, beginRequest) {
|
||||
let userName;
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
this._authPrompt.setUser(this._user);
|
||||
userName = this._userName;
|
||||
} else {
|
||||
userName = null;
|
||||
}
|
||||
|
||||
this._authPrompt.begin({ userName: userName });
|
||||
},
|
||||
|
||||
_escape: function() {
|
||||
if (this.allowCancel)
|
||||
this._authPrompt.cancel();
|
||||
},
|
||||
|
||||
_otherUserClicked: function(button, event) {
|
||||
|
@ -29,7 +29,9 @@ const Avatar = new Lang.Class({
|
||||
|
||||
this.actor = new St.Bin({ style_class: params.styleClass,
|
||||
track_hover: params.reactive,
|
||||
reactive: params.reactive });
|
||||
reactive: params.reactive,
|
||||
width: this._iconSize,
|
||||
height: this._iconSize });
|
||||
},
|
||||
|
||||
setSensitive: function(sensitive) {
|
||||
|
@ -16,10 +16,8 @@ const OverviewControls = imports.ui.overviewControls;
|
||||
const Params = imports.misc.params;
|
||||
const RemoteSearch = imports.ui.remoteSearch;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Wanda = imports.ui.wanda;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
@ -85,8 +83,12 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-find-symbolic' }));
|
||||
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear-symbolic' });
|
||||
if (this._entry.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear-rtl-symbolic' });
|
||||
else
|
||||
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear-symbolic' });
|
||||
|
||||
this._iconClickedId = 0;
|
||||
this._capturedEventId = 0;
|
||||
@ -95,11 +97,11 @@ const ViewSelector = new Lang.Class({
|
||||
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
|
||||
_("Windows"), 'emblem-documents-symbolic');
|
||||
|
||||
this._appDisplay = new AppDisplay.AppDisplay();
|
||||
this._appsPage = this._addPage(this._appDisplay.actor,
|
||||
this.appDisplay = new AppDisplay.AppDisplay();
|
||||
this._appsPage = this._addPage(this.appDisplay.actor,
|
||||
_("Applications"), 'view-grid-symbolic');
|
||||
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
|
||||
this._searchResults = new Search.SearchResults(this._searchSystem);
|
||||
this._searchPage = this._addPage(this._searchResults.actor,
|
||||
_("Search"), 'edit-find-symbolic',
|
||||
{ a11yFocus: this._entry });
|
||||
@ -110,8 +112,6 @@ const ViewSelector = new Lang.Class({
|
||||
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
|
||||
|
||||
// Default search providers
|
||||
// Wanda comes obviously first
|
||||
this.addSearchProvider(new Wanda.WandaSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
|
||||
// Load remote search providers provided by applications
|
||||
@ -165,6 +165,11 @@ const ViewSelector = new Lang.Class({
|
||||
this._showAppsButton.checked = !this._showAppsButton.checked;
|
||||
},
|
||||
|
||||
showApps: function() {
|
||||
Main.overview.show();
|
||||
this._showAppsButton.checked = true;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._activePage = this._workspacesPage;
|
||||
|
||||
@ -525,6 +530,13 @@ const ViewSelector = new Lang.Class({
|
||||
return ViewPage.SEARCH;
|
||||
},
|
||||
|
||||
setActivePage: function(page) {
|
||||
if (page == ViewPage.WINDOWS)
|
||||
this._showPage(this._workspacesPage);
|
||||
else
|
||||
this._showPage(this._appsPage);
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
let actor = this._activePage;
|
||||
Tweener.addTween(actor, { opacity: 255,
|
||||
|
157
js/ui/wanda.js
@ -1,157 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const Panel = imports.ui.panel;
|
||||
|
||||
const FISH_NAME = 'wanda';
|
||||
const FISH_FILENAME = 'wanda.png';
|
||||
const FISH_SPEED = 300;
|
||||
const FISH_COMMAND = 'fortune';
|
||||
// The size of an individual frame in the animation
|
||||
const FISH_HEIGHT = 22;
|
||||
const FISH_WIDTH = 36;
|
||||
|
||||
const FISH_GROUP = 'Fish Animation';
|
||||
|
||||
const MAGIC_FISH_KEY = 'free the fish';
|
||||
|
||||
const WandaIcon = new Lang.Class({
|
||||
Name: 'WandaIcon',
|
||||
Extends: IconGrid.BaseIcon,
|
||||
|
||||
_init : function(fish, label, params) {
|
||||
this.parent(label, params);
|
||||
|
||||
this._fish = fish;
|
||||
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
|
||||
|
||||
this._imgHeight = FISH_HEIGHT;
|
||||
this._imgWidth = FISH_WIDTH;
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
|
||||
this._animations.play();
|
||||
return this._animations.actor;
|
||||
},
|
||||
|
||||
_createIconTexture: function(size) {
|
||||
if (size == this.iconSize)
|
||||
return;
|
||||
|
||||
this.parent(size);
|
||||
}
|
||||
});
|
||||
|
||||
const WandaIconBin = new Lang.Class({
|
||||
Name: 'WandaIconBin',
|
||||
|
||||
_init: function(fish, label, params) {
|
||||
this.actor = new St.Bin({ reactive: true,
|
||||
track_hover: true });
|
||||
this.icon = new WandaIcon(fish, label, params);
|
||||
|
||||
this.actor.child = this.icon.actor;
|
||||
this.actor.label_actor = this.icon.label;
|
||||
},
|
||||
});
|
||||
|
||||
const FortuneDialog = new Lang.Class({
|
||||
Name: 'FortuneDialog',
|
||||
|
||||
_init: function(name, command) {
|
||||
let text;
|
||||
|
||||
try {
|
||||
let [res, stdout, stderr, status] = GLib.spawn_command_line_sync(command);
|
||||
text = String.fromCharCode.apply(null, stdout);
|
||||
} catch(e) {
|
||||
text = _("Sorry, no wisdom for you today:\n%s").format(e.message);
|
||||
}
|
||||
|
||||
this._title = new St.Label({ style_class: 'prompt-dialog-headline',
|
||||
text: _("%s the Oracle says").format(name) });
|
||||
this._label = new St.Label({ style_class: 'prompt-dialog-description',
|
||||
text: text });
|
||||
this._label.clutter_text.line_wrap = true;
|
||||
|
||||
this._box = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'prompt-dialog' // this is just to force a reasonable width
|
||||
});
|
||||
this._box.add(this._title, { align: St.Align.MIDDLE });
|
||||
this._box.add(this._label, { expand: true });
|
||||
|
||||
this._button = new St.Button({ button_mask: St.ButtonMask.ONE,
|
||||
style_class: 'modal-dialog',
|
||||
reactive: true });
|
||||
this._button.connect('clicked', Lang.bind(this, this.destroy));
|
||||
this._button.child = this._box;
|
||||
|
||||
this._bin = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this._bin.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
this._bin.add_actor(this._button);
|
||||
|
||||
Main.layoutManager.addChrome(this._bin);
|
||||
|
||||
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 10, Lang.bind(this, this.destroy));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._bin.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
function capitalize(str) {
|
||||
return str[0].toUpperCase() + str.substring(1, str.length);
|
||||
}
|
||||
|
||||
const WandaSearchProvider = new Lang.Class({
|
||||
Name: 'WandaSearchProvider',
|
||||
|
||||
_init: function() {
|
||||
this.id = 'wanda';
|
||||
},
|
||||
|
||||
getResultMetas: function(fish, callback) {
|
||||
callback([{ 'id': fish[0], // there may be many fish in the sea, but
|
||||
// only one which speaks the truth!
|
||||
'name': capitalize(fish[0]),
|
||||
'createIcon': function(iconSize) {
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string('face-smile'),
|
||||
icon_size: iconSize });
|
||||
}
|
||||
}]);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
if (terms.join(' ') == MAGIC_FISH_KEY) {
|
||||
this.searchSystem.setResults(this, [ FISH_NAME ]);
|
||||
} else {
|
||||
this.searchSystem.setResults(this, []);
|
||||
}
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
this.getInitialResultSet(terms);
|
||||
},
|
||||
|
||||
activateResult: function(fish) {
|
||||
if (this._dialog)
|
||||
this._dialog.destroy();
|
||||
this._dialog = new FortuneDialog(capitalize(fish), FISH_COMMAND);
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let icon = new WandaIconBin(resultMeta.id, resultMeta.name);
|
||||
return icon.actor;
|
||||
}
|
||||
});
|
@ -39,6 +39,9 @@ const WindowAttentionHandler = new Lang.Class({
|
||||
let [title, banner] = this._getTitleAndBanner(app, window);
|
||||
|
||||
let notification = new MessageTray.Notification(source, title, banner);
|
||||
notification.connect('clicked', function() {
|
||||
source.open();
|
||||
});
|
||||
notification.setForFeedback(true);
|
||||
|
||||
source.notify(notification);
|
||||
@ -79,7 +82,7 @@ const Source = new Lang.Class({
|
||||
return this._app.create_icon_texture(size);
|
||||
},
|
||||
|
||||
open : function(notification) {
|
||||
open: function() {
|
||||
Main.activateWindow(this._window);
|
||||
this.destroy();
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const AltTab = imports.ui.altTab;
|
||||
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
@ -20,6 +22,107 @@ const DIM_BRIGHTNESS = -0.3;
|
||||
const DIM_TIME = 0.500;
|
||||
const UNDIM_TIME = 0.250;
|
||||
|
||||
const DISPLAY_REVERT_TIMEOUT = 20; // in seconds - keep in sync with mutter
|
||||
const ONE_SECOND = 1000; // in ms
|
||||
|
||||
const DisplayChangeDialog = new Lang.Class({
|
||||
Name: 'DisplayChangeDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(wm) {
|
||||
this.parent({ styleClass: 'prompt-dialog' });
|
||||
|
||||
this._wm = wm;
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'preferences-desktop-display-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: 'prompt-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(messageBox,
|
||||
{ expand: true, y_align: St.Align.START });
|
||||
|
||||
let subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
|
||||
text: _("Do you want to keep these display settings?") });
|
||||
messageBox.add(subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._countDown = DISPLAY_REVERT_TIMEOUT;
|
||||
let message = this._formatCountDown();
|
||||
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||
text: this._formatCountDown() });
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageBox.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
/* Translators: this and the following message should be limited in lenght,
|
||||
to avoid ellipsizing the labels.
|
||||
*/
|
||||
this._cancelButton = this.addButton({ label: _("Revert Settings"),
|
||||
action: Lang.bind(this, this._onFailure),
|
||||
key: Clutter.Escape },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||
this._okButton = this.addButton({ label: _("Keep Changes"),
|
||||
action: Lang.bind(this, this._onSuccess),
|
||||
default: true },
|
||||
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(ONE_SECOND, Lang.bind(this, this._tick));
|
||||
},
|
||||
|
||||
close: function(timestamp) {
|
||||
if (this._timeoutId > 0) {
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
}
|
||||
|
||||
this.parent(timestamp);
|
||||
},
|
||||
|
||||
_formatCountDown: function() {
|
||||
let fmt = ngettext("Settings changes will revert in %d second",
|
||||
"Settings changes will revert in %d seconds");
|
||||
return fmt.format(this._countDown);
|
||||
},
|
||||
|
||||
_tick: function() {
|
||||
this._countDown--;
|
||||
|
||||
if (this._countDown == 0) {
|
||||
/* mutter already takes care of failing at timeout */
|
||||
this._timeoutId = 0;
|
||||
this.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
this._descriptionLabel.text = this._formatCountDown();
|
||||
return true;
|
||||
},
|
||||
|
||||
_onFailure: function() {
|
||||
this._wm.complete_display_change(false);
|
||||
this.close();
|
||||
},
|
||||
|
||||
_onSuccess: function() {
|
||||
this._wm.complete_display_change(true);
|
||||
this.close();
|
||||
},
|
||||
});
|
||||
|
||||
const WindowDimmer = new Lang.Class({
|
||||
Name: 'WindowDimmer',
|
||||
@ -147,9 +250,6 @@ const WorkspaceTracker = new Lang.Class({
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
|
||||
activeWorkspaceIndex < emptyWorkspaces.length - 1);
|
||||
// Don't enter the overview when removing multiple empty workspaces at startup
|
||||
let showOverview = (removingCurrentWorkspace &&
|
||||
!emptyWorkspaces.every(function(x) { return x; }));
|
||||
|
||||
if (removingCurrentWorkspace) {
|
||||
// "Merge" the empty workspace we are removing with the one at the end
|
||||
@ -165,9 +265,6 @@ const WorkspaceTracker = new Lang.Class({
|
||||
if (removingCurrentWorkspace) {
|
||||
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
||||
this._wm.unblockAnimations();
|
||||
|
||||
if (!Main.overview.visible && showOverview)
|
||||
Main.overview.show();
|
||||
}
|
||||
|
||||
this._checkWorkspacesId = 0;
|
||||
@ -306,6 +403,7 @@ const WindowManager = new Lang.Class({
|
||||
this._shellwm.connect('map', Lang.bind(this, this._mapWindow));
|
||||
this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow));
|
||||
this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding));
|
||||
this._shellwm.connect('confirm-display-change', Lang.bind(this, this._confirmDisplayChange));
|
||||
|
||||
this._workspaceSwitcherPopup = null;
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-left',
|
||||
@ -1056,4 +1154,9 @@ const WindowManager = new Lang.Class({
|
||||
workspace.activate_with_focus (window, global.get_current_time());
|
||||
}
|
||||
},
|
||||
|
||||
_confirmDisplayChange: function() {
|
||||
let dialog = new DisplayChangeDialog(this._shellwm);
|
||||
dialog.open();
|
||||
},
|
||||
});
|
||||
|
@ -20,8 +20,6 @@ const FOCUS_ANIMATION_TIME = 0.15;
|
||||
|
||||
const WINDOW_DND_SIZE = 256;
|
||||
|
||||
const SCROLL_SCALE_AMOUNT = 100 / 5;
|
||||
|
||||
const WINDOW_CLONE_MAXIMUM_SCALE = 0.7;
|
||||
|
||||
const LIGHTBOX_FADE_TIME = 0.1;
|
||||
@ -345,19 +343,6 @@ const WindowOverlay = new Lang.Class({
|
||||
this._animateVisible();
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
if (!this._hidden)
|
||||
return;
|
||||
|
||||
this.show();
|
||||
this.title.opacity = 0;
|
||||
this._parentActor.raise_top();
|
||||
Tweener.addTween(this.title,
|
||||
{ opacity: 255,
|
||||
time: CLOSE_BUTTON_FADE_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
chromeHeights: function () {
|
||||
return [Math.max(this.borderSize, this.closeButton.height - this.closeButton._overlap),
|
||||
this.title.height + this.title._spacing];
|
||||
@ -374,7 +359,6 @@ const WindowOverlay = new Lang.Class({
|
||||
let border = this.border;
|
||||
|
||||
Tweener.removeTweens(button);
|
||||
Tweener.removeTweens(title);
|
||||
Tweener.removeTweens(border);
|
||||
|
||||
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||
@ -876,7 +860,6 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
||||
row.windows.push(window);
|
||||
row.fullWidth += width;
|
||||
} else {
|
||||
this._sortRow(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -886,6 +869,8 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
||||
let maxRow;
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let row = rows[i];
|
||||
this._sortRow(row);
|
||||
|
||||
if (!maxRow || row.fullWidth > maxRow.fullWidth)
|
||||
maxRow = row;
|
||||
gridHeight += row.fullHeight;
|
||||
@ -1141,8 +1126,9 @@ const Workspace = new Lang.Class({
|
||||
Tweener.removeTweens(clone.actor);
|
||||
clone.actor.set_position(x, y);
|
||||
clone.actor.set_scale(scale, scale);
|
||||
clone.actor.set_opacity(255);
|
||||
clone.overlay.relayout(false);
|
||||
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
|
||||
this._showWindowOverlay(clone, overlay);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1172,23 +1158,19 @@ const Workspace = new Lang.Class({
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._showWindowOverlay(clone, overlay, true);
|
||||
this._showWindowOverlay(clone, overlay);
|
||||
})
|
||||
});
|
||||
|
||||
clone.overlay.relayout(true);
|
||||
},
|
||||
|
||||
_showWindowOverlay: function(clone, overlay, fade) {
|
||||
_showWindowOverlay: function(clone, overlay) {
|
||||
if (clone.inDrag)
|
||||
return;
|
||||
|
||||
if (overlay) {
|
||||
if (fade)
|
||||
overlay.fadeIn();
|
||||
else
|
||||
if (overlay && overlay._hidden)
|
||||
overlay.show();
|
||||
}
|
||||
},
|
||||
|
||||
_delayedWindowRepositioning: function() {
|
||||
@ -1432,6 +1414,10 @@ const Workspace = new Lang.Class({
|
||||
|
||||
if (this._positionWindowsId > 0)
|
||||
Meta.later_remove(this._positionWindowsId);
|
||||
|
||||
if (this._actualGeometryLater > 0)
|
||||
Meta.later_remove(this._actualGeometryLater);
|
||||
|
||||
this._windows = [];
|
||||
},
|
||||
|
||||
@ -1635,9 +1621,7 @@ const Workspace = new Lang.Class({
|
||||
metaWindow.move_to_monitor(this.monitorIndex);
|
||||
|
||||
let index = this.metaWorkspace ? this.metaWorkspace.index() : global.screen.get_active_workspace_index();
|
||||
metaWindow.change_workspace_by_index(index,
|
||||
false, // don't create workspace
|
||||
time);
|
||||
metaWindow.change_workspace_by_index(index, false);
|
||||
return true;
|
||||
} else if (source.shellWorkspaceLaunch) {
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
|
||||
|