Compare commits
519 Commits
Author | SHA1 | Date | |
---|---|---|---|
e304854fa5 | |||
d7f54213f1 | |||
12a71be076 | |||
855f0dbcad | |||
03db0d630e | |||
5a35c57866 | |||
d11027e8c8 | |||
aa733b5e53 | |||
355ec9ceca | |||
aa30c6a323 | |||
ad5572f275 | |||
9082b4df48 | |||
c3179583c3 | |||
4db87839a6 | |||
b018d49dc3 | |||
83362226cf | |||
1e3796967e | |||
8d7fa54af4 | |||
59cd9b4220 | |||
59dfb5866d | |||
a29ceaa8ec | |||
782e11b96c | |||
fd3be5b7de | |||
0b9a5c3441 | |||
7431c4d611 | |||
ec78dd60fc | |||
b479eec758 | |||
413ace2eb1 | |||
d8390ef77f | |||
e7e56e175a | |||
e875b9cdca | |||
66197b18b6 | |||
ed991f79b1 | |||
48b70f358d | |||
56adbda2dc | |||
7dbc78c95f | |||
3ffa1e35e8 | |||
114fb4219b | |||
4d7d66bc1f | |||
e134d2e5a5 | |||
cca84761a5 | |||
c2a5d6f61b | |||
4449007bb4 | |||
30cd1e84bc | |||
dcf872b485 | |||
c9d51ca775 | |||
96c9f8058b | |||
a3f4bca14e | |||
e55f2dd3b9 | |||
247566ca1d | |||
aa120e0902 | |||
5c7992beef | |||
6a615f30dc | |||
bfea41b771 | |||
a2465e0670 | |||
57044ea9a0 | |||
1392be2952 | |||
9e503aa69c | |||
a004389f25 | |||
d474261912 | |||
144e959cab | |||
626488e659 | |||
5bae5574f0 | |||
ce14d6d9db | |||
519addf6ac | |||
fd87468e36 | |||
75e49610cb | |||
414fe75d02 | |||
26031eb761 | |||
be801dff5f | |||
5fd47ed742 | |||
f7f2f50435 | |||
e0bb15e572 | |||
0e4171a87c | |||
34cb92ff4c | |||
22eea750f3 | |||
c3afe1a83a | |||
904ceba6b2 | |||
a28d639c3b | |||
c0652bcd8f | |||
54dc0fd123 | |||
c22a00afee | |||
2c3ec7846f | |||
46db9edacc | |||
4e6fa56c87 | |||
8970e17911 | |||
ac6c808124 | |||
26d3b1929e | |||
a29507e452 | |||
2c073fb005 | |||
d9c3b83d1e | |||
f5e58c500f | |||
5e865f5bc4 | |||
01a1255967 | |||
dd80f39049 | |||
19e4c953ef | |||
ef218c95fc | |||
eec9dba855 | |||
f46a165886 | |||
ec47bd1604 | |||
bfd4016c1c | |||
63cdb1848e | |||
10a0762fe7 | |||
76472b86ed | |||
edb96cde70 | |||
e06ecb8f0c | |||
2791d948e9 | |||
360e6e790a | |||
d0807c8276 | |||
75d0362cd8 | |||
4f7c554d8d | |||
8b81f23caf | |||
0098c2b7f7 | |||
f0e03b5e82 | |||
be2f1001a5 | |||
cf08b4d56a | |||
04074f883f | |||
14d3235f1a | |||
e00c1cbd20 | |||
3df3f0d9dc | |||
985db40547 | |||
c9fa0fdff0 | |||
fe69ea305b | |||
ff9088e42b | |||
f556cdf0ca | |||
c671ff74c6 | |||
04570ac783 | |||
6ab25cd791 | |||
a04350f7ce | |||
b7018de7e0 | |||
d212d57466 | |||
c4e7d8ed8c | |||
464813ecbb | |||
9812771dcd | |||
6f605598de | |||
85bc8ccccc | |||
e756c2dbce | |||
e82fe14f00 | |||
de65739c01 | |||
e1ec89a133 | |||
bdb3410d9d | |||
168e0b5a42 | |||
f906cfe5f6 | |||
9faac81a37 | |||
b90e7eb95c | |||
1e286e43ad | |||
539993b4f4 | |||
1363d30f79 | |||
3a48daaa64 | |||
d2b4a65e65 | |||
1ead290c23 | |||
6658660355 | |||
85ab019987 | |||
460cda2aa1 | |||
2d913578e1 | |||
34831796f6 | |||
8754b2767c | |||
04fb688f7d | |||
58dbd285fc | |||
cf6f149888 | |||
8d017ceaf1 | |||
02428019fa | |||
b4464929cb | |||
3ea22f8b0e | |||
9745e97e14 | |||
6c6e182ecc | |||
7973dd45b7 | |||
a1837dde68 | |||
4448b65a18 | |||
2231c23c4d | |||
f17fc43d6e | |||
f9dbe56785 | |||
8845a2170c | |||
a4b1ebd8c3 | |||
66adeef9bd | |||
20769f68a7 | |||
e92719b98d | |||
59246babea | |||
23e86d7dd5 | |||
c99e8eb29d | |||
7949397958 | |||
1d1359b58f | |||
8915bb4892 | |||
6a117ac12f | |||
67689f1a6d | |||
48b83f1ffd | |||
7da0f398a5 | |||
7e277fdd4a | |||
5d10d8566b | |||
a10295f584 | |||
46cf9faa11 | |||
971341bb53 | |||
2b2a235a49 | |||
970b9deeaa | |||
fd256b624c | |||
698fb64be9 | |||
9561f77b17 | |||
c3d3d346d4 | |||
e43fe98263 | |||
04dbf15d9b | |||
de72065a4a | |||
a1bb0ec738 | |||
1a33cd9584 | |||
00279dbd04 | |||
eb759cf22f | |||
ae16da4e81 | |||
965287e724 | |||
3d5312e8d2 | |||
bc91b7dcae | |||
4d77eb94ff | |||
1b8d03f945 | |||
de2dcfeb99 | |||
c7196a519f | |||
0d82ce5210 | |||
3ce9ad05b3 | |||
ab75faac74 | |||
9e25e13218 | |||
69e1503c6d | |||
7eaf231e56 | |||
a347a72091 | |||
560daec913 | |||
d9e968d863 | |||
556d5d181e | |||
4e4092f9e8 | |||
fd62aba71c | |||
ef0aa65774 | |||
6b5f9a647a | |||
01c07fbea1 | |||
81929c2a92 | |||
d9ff8e3122 | |||
0c4692ae58 | |||
df56ff4f09 | |||
96cdc9c4eb | |||
3b4ad5cd7d | |||
317c6b77f3 | |||
e5f5a2adaa | |||
594c3174ab | |||
5b7e4bb4a7 | |||
f7c0f826d4 | |||
66af7de6d6 | |||
d1815a36d0 | |||
61de3de909 | |||
36888a34d6 | |||
646435ee3e | |||
cbb8d5b0dc | |||
ebee01b355 | |||
0e3795b2f3 | |||
de73128d47 | |||
281b0a3e63 | |||
c414f9ac9d | |||
77242cfec0 | |||
8ebbba6eb8 | |||
0804cefbee | |||
2404d2935d | |||
e6f5e21b5d | |||
c303c6b5c1 | |||
f58e8f2a35 | |||
ededba0c6d | |||
e112fa92fe | |||
7524210d1f | |||
201dc05416 | |||
2b34978993 | |||
447246da74 | |||
1cf2bb6646 | |||
de1eafb564 | |||
22f0099a5d | |||
300eb87016 | |||
aa38b16368 | |||
266bfdf739 | |||
19ef6b0421 | |||
75570b7995 | |||
3290bfae68 | |||
0805d7a35f | |||
11278a0814 | |||
86de6f5861 | |||
498b023989 | |||
5265884af9 | |||
cdbe0bbf38 | |||
feef35a8ca | |||
3ff51da529 | |||
496e9f7b16 | |||
0b30dc29a5 | |||
15b4d29e70 | |||
9eee4b7687 | |||
196f6c241a | |||
c9296191a8 | |||
c7b4022283 | |||
a7ecc4cdd6 | |||
df5298d59c | |||
b79d17332e | |||
675572a41a | |||
0078fe9349 | |||
dc004f6eb7 | |||
07f1a05ab4 | |||
54292a99af | |||
f5933c8cb8 | |||
6700f86145 | |||
b31d22488e | |||
6382eeb8fd | |||
b07345a55c | |||
06febdce22 | |||
c31d9d5e3d | |||
cda8a545f1 | |||
f850e92524 | |||
cc9d53e038 | |||
7b048fc092 | |||
4ba4a501fd | |||
2b2cce6896 | |||
c1f51a7bf3 | |||
465556f0d8 | |||
db7ac5208a | |||
cfcd1bc014 | |||
e63f7e8779 | |||
7911154bad | |||
ec0730f3e5 | |||
76005f5adf | |||
94493cde35 | |||
a1f68720e5 | |||
022376dd56 | |||
934e5aacab | |||
35b142f23f | |||
41c3795a7b | |||
95d7099133 | |||
33dc9abb9b | |||
5e4edac14d | |||
cb5ae92986 | |||
ebbd295ebe | |||
65d23fb9a3 | |||
ad6d986172 | |||
985641cc2e | |||
de0a714081 | |||
3f942302d1 | |||
96396163cf | |||
0c736c4561 | |||
33ad9d1035 | |||
d57658d059 | |||
031206cf1f | |||
12c7cc278d | |||
9d3750b9b8 | |||
ba4b9f229e | |||
850fe98cbb | |||
8a9e3e0df2 | |||
a277569d31 | |||
7ed9516884 | |||
ecff2fa2b7 | |||
e49b94658c | |||
e4f1572a3a | |||
b5b13322d8 | |||
c25e7f3c41 | |||
f6a2c92bfa | |||
ed17418101 | |||
5264f39209 | |||
de69c719fb | |||
ab3173487d | |||
a3fcb8c284 | |||
ba92cfa064 | |||
6bee51ed33 | |||
122bca49ea | |||
19318a1eeb | |||
a7a46bbe1c | |||
3d26224180 | |||
940ddb104c | |||
5617f91281 | |||
a9a863aab4 | |||
ca26347dea | |||
41a14e808e | |||
6452501275 | |||
b61ada72cc | |||
ace42d845c | |||
850b6f28e5 | |||
3c81e9f0e7 | |||
e20ea19f34 | |||
ce041a3190 | |||
3a01aaf7fb | |||
ec4a2aae95 | |||
de8a66d4ce | |||
dc3d3acb3b | |||
48d6eb168f | |||
6190d6c962 | |||
76616dc98c | |||
82c2f5221d | |||
a3bbb7be14 | |||
9e1a2cfeac | |||
c6fabe504a | |||
61a17d7fab | |||
865cfa5211 | |||
e2b857adae | |||
f3924ccd91 | |||
86e3e59530 | |||
02e4726ba6 | |||
66e470e073 | |||
afaa5c24d6 | |||
518282e169 | |||
d955adbbad | |||
83d3225e57 | |||
6fa45975bf | |||
e6087efb40 | |||
4ce2f80a2f | |||
0862d1c804 | |||
333e380340 | |||
58f77a19ed | |||
f2d883dab2 | |||
7ba8c7c2b5 | |||
0bf6c93faa | |||
b9f0158278 | |||
25ee41f344 | |||
5436634829 | |||
915524e1ab | |||
d579cd1605 | |||
cb5941ec55 | |||
a5ac183d86 | |||
a36de92bb9 | |||
01f9d551f1 | |||
399df66b18 | |||
723a1c843a | |||
e333263fd6 | |||
507df9eea1 | |||
a9a3687ea0 | |||
f05c649c61 | |||
a2dfba1842 | |||
c199da4dfa | |||
ee9033e12f | |||
54788d750e | |||
78e894c6f2 | |||
32107ba8b5 | |||
6122f65e7a | |||
43fd29f9bf | |||
54c624b356 | |||
8c33adfd29 | |||
2e8881b77c | |||
64aa729edd | |||
ccf95b738d | |||
b2847fedd3 | |||
8befcb9bba | |||
988fc52303 | |||
7293ddb22c | |||
f23c118e81 | |||
f68b3be35a | |||
3d95e7bb11 | |||
337c484f01 | |||
5d98e2bf04 | |||
6f300d0cc6 | |||
3422e1dca7 | |||
963c6ae567 | |||
6304169926 | |||
221afde55e | |||
0ae87270ad | |||
9d33baec70 | |||
0f37b22cdb | |||
47afd87e84 | |||
700c06023e | |||
4fea5b5ca3 | |||
521bddc1cc | |||
bdfff20ec2 | |||
bdd05aba3b | |||
89c2538ff1 | |||
7680819108 | |||
d79e8b84c9 | |||
731317230a | |||
5046938913 | |||
0e8fd45559 | |||
6099a5dbc3 | |||
2daa98a694 | |||
5d2c6496fa | |||
817dbbe73f | |||
29c89c82f8 | |||
0b714bd479 | |||
8c94a5afb9 | |||
aeb117c9d1 | |||
a2d4f133b7 | |||
b833aff3c8 | |||
6601e4ddba | |||
815da2d0ec | |||
ddd35b3653 | |||
8a32894c83 | |||
49d8e6da40 | |||
8089f24c81 | |||
b6aab53d10 | |||
55a4517cd1 | |||
b095319a16 | |||
5ea5806730 | |||
bfbf812148 | |||
168e9eeac1 | |||
dd79c1a79a | |||
fe3402589b | |||
74dcaff21c | |||
0a7968a2e5 | |||
00091a2acb | |||
c5aa834b6a | |||
b1bde46694 | |||
49e4fa494e | |||
4622c52b71 | |||
e9ac5dd5f4 | |||
7570c43d11 | |||
4332e7ec49 | |||
9f26f1e225 | |||
0c0319c415 | |||
c16dbd7607 | |||
7b1f10a5fe | |||
dce74749b7 | |||
1ba88b8c42 | |||
9c50b57d46 | |||
900bd3ee97 | |||
b4affe00a7 | |||
ca49c84bc1 | |||
2cddf60226 | |||
d9c3951f02 | |||
aa5997d975 | |||
c51acf7c2a | |||
348044bc8a | |||
7dbdf2aa07 | |||
c933731ead | |||
e7da715994 | |||
6d82aefad4 | |||
8a11ab7d96 | |||
f465086405 | |||
70313a8b79 | |||
71679c38be | |||
addd943074 |
6
.gitignore
vendored
@ -23,6 +23,8 @@ data/gnome-shell-extension-prefs.desktop.in
|
||||
data/gschemas.compiled
|
||||
data/org.gnome.shell.gschema.xml
|
||||
data/org.gnome.shell.gschema.valid
|
||||
data/org.gnome.shell.evolution.calendar.gschema.xml
|
||||
data/org.gnome.shell.evolution.calendar.gschema.valid
|
||||
docs/reference/*/*.args
|
||||
docs/reference/*/*.bak
|
||||
docs/reference/*/*.hierarchy
|
||||
@ -48,6 +50,7 @@ po/gnome-shell.pot
|
||||
po/*.header
|
||||
po/*.sed
|
||||
po/*.sin
|
||||
po/.intltool-merge-cache
|
||||
po/Makefile.in.in
|
||||
po/Makevars.template
|
||||
po/POTFILES
|
||||
@ -60,6 +63,8 @@ src/*-enum-types.[ch]
|
||||
src/*-marshal.[ch]
|
||||
src/Makefile
|
||||
src/Makefile.in
|
||||
src/calendar-server/evolution-calendar.desktop
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||
src/gnome-shell
|
||||
src/gnome-shell-calendar-server
|
||||
@ -68,6 +73,7 @@ src/gnome-shell-extension-prefs
|
||||
src/gnome-shell-hotplug-sniffer
|
||||
src/gnome-shell-jhbuild
|
||||
src/gnome-shell-perf-helper
|
||||
src/gnome-shell-perf-tool
|
||||
src/gnome-shell-real
|
||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||
src/run-js-test
|
||||
|
263
NEWS
@ -1,3 +1,254 @@
|
||||
3.5.5
|
||||
=====
|
||||
* Update style to match mockups [Allan]
|
||||
- improve calendar layout and legibility
|
||||
- update notifications and menus
|
||||
- use a common style for entries
|
||||
- update scrollbars to match GTK+
|
||||
- improve clock/unlock button in lock screen
|
||||
- update polkit dialogs [Jasper]
|
||||
* Fix login dialog growing when selecting different users [Florian; #675076]
|
||||
* Implement screen lock in the shell [Giovanni]
|
||||
- restructure login code to be shared with session unlock [#619955]
|
||||
- add initial screen shield / unlock dialog implementation [#619955]
|
||||
- implement (optional) notification list on lock shield [#619955]
|
||||
- update login dialog style to match lock screen [#619955]
|
||||
- filter notifications to only show new ones on the screen lock [#681143]
|
||||
- make notifications scrollable if necessary [#681143]
|
||||
- use correct application names in notifications [#681143]
|
||||
- allow to return to the shield by pressing Escape [#681143]
|
||||
* Minor login dialog improvements [Florian]
|
||||
- update style to match the overall visuals [#660913]
|
||||
- indicate whether users are logged in [#658185]
|
||||
* Add support for background-repeat CSS property [Jasper; #680801]
|
||||
* Add :active pseudo class on scroll handles [Florian]
|
||||
* Remove markup from translated strings [Matthias; #681270]
|
||||
* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
|
||||
Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
|
||||
#681101, #681382]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||
Piotr Drąg , Florian Müllner, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
|
||||
Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
|
||||
Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
|
||||
Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
|
||||
Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
|
||||
|
||||
3.5.4
|
||||
=====
|
||||
* Fix wrong result handling of remote calls [Florian; #678852]
|
||||
* dateMenu: Fix regression that caused no date to be displayed [Colin]
|
||||
* WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992]
|
||||
* Show the workspace switcher for move-to-workspace keybinding
|
||||
[Giovanni, Jasper; #674104, #660839, #679005]
|
||||
* userMenu: Move "Power off" item to the bottom [Florian; #678887]
|
||||
* Remove contacts search provider [Florian, Rui; #677442]
|
||||
* network: don't ask for always-ask secrets when interaction isn't allowed
|
||||
[Dan; #679091]
|
||||
* PolkitAgent: Look for the right password prompt [Matthias; #675300]
|
||||
* Implement extension updates [Jasper; #679099]
|
||||
* userMenu: Don't disconnect account signals when disabled [Guillaume; #669112]
|
||||
* Fix startup notification when opening calendar [Florian; #677907]
|
||||
* networkAgent: use absolute path if configured [Clemens; #679212]
|
||||
* recorder: Port to GStreamer-1.0 API [Florian; #679445]
|
||||
* telepathyClient: don't add log messages on presence changes [Ana; #669508]
|
||||
* lookingGlass: Don't use a signal callback on 'paint' to draw the border
|
||||
[Jasper; #679464]
|
||||
* Add support for inhibiting automount [Hans; #678597]
|
||||
* Implemented banner support for the login screen [Matthias, Marius; #665346]
|
||||
* boxpointer: Flip side if we would end outside the monitor [Rui; #678164]
|
||||
* boxpointer: Change 'animate' parameter on show/hide to a bitmask
|
||||
[Rui; #678337]
|
||||
* Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499]
|
||||
* UserMenu: show "Install Updates & Restart" when appropriate
|
||||
[Giovanni; #677394, #680080]
|
||||
* messageTray: don't show the message tray when a new notification is shown
|
||||
[Ana; #677210]
|
||||
* panel: don't break when indicator has no menu [Jean-Philippe; #678694]
|
||||
* appMenu: Disable app menu during startup animations [Florian; #672322]
|
||||
* autorun: Add a notification when unmounting drives [Cosimo; #676125]
|
||||
* st-icon: Fix potential crash involving shadows [Jasper; #679776]
|
||||
* Remove manual garbage collection on tweeners end [Cosimo; #679832]
|
||||
* dash: hide tooltips when overview begins hiding [Stefano; #674241]
|
||||
* Update modal dialog animation for new centered position [Florian; #674499]
|
||||
* calendar: Fix grid lines in RTL locales [Florian; #679879]
|
||||
* Integrate IBus with keyboard indicator [Rui; #641531]
|
||||
* Move ibus status icon under keyboard [Matthias]
|
||||
* gdm: port from libgdmgreeter to libgdm [Ray; #676401]
|
||||
* Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico;
|
||||
#678978, #672790, #679847, #679944]
|
||||
|
||||
Contributors:
|
||||
Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi,
|
||||
Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini,
|
||||
Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska,
|
||||
Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi],
|
||||
Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg],
|
||||
Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he],
|
||||
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be],
|
||||
Praveen Illa [te]
|
||||
|
||||
3.5.3
|
||||
=====
|
||||
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
|
||||
* messageTray: Don't show non urgent notifications while in fullscreen
|
||||
[Adel; #677590]
|
||||
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
|
||||
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
|
||||
* appDisplay: Don't show apps in NoDisplay categories in the All view
|
||||
[Jasper; #658176]
|
||||
* st: Trigger theme updates on resolution changes [Florian; #677975]
|
||||
* Always enable a11y [Bastien; #678095]
|
||||
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
|
||||
* shell-app: Update app menu if necessary [Florian; #676238]
|
||||
* Enable the Screen Reader menu item [Matthias; #663256]
|
||||
* Disable unredirection when a modal operation is active [Giovanni]
|
||||
* Make folks optional [Colin]
|
||||
* Improve mount-operation support [Cosimo]
|
||||
- Fix exception when showing password entry [#678428]
|
||||
- Close the password entry on operation abort [#673787]
|
||||
- autorun: Don't allow autorun for things we mount on startup [#660595]
|
||||
- Turn passphrase prompt into a dialog [#674962]
|
||||
- Implement org.Gtk.MountOperationHandler [#678516]
|
||||
* Network menu improvements
|
||||
- Sort Wifi networks by strength [Giovanni; #658946]
|
||||
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
|
||||
* clock: Switch to using GnomeWallClock [Colin; #657074]
|
||||
* remoteSearch: Allow to reference .desktop file for Title/Icon
|
||||
[Florian; #678816]
|
||||
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
|
||||
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
|
||||
#658955, #677497, #678396, #678502]
|
||||
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
|
||||
|
||||
Contributors:
|
||||
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
|
||||
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
|
||||
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
|
||||
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
|
||||
|
||||
3.5.2
|
||||
=====
|
||||
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
|
||||
* popupMenu: make sure to break the grab when the slider is not visible
|
||||
[Stefano; #672713]
|
||||
* st-theme-node-drawing: Don't use GL types [Neil; #672711]
|
||||
* Mirror Evolution calendar settings into our own schema [Owen; #674424]
|
||||
* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
|
||||
* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
|
||||
* NetworkMenu: use network-offline while loading [Giovanni; #674426]
|
||||
* lookingGlass: Remove the Errors tab [Jasper; #675104]
|
||||
* searchDisplay: Reset keyboard focus after displaying async results
|
||||
[Rui; #675078]
|
||||
* gdm: don't fail if fprintd is unavailable [Ray; #675006]
|
||||
* messageTray: Fix scrolling up [Jasper; #661615]
|
||||
* main: Close the recorder instead of pausing it [Rui; #675128]
|
||||
* Accessibility [Alejandro]
|
||||
- Use the proper label_actor for date menu on top panel [#675307]
|
||||
- Set the proper role/label_actor for SearchResult.content [#672242]
|
||||
- do not expose a label text if 'hidden' style class is used [#675341]
|
||||
* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
|
||||
* theme: use a smaller border-radius for top bar [Jakub; #672430]
|
||||
* placeDisplay: use new bookmark file location [Matthias; #675443]
|
||||
* port all synchronous search providers to the async API [Jasper, Rui; #675328]
|
||||
* NetworkAgent: disallow multiple requests for the same connection/setting
|
||||
[Giovanni; #674961]
|
||||
* userMenu: Update to latest mockups [Florian; #675802]
|
||||
* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
|
||||
* messageTray: Make Source usable without subclassing [Jasper; #661236]
|
||||
* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
|
||||
* Fix formatting of bluetooth passkey [Florian; #651251]
|
||||
* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
|
||||
* Don't use global.log() [Jasper; #675790]
|
||||
* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
|
||||
* shell-app: Raise windows in reverse order to preserve the stacking
|
||||
[Rui; #676371]
|
||||
* Generalize gdm-mode [Florian; #676156]
|
||||
* Switch string formatting to the one inside gjs [Jasper; #675479]
|
||||
* extensionUtils: Support subdirectories in getCurrentExtension
|
||||
[Jasper; #677001]
|
||||
* panel: Refuse to add (legacy) status icons not part of the session mode
|
||||
[Florian; #677058]
|
||||
* Add an initial-setup mode [Matthias; #676697]
|
||||
* status/keyboard: Port to the new input sources settings [Rui; #641531]
|
||||
* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
|
||||
* userMenu: Indicate progress on status changes [Florian; #659067]
|
||||
* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
|
||||
* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
|
||||
* endSessionDialog: Factor out _updateDescription from _updateContent
|
||||
[Alejandro; #674210]
|
||||
* Fix empathy's appMenu freezing the shell [Alban; #676447]
|
||||
* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
|
||||
#672272]
|
||||
* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
|
||||
Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
|
||||
#675370, #676347, #676806, #677097]
|
||||
|
||||
Contributors:
|
||||
Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
|
||||
Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
|
||||
Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
|
||||
Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
|
||||
Colin Walters, Dan Winship, Ron Yorston
|
||||
|
||||
Translations:
|
||||
OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
|
||||
Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
|
||||
Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
|
||||
Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
|
||||
Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
|
||||
|
||||
3.4.1
|
||||
=====
|
||||
* Fix crash that occurred when an icon theme change caused unexpected
|
||||
reentrancy in the icon loading code [Jasper; #673512]
|
||||
* Don't show system and other disabled users in the GDM user list
|
||||
[Adel; #673784]
|
||||
* Make gnome-shell-calendar-server initialize GTK+ so it can display
|
||||
password prompts if needed [#673608; Owen, Rico]
|
||||
* Adapt to Mutter API change for keybinding addition [Florian; #673014]
|
||||
* Fix crash when an extension was installed as both a user extension
|
||||
and a system extension [#673613; Jasper]
|
||||
* Fix bug where chat entry could end up partially offscreen [Joost, 661944]
|
||||
* Fix problem where icons weren't updating when theme was changed
|
||||
[#672941; Florian]
|
||||
* Look for Evolution calendar settings in GSettings, not GConf [#673610; Owen]
|
||||
* Add <super>F10 for the application menu [#672909; Florian]
|
||||
* Fix %Id format characters to work in translations [#673106; Cosimo]
|
||||
(were already used in fa translation)
|
||||
* Fix error when NetworkManager restarts [#673043; Giovanni]
|
||||
* Improve efficiency of overview redraws by working around Clutter issue
|
||||
[Stefano; #670636]
|
||||
* Misc bug fixes [Florian, Giovanni, Jasper, Rui, Stefano;
|
||||
#672592, #672641, #672719, #673187, #673233, #673656]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, Adel Gadllah, Rui Matos,
|
||||
Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz,
|
||||
Joost Verdoorn
|
||||
|
||||
Translations:
|
||||
Khaled Hosny [ar], Ihar Hrachyshka [be], Alexander Shopov [bg], Gil Forcada,
|
||||
Jordi Serratosa [ca], Petr Kovar [cs], Bruce Cowan [en_GB],
|
||||
Carles Ferrando [ca@valencia], Wolfgang Stöggl [de], Daniel Mustieles [es],
|
||||
Arash Mousavi [fa], Bruno Brouard [fr], Fran Diéguez [gl],
|
||||
Sweta Kothari [gu], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
||||
Shankar Prasad [kn], Žygimantas Beručka [lt], Rudolfs Mazurs [lv],
|
||||
Sandeep Sheshrao Shedmake [mr], Kjartan Maraas [nb], Piotr Drąg [pl],
|
||||
Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl],
|
||||
Miroslav Nikolić [sr], Sasi Bhushan, Praveen Illa [te], Yinghua Wang [zh_CN]
|
||||
|
||||
3.4.0
|
||||
=====
|
||||
* Don't crash when taking screenshots [Jasper; #672775]
|
||||
@ -8,10 +259,10 @@ Contributors:
|
||||
|
||||
Translations:
|
||||
Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
|
||||
Alexander Shopov [bg], Marek Černocký [cz], Jiri Grönroos, Timo Jyrinki [fi],
|
||||
Alexander Shopov [bg], Marek Černocký [cs], Jiri Grönroos, Timo Jyrinki [fi],
|
||||
Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
|
||||
Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
|
||||
Mattias Põldaru [et], Changwoo Ryu [kr], Rudolfs Mazurs [lv],
|
||||
Mattias Põldaru [et], Changwoo Ryu [ko], Rudolfs Mazurs [lv],
|
||||
Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
|
||||
Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
@ -80,7 +331,7 @@ Contributors:
|
||||
|
||||
Translations:
|
||||
Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
|
||||
Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cz],
|
||||
Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cs],
|
||||
Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
|
||||
Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
|
||||
Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
|
||||
@ -129,7 +380,7 @@ Contributors:
|
||||
Will Thompson, Stef Walter
|
||||
|
||||
Translations:
|
||||
Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cz],
|
||||
Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cs],
|
||||
Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
|
||||
Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
|
||||
@ -260,7 +511,7 @@ Contributors:
|
||||
Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Petr Kovar [cz], Kris Thomsen [dk], Daniel Mustieles [es],
|
||||
Petr Kovar [cs], Kris Thomsen [dk], Daniel Mustieles [es],
|
||||
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
|
||||
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
|
||||
@ -423,7 +674,7 @@ Contributors:
|
||||
Translations:
|
||||
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
|
||||
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
|
||||
Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
|
||||
Petr Kovar [cs], Mario Blättermann [de], Kris Thomsen [dk],
|
||||
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
|
||||
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
|
||||
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
|
||||
|
@ -41,7 +41,7 @@
|
||||
"It can be used only by extensions.gnome.org"
|
||||
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
|
||||
|
||||
#define PLUGIN_API_VERSION 3
|
||||
#define PLUGIN_API_VERSION 5
|
||||
|
||||
typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
@ -163,6 +163,7 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
||||
plugin->newp = NPP_New;
|
||||
plugin->destroy = NPP_Destroy;
|
||||
plugin->getvalue = NPP_GetValue;
|
||||
plugin->setwindow = NPP_SetWindow;
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
@ -224,7 +225,7 @@ NPP_New(NPMIMEType mimetype,
|
||||
NULL, /* interface info */
|
||||
"org.gnome.Shell",
|
||||
"/org/gnome/Shell",
|
||||
"org.gnome.Shell",
|
||||
"org.gnome.Shell.Extensions",
|
||||
NULL, /* GCancellable */
|
||||
&error);
|
||||
if (!data->proxy)
|
||||
@ -267,6 +268,7 @@ typedef struct {
|
||||
NPObject parent;
|
||||
NPP instance;
|
||||
GDBusProxy *proxy;
|
||||
GSettings *settings;
|
||||
NPObject *listener;
|
||||
NPObject *restart_listener;
|
||||
gint signal_id;
|
||||
@ -323,6 +325,9 @@ on_shell_appeared (GDBusConnection *connection,
|
||||
}
|
||||
}
|
||||
|
||||
#define SHELL_SCHEMA "org.gnome.shell"
|
||||
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
|
||||
|
||||
static NPObject *
|
||||
plugin_object_allocate (NPP instance,
|
||||
NPClass *klass)
|
||||
@ -332,6 +337,7 @@ plugin_object_allocate (NPP instance,
|
||||
|
||||
obj->instance = instance;
|
||||
obj->proxy = g_object_ref (data->proxy);
|
||||
obj->settings = g_settings_new (SHELL_SCHEMA);
|
||||
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
|
||||
G_CALLBACK (on_shell_signal), obj);
|
||||
|
||||
@ -367,39 +373,14 @@ plugin_object_deallocate (NPObject *npobj)
|
||||
g_slice_free (PluginObject, obj);
|
||||
}
|
||||
|
||||
static NPIdentifier api_version_id;
|
||||
static NPIdentifier shell_version_id;
|
||||
static NPIdentifier get_info_id;
|
||||
static NPIdentifier list_extensions_id;
|
||||
static NPIdentifier enable_extension_id;
|
||||
static NPIdentifier install_extension_id;
|
||||
static NPIdentifier uninstall_extension_id;
|
||||
static NPIdentifier onextension_changed_id;
|
||||
static NPIdentifier onrestart_id;
|
||||
static NPIdentifier get_errors_id;
|
||||
static NPIdentifier launch_extension_prefs_id;
|
||||
|
||||
static bool
|
||||
plugin_object_has_method (NPObject *npobj,
|
||||
NPIdentifier name)
|
||||
{
|
||||
return (name == get_info_id ||
|
||||
name == list_extensions_id ||
|
||||
name == enable_extension_id ||
|
||||
name == install_extension_id ||
|
||||
name == uninstall_extension_id ||
|
||||
name == get_errors_id ||
|
||||
name == launch_extension_prefs_id);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
uuid_is_valid (const gchar *uuid)
|
||||
uuid_is_valid (NPString string)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; uuid[i]; i ++)
|
||||
for (i = 0; i < string.UTF8Length; i++)
|
||||
{
|
||||
gchar c = uuid[i];
|
||||
gchar c = string.UTF8Characters[i];
|
||||
if (c < 32 || c >= 127)
|
||||
return FALSE;
|
||||
|
||||
@ -463,8 +444,73 @@ jsonify_variant (GVariant *variant,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_list_extensions (PluginObject *obj,
|
||||
NPVariant *result)
|
||||
parse_args (const gchar *format_str,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
gsize i;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (strlen (format_str) != argc)
|
||||
return FALSE;
|
||||
|
||||
va_start (args, argv);
|
||||
|
||||
for (i = 0; format_str[i]; i++)
|
||||
{
|
||||
gpointer arg_location;
|
||||
const NPVariant arg = argv[i];
|
||||
|
||||
arg_location = va_arg (args, gpointer);
|
||||
|
||||
switch (format_str[i])
|
||||
{
|
||||
case 'u':
|
||||
{
|
||||
NPString string;
|
||||
|
||||
if (!NPVARIANT_IS_STRING (arg))
|
||||
goto out;
|
||||
|
||||
string = NPVARIANT_TO_STRING (arg);
|
||||
|
||||
if (!uuid_is_valid (string))
|
||||
goto out;
|
||||
|
||||
*(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
if (!NPVARIANT_IS_BOOLEAN (arg))
|
||||
goto out;
|
||||
|
||||
*(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
if (!NPVARIANT_IS_OBJECT (arg))
|
||||
goto out;
|
||||
|
||||
*(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
va_end (args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_list_extensions (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *args,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
@ -488,91 +534,159 @@ plugin_list_extensions (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_enable_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
gboolean enabled)
|
||||
plugin_enable_extension (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
gboolean ret;
|
||||
gchar *uuid;
|
||||
gboolean enabled;
|
||||
gsize length;
|
||||
gchar **uuids;
|
||||
const gchar **new_uuids;
|
||||
|
||||
if (!parse_args ("ub", argc, argv, &uuid, &enabled))
|
||||
return FALSE;
|
||||
|
||||
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
|
||||
length = g_strv_length (uuids);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
|
||||
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
|
||||
new_uuids[length] = uuid;
|
||||
new_uuids[length + 1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize i = 0, j = 0;
|
||||
new_uuids = g_new (const gchar *, length);
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
if (g_str_equal (uuids[i], uuid))
|
||||
continue;
|
||||
|
||||
new_uuids[j] = uuids[i];
|
||||
j++;
|
||||
}
|
||||
|
||||
new_uuids[j] = NULL;
|
||||
}
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
(enabled ? "EnableExtension" : "DisableExtension"),
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
ret = g_settings_set_strv (obj->settings,
|
||||
ENABLED_EXTENSIONS_KEY,
|
||||
new_uuids);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_strfreev (uuids);
|
||||
g_free (new_uuids);
|
||||
g_free (uuid);
|
||||
|
||||
return TRUE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct _AsyncClosure AsyncClosure;
|
||||
|
||||
struct _AsyncClosure {
|
||||
PluginObject *obj;
|
||||
NPObject *callback;
|
||||
NPObject *errback;
|
||||
};
|
||||
|
||||
static void
|
||||
install_extension_cb (GObject *proxy,
|
||||
GAsyncResult *async_res,
|
||||
gpointer user_data)
|
||||
{
|
||||
AsyncClosure *async_closure = (AsyncClosure *) user_data;
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
NPVariant args[1];
|
||||
NPVariant result = { NPVariantType_Void };
|
||||
NPObject *callback;
|
||||
|
||||
res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
|
||||
|
||||
if (res == NULL)
|
||||
{
|
||||
if (g_dbus_error_is_remote_error (error))
|
||||
g_dbus_error_strip_remote_error (error);
|
||||
STRINGZ_TO_NPVARIANT (error->message, args[0]);
|
||||
callback = async_closure->errback;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *string_result;
|
||||
g_variant_get (res, "(&s)", &string_result);
|
||||
STRINGZ_TO_NPVARIANT (string_result, args[0]);
|
||||
callback = async_closure->callback;
|
||||
}
|
||||
|
||||
funcs.invokeDefault (async_closure->obj->instance,
|
||||
callback, args, 1, &result);
|
||||
|
||||
funcs.releasevariantvalue (&result);
|
||||
|
||||
funcs.releaseobject (async_closure->callback);
|
||||
funcs.releaseobject (async_closure->errback);
|
||||
g_slice_free (AsyncClosure, async_closure);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_install_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPString version_tag)
|
||||
plugin_install_extension (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
gchar *version_tag_str;
|
||||
gchar *uuid;
|
||||
NPObject *callback, *errback;
|
||||
AsyncClosure *async_closure;
|
||||
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
|
||||
return FALSE;
|
||||
|
||||
version_tag_str = g_strndup (version_tag.UTF8Characters,
|
||||
version_tag.UTF8Length);
|
||||
async_closure = g_slice_new (AsyncClosure);
|
||||
async_closure->obj = obj;
|
||||
async_closure->callback = funcs.retainobject (callback);
|
||||
async_closure->errback = funcs.retainobject (errback);
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
"InstallRemoteExtension",
|
||||
g_variant_new ("(ss)",
|
||||
uuid_str,
|
||||
version_tag_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
install_extension_cb,
|
||||
async_closure);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_free (version_tag_str);
|
||||
g_free (uuid);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_uninstall_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_uninstall_extension (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"UninstallExtension",
|
||||
g_variant_new ("(s)",
|
||||
uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_free (uuid);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
@ -585,30 +699,27 @@ plugin_uninstall_extension (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_get_info (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_get_info (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"GetExtensionInfo",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
g_free (uuid);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
@ -621,31 +732,26 @@ plugin_get_info (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_get_errors (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_get_errors (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"GetExtensionErrors",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
g_warning ("Failed to retrieve errors: %s", error->message);
|
||||
@ -657,29 +763,25 @@ plugin_get_errors (PluginObject *obj,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_launch_extension_prefs (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
plugin_launch_extension_prefs (PluginObject *obj,
|
||||
uint32_t argc,
|
||||
const NPVariant *argv,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str;
|
||||
gchar *uuid;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
if (!parse_args ("u", argc, argv, &uuid))
|
||||
return FALSE;
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
"LaunchExtensionPrefs",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
g_variant_new ("(s)", uuid),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
|
||||
g_free (uuid_str);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -733,10 +835,40 @@ plugin_get_shell_version (PluginObject *obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define METHODS \
|
||||
METHOD (list_extensions) \
|
||||
METHOD (get_info) \
|
||||
METHOD (enable_extension) \
|
||||
METHOD (install_extension) \
|
||||
METHOD (uninstall_extension) \
|
||||
METHOD (get_errors) \
|
||||
METHOD (launch_extension_prefs) \
|
||||
/* */
|
||||
|
||||
#define METHOD(x) \
|
||||
static NPIdentifier x##_id;
|
||||
METHODS
|
||||
#undef METHOD
|
||||
|
||||
static NPIdentifier api_version_id;
|
||||
static NPIdentifier shell_version_id;
|
||||
static NPIdentifier onextension_changed_id;
|
||||
static NPIdentifier onrestart_id;
|
||||
|
||||
static bool
|
||||
plugin_object_has_method (NPObject *npobj,
|
||||
NPIdentifier name)
|
||||
{
|
||||
#define METHOD(x) (name == (x##_id)) ||
|
||||
/* expands to (name == list_extensions_id) || FALSE; */
|
||||
return METHODS FALSE;
|
||||
#undef METHOD
|
||||
}
|
||||
|
||||
static bool
|
||||
plugin_object_invoke (NPObject *npobj,
|
||||
NPIdentifier name,
|
||||
const NPVariant *args,
|
||||
const NPVariant *argv,
|
||||
uint32_t argc,
|
||||
NPVariant *result)
|
||||
{
|
||||
@ -748,61 +880,13 @@ plugin_object_invoke (NPObject *npobj,
|
||||
|
||||
VOID_TO_NPVARIANT (*result);
|
||||
|
||||
if (!plugin_object_has_method (npobj, name))
|
||||
return FALSE;
|
||||
#define METHOD(x) \
|
||||
if (name == x##_id) \
|
||||
return plugin_##x (obj, argc, argv, result);
|
||||
METHODS
|
||||
#undef METHOD
|
||||
|
||||
if (name == list_extensions_id)
|
||||
return plugin_list_extensions (obj, result);
|
||||
else if (name == get_info_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result);
|
||||
}
|
||||
else if (name == enable_extension_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE;
|
||||
|
||||
return plugin_enable_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
NPVARIANT_TO_BOOLEAN(args[1]));
|
||||
}
|
||||
else if (name == install_extension_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
|
||||
|
||||
return plugin_install_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
NPVARIANT_TO_STRING(args[1]));
|
||||
}
|
||||
else if (name == uninstall_extension_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_uninstall_extension (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
result);
|
||||
}
|
||||
else if (name == get_errors_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_get_errors (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
result);
|
||||
}
|
||||
else if (name == launch_extension_prefs_id)
|
||||
{
|
||||
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
|
||||
|
||||
return plugin_launch_extension_prefs (obj,
|
||||
NPVARIANT_TO_STRING(args[0]),
|
||||
result);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -946,3 +1030,12 @@ NPP_GetValue(NPP instance,
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
/* Opera tries to call NPP_SetWindow without checking the
|
||||
* NULL pointer beforehand. */
|
||||
NPError
|
||||
NPP_SetWindow(NPP instance,
|
||||
NPWindow *window)
|
||||
{
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
63
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.4.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.5.5],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -44,15 +44,15 @@ AC_SUBST(PYTHON)
|
||||
|
||||
# We need at least this, since gst_plugin_register_static() was added
|
||||
# in 0.10.16, but nothing older than 0.10.21 has been tested.
|
||||
GSTREAMER_MIN_VERSION=0.10.16
|
||||
GSTREAMER_MIN_VERSION=0.11.92
|
||||
|
||||
recorder_modules=
|
||||
build_recorder=false
|
||||
AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
|
||||
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
@ -62,28 +62,30 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.9.16
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.29.18
|
||||
MUTTER_MIN_VERSION=3.3.92
|
||||
FOLKS_MIN_VERSION=0.5.2
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.5.5
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.31.6
|
||||
LIBECAL_MIN_VERSION=2.32.0
|
||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
|
||||
# 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
|
||||
folks >= $FOLKS_MIN_VERSION
|
||||
atk-bridge-2.0
|
||||
libmutter >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 $recorder_modules
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
gl
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
@ -95,7 +97,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util gnome-keyring-1
|
||||
gcr-3 >= $GCR_MIN_VERSION)
|
||||
gcr-3 >= $GCR_MIN_VERSION
|
||||
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
@ -103,13 +106,7 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
|
||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
||||
AC_SUBST(JHBUILD_TYPELIBDIR)
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
@ -123,7 +120,7 @@ PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
@ -239,31 +236,6 @@ AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
||||
|
||||
AC_MSG_CHECKING([location of system Certificate Authority list])
|
||||
AC_ARG_WITH(ca-certificates,
|
||||
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
|
||||
[path to system Certificate Authority list])])
|
||||
|
||||
if test "$with_ca_certificates" = "no"; then
|
||||
AC_MSG_RESULT([disabled])
|
||||
else
|
||||
if test -z "$with_ca_certificates"; then
|
||||
for f in /etc/pki/tls/certs/ca-bundle.crt \
|
||||
/etc/ssl/certs/ca-certificates.crt; do
|
||||
if test -f "$f"; then
|
||||
with_ca_certificates="$f"
|
||||
fi
|
||||
done
|
||||
if test -z "$with_ca_certificates"; then
|
||||
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT($with_ca_certificates)
|
||||
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
|
||||
fi
|
||||
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
|
||||
|
||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
||||
|
||||
@ -277,6 +249,7 @@ AC_CONFIG_FILES([
|
||||
docs/reference/st/Makefile
|
||||
docs/reference/st/st-docs.sgml
|
||||
js/Makefile
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/Makefile
|
||||
browser-plugin/Makefile
|
||||
tests/Makefile
|
||||
|
@ -8,9 +8,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
# Placeholder until we add intltool
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
|
||||
searchprovidersdir = $(pkgdatadir)/open-search-providers
|
||||
dist_searchproviders_DATA = \
|
||||
@ -36,15 +34,14 @@ dist_theme_DATA = \
|
||||
theme/dash-placeholder.svg \
|
||||
theme/filter-selected-ltr.svg \
|
||||
theme/filter-selected-rtl.svg \
|
||||
theme/gdm.css \
|
||||
theme/gnome-shell.css \
|
||||
theme/logged-in-indicator.svg \
|
||||
theme/noise-texture.png \
|
||||
theme/panel-button-border.svg \
|
||||
theme/panel-button-highlight-narrow.svg \
|
||||
theme/panel-button-highlight-wide.svg \
|
||||
theme/process-working.svg \
|
||||
theme/running-indicator.svg \
|
||||
theme/scroll-hhandle.svg \
|
||||
theme/scroll-vhandle.svg \
|
||||
theme/source-button-border.svg \
|
||||
theme/toggle-off-us.svg \
|
||||
theme/toggle-off-intl.svg \
|
||||
@ -56,6 +53,11 @@ dist_theme_DATA = \
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
|
||||
%.gschema.xml.in: %.gschema.xml.in.in Makefile
|
||||
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
|
||||
$< > $@ || rm $@
|
||||
|
||||
@GSETTINGS_RULES@
|
||||
|
||||
# We need to compile schemas at make time
|
||||
@ -68,24 +70,19 @@ all-local: gschemas.compiled
|
||||
convertdir = $(datadir)/GConf/gsettings
|
||||
convert_DATA = gnome-shell-overrides.convert
|
||||
|
||||
shadersdir = $(pkgdatadir)/shaders
|
||||
shaders_DATA = \
|
||||
shaders/dim-window.glsl
|
||||
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-extension-prefs.desktop.in.in \
|
||||
$(introspection_DATA) \
|
||||
$(menu_DATA) \
|
||||
$(shaders_DATA) \
|
||||
$(convert_DATA) \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
org.gnome.shell.gschema.xml.in.in
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
gschemas.compiled
|
||||
|
||||
gschemas.compiled \
|
||||
org.gnome.shell.gschema.valid \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
|
@ -61,9 +61,9 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
|
||||
<_summary>Internally used to store the last session presence status for the user. The
|
||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
||||
<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>
|
||||
|
||||
@ -78,6 +78,24 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="open-application-menu" type="as">
|
||||
<default>["<Super>F10"]</default>
|
||||
<_summary>Keybinding to open the application menu</_summary>
|
||||
<_description>
|
||||
Keybinding to open the application menu.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-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/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="keyboard-type" type="s">
|
||||
@ -89,24 +107,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-seconds" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show time with seconds</_summary>
|
||||
<_description>
|
||||
If true, display seconds in time.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-date" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show date in clock</_summary>
|
||||
<_description>
|
||||
If true, display date in the clock, in addition to time.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="framerate" type="i">
|
@ -1,27 +0,0 @@
|
||||
#version 110
|
||||
uniform sampler2D tex;
|
||||
uniform float fraction;
|
||||
uniform float height;
|
||||
const float c = -0.2;
|
||||
const float border_max_height = 60.0;
|
||||
|
||||
mat4 contrast = mat4 (1.0 + c, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0 + c, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0 + c, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0);
|
||||
vec4 off = vec4(0.633, 0.633, 0.633, 0);
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture2D(tex, cogl_tex_coord_in[0].xy);
|
||||
float y = height * cogl_tex_coord_in[0].y;
|
||||
|
||||
// To reduce contrast, blend with a mid gray
|
||||
cogl_color_out = color * contrast - off * c * color.a;
|
||||
|
||||
// We only fully dim at a distance of BORDER_MAX_HEIGHT from the top and
|
||||
// when the fraction is 1.0. For other locations and fractions we linearly
|
||||
// interpolate back to the original undimmed color, so the top of the window
|
||||
// is at full color.
|
||||
cogl_color_out = color + (cogl_color_out - color) * max(min(y / border_max_height, 1.0), 0.0);
|
||||
cogl_color_out = color + (cogl_color_out - color) * fraction;
|
||||
}
|
@ -10,11 +10,11 @@
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="28"
|
||||
height="25"
|
||||
width="29"
|
||||
height="29"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="calendar-today.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
@ -118,6 +118,17 @@
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3113"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
@ -127,20 +138,29 @@
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="8.3750933"
|
||||
inkscape:cy="8.0837211"
|
||||
inkscape:cx="20.652108"
|
||||
inkscape:cy="11.839084"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="741"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
borderlayer="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3109"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
@ -157,31 +177,28 @@
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-469.08263,-536.99307)">
|
||||
<g
|
||||
id="g3003">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
|
||||
sodipodi:end="6.2831853"
|
||||
sodipodi:start="3.1415927"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:cx="51"
|
||||
id="path34506-3"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:type="arc" />
|
||||
<rect
|
||||
y="558.85046"
|
||||
x="468.96878"
|
||||
height="3.1425927"
|
||||
width="28.149134"
|
||||
id="rect2996"
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
</g>
|
||||
transform="translate(-469.08263,-532.99307)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect2996"
|
||||
width="31"
|
||||
height="3"
|
||||
x="468.08264"
|
||||
y="558.99304" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.1 KiB |
@ -1,180 +0,0 @@
|
||||
/* Copyright 2011, Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU Lesser General Public License,
|
||||
* version 2.1, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* Login Dialog */
|
||||
|
||||
.login-dialog-title {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.login-dialog {
|
||||
border-radius: 16px;
|
||||
min-height: 150px;
|
||||
max-height: 700px;
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-fingerprint-message {
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-view {
|
||||
-st-vfade-offset: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:ltr {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:rtl {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
||||
font-size: 20pt;
|
||||
padding-left: 1em;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
|
||||
color: white;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-vertical-layout {
|
||||
spacing: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
|
||||
background-color: rgba(0,0,0,0.0);
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 8px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button {
|
||||
padding-top: 2em;
|
||||
}
|
||||
.login-dialog-not-listed-label {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-layout {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
.login-dialog-prompt-label {
|
||||
color: white;
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #5656cc;
|
||||
color: black;
|
||||
background-color: white;
|
||||
caret-color: black;
|
||||
caret-size: 1px;
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry .capslock-warning {
|
||||
icon-size: 16px;
|
||||
warning-color: #999;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry:insensitive {
|
||||
color: rgba(0,0,0,0.7);
|
||||
border: 2px solid #565656;
|
||||
}
|
||||
|
||||
.login-dialog-session-list {
|
||||
color: #ffffff;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:focus {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:active {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-scroll-view {
|
||||
background-gradient-start: rgba(80,80,80,0.3);
|
||||
background-gradient-end: rgba(80,80,80,0.7);
|
||||
background-gradient-direction: vertical;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(80,80,80,1.0);
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item:focus {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-triangle {
|
||||
padding-right: .5em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-box {
|
||||
spacing: .25em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-dot {
|
||||
width: .75em;
|
||||
height: .75em;
|
||||
}
|
130
data/theme/logged-in-indicator.svg
Normal file
@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="300"
|
||||
height="80"
|
||||
id="svg7355"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="logged-in-indicator.svg">
|
||||
<metadata
|
||||
id="metadata4175">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#2c1cff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
id="namedview4173"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.8760889"
|
||||
inkscape:cx="106.00403"
|
||||
inkscape:cy="80.68078"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g30864" />
|
||||
<defs
|
||||
id="defs7357">
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient36429"
|
||||
id="radialGradient7461"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.5919312,0,0,0.57582113,-20.687059,48.400487)"
|
||||
cx="47.428951"
|
||||
cy="167.16817"
|
||||
fx="47.428951"
|
||||
fy="167.16817"
|
||||
r="37" />
|
||||
<linearGradient
|
||||
id="linearGradient36429">
|
||||
<stop
|
||||
id="stop36431"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop36433"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient36471"
|
||||
id="radialGradient7463"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
|
||||
cx="49.067139"
|
||||
cy="242.50381"
|
||||
fx="49.067139"
|
||||
fy="242.50381"
|
||||
r="37.00671" />
|
||||
<linearGradient
|
||||
id="linearGradient36471">
|
||||
<stop
|
||||
id="stop36473"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop36475"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="37.00671"
|
||||
fy="242.50381"
|
||||
fx="49.067139"
|
||||
cy="242.50381"
|
||||
cx="49.067139"
|
||||
gradientTransform="matrix(3.4218418,0,0,0.03365337,-61.309005,138.5071)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient7488"
|
||||
xlink:href="#linearGradient36471" />
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g30864"
|
||||
transform="translate(255.223,70.118091)">
|
||||
<rect
|
||||
ry="3.4593496"
|
||||
rx="8.8641119"
|
||||
y="76.159348"
|
||||
x="12.596948"
|
||||
height="71.116341"
|
||||
width="182.22595"
|
||||
id="rect14000"
|
||||
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
id="rect34520"
|
||||
d="m 194.80022,146.83551 -182.559919,0"
|
||||
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:0.61184424;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
connector-curvature="0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
BIN
data/theme/noise-texture.png
Normal file
After Width: | Height: | Size: 78 KiB |
@ -9,7 +9,7 @@
|
||||
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="21"
|
||||
width="17"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
@ -66,9 +66,9 @@
|
||||
<rect
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="3"
|
||||
width="7"
|
||||
height="2"
|
||||
x="9"
|
||||
x="5"
|
||||
y="8" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10"
|
||||
height="4"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="scroll-hhandle.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="rect3592"
|
||||
width="2"
|
||||
height="4"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect3592"
|
||||
id="use2825"
|
||||
transform="translate(8,0)"
|
||||
width="10"
|
||||
height="4" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use2825"
|
||||
id="use2827"
|
||||
transform="translate(-4,0)"
|
||||
width="10"
|
||||
height="4" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="4"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="scroll-hhandle.svg">
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="rect3592"
|
||||
width="2"
|
||||
height="4"
|
||||
x="0"
|
||||
y="-4"
|
||||
rx="0"
|
||||
ry="0"
|
||||
transform="matrix(0,1,-1,0,0,0)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#rect3592"
|
||||
id="use3705"
|
||||
transform="translate(0,4)"
|
||||
width="4"
|
||||
height="10" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use3705"
|
||||
id="use3707"
|
||||
transform="translate(0,4)"
|
||||
width="4"
|
||||
height="10" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
@ -9,7 +9,7 @@
|
||||
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="64"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg3273"
|
||||
version="1.1"
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
@ -9,7 +9,7 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64"
|
||||
width="65"
|
||||
height="22"
|
||||
id="svg3012"
|
||||
version="1.1"
|
||||
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@ -68,6 +68,10 @@ IGNORE_HFILES= \
|
||||
gactionobserver.h \
|
||||
shell-recorder-src.h
|
||||
|
||||
if !BUILD_RECORDER
|
||||
IGNORE_HFILES += shell-recorder.h
|
||||
endif
|
||||
|
||||
# Images to copy into HTML directory.
|
||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
||||
HTML_IMAGES=
|
||||
|
@ -66,4 +66,11 @@ its dependencies to build from tarballs.</description>
|
||||
<gnome:userid>marinaz</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Florian Müllner</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
|
||||
<gnome:userid>fmuellner</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
</Project>
|
||||
|
@ -6,9 +6,7 @@ misc/config.js: misc/config.js.in Makefile
|
||||
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
||||
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
|
||||
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
|
||||
-e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
|
||||
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
|
||||
-e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \
|
||||
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
|
||||
-e "s|[@]datadir@|$(datadir)|g" \
|
||||
-e "s|[@]libexecdir@|$(libexecdir)|g" \
|
||||
@ -24,17 +22,16 @@ nobase_dist_js_DATA = \
|
||||
gdm/loginDialog.js \
|
||||
gdm/powerMenu.js \
|
||||
gdm/systemd.js \
|
||||
gdm/util.js \
|
||||
extensionPrefs/main.js \
|
||||
misc/config.js \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/format.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/history.js \
|
||||
misc/jsParse.js \
|
||||
misc/modemManager.js \
|
||||
misc/params.js \
|
||||
misc/screenSaver.js \
|
||||
misc/util.js \
|
||||
perf/core.js \
|
||||
ui/altTab.js \
|
||||
@ -45,7 +42,6 @@ nobase_dist_js_DATA = \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/checkBox.js \
|
||||
ui/contactDisplay.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
@ -53,13 +49,14 @@ nobase_dist_js_DATA = \
|
||||
ui/endSessionDialog.js \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/flashspot.js \
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/iconGrid.js \
|
||||
ui/keyboard.js \
|
||||
ui/keyringPrompt.js \
|
||||
ui/layout.js \
|
||||
ui/lightbox.js \
|
||||
ui/link.js \
|
||||
ui/lookingGlass.js \
|
||||
ui/magnifier.js \
|
||||
ui/magnifierDBus.js \
|
||||
@ -67,6 +64,7 @@ nobase_dist_js_DATA = \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/networkAgent.js \
|
||||
ui/sessionMode.js \
|
||||
ui/shellEntry.js \
|
||||
ui/shellMountOperation.js \
|
||||
ui/notificationDaemon.js \
|
||||
@ -78,11 +76,11 @@ nobase_dist_js_DATA = \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/runDialog.js \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
ui/searchDisplay.js \
|
||||
ui/shellDBus.js \
|
||||
ui/statusIconDispatcher.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/network.js \
|
||||
@ -91,6 +89,7 @@ nobase_dist_js_DATA = \
|
||||
ui/status/bluetooth.js \
|
||||
ui/telepathyClient.js \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userMenu.js \
|
||||
ui/viewSelector.js \
|
||||
ui/wanda.js \
|
||||
|
@ -6,15 +6,14 @@ const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Format = imports.format;
|
||||
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const Format = imports.misc.format;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
@ -162,7 +161,7 @@ const Application = new Lang.Class({
|
||||
vbox.add(toolbar);
|
||||
let toolitem;
|
||||
|
||||
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
|
||||
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
|
||||
use_markup: true });
|
||||
toolitem = new Gtk.ToolItem({ child: label });
|
||||
toolbar.add(toolitem);
|
||||
@ -202,24 +201,18 @@ const Application = new Lang.Class({
|
||||
},
|
||||
|
||||
_scanExtensions: function() {
|
||||
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
|
||||
if (ExtensionUtils.extensions[uuid] !== undefined)
|
||||
return;
|
||||
|
||||
let extension;
|
||||
try {
|
||||
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||
} catch(e) {
|
||||
global.logError('' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
let iter = this._model.append();
|
||||
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
|
||||
this._extensionIters[uuid] = iter;
|
||||
}));
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||
finder.scanExtensions();
|
||||
},
|
||||
|
||||
_extensionFound: function(signals, extension) {
|
||||
let iter = this._model.append();
|
||||
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||
this._extensionIters[extension.uuid] = iter;
|
||||
},
|
||||
|
||||
|
||||
_onActivate: function() {
|
||||
this._window.present();
|
||||
},
|
||||
@ -257,7 +250,7 @@ function initEnvironment() {
|
||||
},
|
||||
|
||||
logError: function(s) {
|
||||
global.log('ERROR: ' + s);
|
||||
log('ERROR: ' + s);
|
||||
},
|
||||
|
||||
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
|
||||
@ -268,7 +261,6 @@ function initEnvironment() {
|
||||
|
||||
function main(argv) {
|
||||
initEnvironment();
|
||||
ExtensionUtils.init();
|
||||
|
||||
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
||||
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
||||
|
@ -11,10 +11,17 @@ const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
|
||||
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
|
||||
|
||||
function FprintManager() {
|
||||
return new FprintManagerProxy(Gio.DBus.system,
|
||||
'net.reactivated.Fprint',
|
||||
'/net/reactivated/Fprint/Manager');
|
||||
};
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
||||
g_interface_name: FprintManagerInfo.name,
|
||||
g_interface_info: FprintManagerInfo,
|
||||
g_name: 'net.reactivated.Fprint',
|
||||
g_object_path: '/net/reactivated/Fprint/Manager',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
@ -30,83 +30,23 @@ const Pango = imports.gi.Pango;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const GdmGreeter = imports.gi.GdmGreeter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const Fprint = imports.gdm.fingerprint;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const _PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||
const _FADE_ANIMATION_TIME = 0.16;
|
||||
const _RESIZE_ANIMATION_TIME = 0.25;
|
||||
const _SCROLL_ANIMATION_TIME = 2.0;
|
||||
const _SCROLL_ANIMATION_TIME = 0.5;
|
||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||
const _LOGO_ICON_NAME_SIZE = 48;
|
||||
|
||||
const _LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const _FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
|
||||
const _LOGO_KEY = 'logo';
|
||||
|
||||
let _loginDialog = null;
|
||||
|
||||
function _fadeInActor(actor) {
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
if (actor.opacity == 255 && actor.visible)
|
||||
return null;
|
||||
|
||||
actor.show();
|
||||
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
|
||||
|
||||
actor.opacity = 0;
|
||||
actor.set_height(0);
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 255,
|
||||
height: naturalHeight,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
actor.set_height(-1);
|
||||
hold.release();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
return hold;
|
||||
}
|
||||
|
||||
function _fadeOutActor(actor) {
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
if (!actor.visible) {
|
||||
actor.opacity = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (actor.opacity == 0) {
|
||||
actor.hide();
|
||||
return null;
|
||||
}
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 0,
|
||||
height: 0,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
actor.hide();
|
||||
actor.set_height(-1);
|
||||
hold.release();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
return hold;
|
||||
}
|
||||
|
||||
function _smoothlyResizeActor(actor, width, height) {
|
||||
let finalWidth;
|
||||
let finalHeight;
|
||||
@ -148,43 +88,37 @@ const UserListItem = new Lang.Class({
|
||||
this._userChangedId = this.user.connect('changed',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
|
||||
this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout',
|
||||
vertical: true });
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false });
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
|
||||
can_focus: true,
|
||||
child: this._verticalBox,
|
||||
child: layout,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
let layout = new St.BoxLayout({ vertical: false });
|
||||
|
||||
this._verticalBox.add(layout,
|
||||
{ y_fill: true,
|
||||
x_fill: true,
|
||||
expand: true });
|
||||
|
||||
this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' });
|
||||
this._verticalBox.add(this._focusBin,
|
||||
{ x_fill: false,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_fill: false,
|
||||
expand: true });
|
||||
|
||||
this._iconBin = new St.Bin();
|
||||
layout.add(this._iconBin);
|
||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||
vertical: true });
|
||||
layout.add(textLayout,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
layout.add(textLayout, { expand: true });
|
||||
|
||||
this._nameLabel = new St.Label({ text: this.user.get_real_name(),
|
||||
style_class: 'login-dialog-user-list-item-name' });
|
||||
textLayout.add(this._nameLabel);
|
||||
textLayout.add(this._nameLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
|
||||
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
|
||||
scale_x: 0 });
|
||||
textLayout.add(this._timedLoginIndicator,
|
||||
{ x_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_fill: false,
|
||||
y_align: St.Align.END });
|
||||
|
||||
this._updateIcon();
|
||||
this._updateLoggedIn();
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
},
|
||||
@ -192,6 +126,7 @@ const UserListItem = new Lang.Class({
|
||||
_onUserChanged: function() {
|
||||
this._nameLabel.set_text(this.user.get_real_name());
|
||||
this._updateIcon();
|
||||
this._updateLoggedIn();
|
||||
},
|
||||
|
||||
_setIconFromFile: function(iconFile, styleClass) {
|
||||
@ -239,30 +174,40 @@ const UserListItem = new Lang.Class({
|
||||
this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
|
||||
},
|
||||
|
||||
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() {
|
||||
if (this.user.is_logged_in())
|
||||
this.actor.add_style_pseudo_class('logged-in');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('logged-in');
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
},
|
||||
|
||||
fadeOutName: function() {
|
||||
return _fadeOutActor(this._nameLabel);
|
||||
return GdmUtil.fadeOutActor(this._nameLabel);
|
||||
},
|
||||
|
||||
fadeInName: function() {
|
||||
return _fadeInActor(this._nameLabel);
|
||||
return GdmUtil.fadeInActor(this._nameLabel);
|
||||
},
|
||||
|
||||
showFocusAnimation: function(time) {
|
||||
showTimedLoginIndicator: function(time) {
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
let node = this.actor.get_theme_node();
|
||||
let padding = node.get_horizontal_padding();
|
||||
|
||||
let box = this._verticalBox.get_allocation_box();
|
||||
|
||||
Tweener.removeTweens(this._focusBin);
|
||||
this._focusBin.width = 0;
|
||||
Tweener.addTween(this._focusBin,
|
||||
{ width: (box.x2 - box.x1 - padding),
|
||||
this.hideTimedLoginIndicator();
|
||||
Tweener.addTween(this._timedLoginIndicator,
|
||||
{ scale_x: 1.,
|
||||
time: time,
|
||||
transition: 'linear',
|
||||
onComplete: function() {
|
||||
@ -271,6 +216,11 @@ const UserListItem = new Lang.Class({
|
||||
onCompleteScope: this
|
||||
});
|
||||
return hold;
|
||||
},
|
||||
|
||||
hideTimedLoginIndicator: function() {
|
||||
Tweener.removeTweens(this._timedLoginIndicator);
|
||||
this._timedLoginIndicator.scale_x = 0.;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(UserListItem.prototype);
|
||||
@ -284,13 +234,10 @@ const UserList = new Lang.Class({
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
|
||||
this._box = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'login-dialog-user-list' });
|
||||
style_class: 'login-dialog-user-list',
|
||||
pseudo_class: 'expanded' });
|
||||
|
||||
this.actor.add_actor(this._box,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.actor.add_actor(this._box);
|
||||
this._items = {};
|
||||
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems));
|
||||
@ -310,7 +257,7 @@ const UserList = new Lang.Class({
|
||||
|
||||
_showItem: function(item) {
|
||||
let tasks = [function() {
|
||||
return _fadeInActor(item.actor);
|
||||
return GdmUtil.fadeInActor(item.actor);
|
||||
},
|
||||
|
||||
function() {
|
||||
@ -367,17 +314,21 @@ const UserList = new Lang.Class({
|
||||
for (let userName in this._items) {
|
||||
let item = this._items[userName];
|
||||
|
||||
item.actor.set_hover(false);
|
||||
item.actor.reactive = false;
|
||||
item.actor.can_focus = false;
|
||||
item._focusBin.width = 0;
|
||||
item.syncStyleClasses();
|
||||
item._timedLoginIndicator.scale_x = 0.;
|
||||
if (item != exception)
|
||||
tasks.push(function() {
|
||||
return _fadeOutActor(item.actor);
|
||||
return GdmUtil.fadeOutActor(item.actor);
|
||||
});
|
||||
}
|
||||
|
||||
this._box.remove_style_pseudo_class('expanded');
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
return _fadeOutActor(this.actor.vscroll);
|
||||
return GdmUtil.fadeOutActor(this.actor.vscroll);
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, tasks)
|
||||
@ -421,12 +372,16 @@ const UserList = new Lang.Class({
|
||||
|
||||
for (let userName in this._items) {
|
||||
let item = this._items[userName];
|
||||
item.actor.sync_hover();
|
||||
item.actor.reactive = true;
|
||||
item.actor.can_focus = true;
|
||||
item.syncStyleClasses();
|
||||
tasks.push(function() {
|
||||
return this._showItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
this._box.add_style_pseudo_class('expanded');
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
this.takeOverWhitespace();
|
||||
@ -444,7 +399,7 @@ const UserList = new Lang.Class({
|
||||
},
|
||||
|
||||
function() {
|
||||
return _fadeInActor(this.actor.vscroll);
|
||||
return GdmUtil.fadeInActor(this.actor.vscroll);
|
||||
}]);
|
||||
return batch.run();
|
||||
},
|
||||
@ -459,7 +414,7 @@ const UserList = new Lang.Class({
|
||||
Tweener.addTween (adjustment,
|
||||
{ value: value,
|
||||
time: _SCROLL_ANIMATION_TIME,
|
||||
transition: 'linear' });
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
jumpToItem: function(item) {
|
||||
@ -488,6 +443,9 @@ const UserList = new Lang.Class({
|
||||
if (user.is_system_account())
|
||||
return;
|
||||
|
||||
if (user.locked)
|
||||
return;
|
||||
|
||||
let userName = user.get_user_name();
|
||||
|
||||
if (!userName)
|
||||
@ -508,7 +466,6 @@ const UserList = new Lang.Class({
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
this.scrollToItem(item);
|
||||
item.showFocusAnimation(0);
|
||||
}));
|
||||
|
||||
this._moveFocusToItems();
|
||||
@ -550,10 +507,7 @@ const SessionListItem = new Lang.Class({
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
|
||||
|
||||
this.actor.add_actor(this._box,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor.add_actor(this._box);
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
|
||||
@ -564,10 +518,7 @@ const SessionListItem = new Lang.Class({
|
||||
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
|
||||
text: name });
|
||||
|
||||
this._box.add_actor(label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._box.add_actor(label);
|
||||
},
|
||||
|
||||
setShowDot: function(show) {
|
||||
@ -611,10 +562,7 @@ const SessionList = new Lang.Class({
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
let box = new St.BoxLayout();
|
||||
this._button.add_actor(box,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._button.add_actor(box);
|
||||
|
||||
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
|
||||
text: '\u25B8' });
|
||||
@ -622,30 +570,18 @@ const SessionList = new Lang.Class({
|
||||
|
||||
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
|
||||
text: _("Session...") });
|
||||
box.add_actor(label,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
box.add_actor(label);
|
||||
|
||||
this._button.connect('clicked',
|
||||
Lang.bind(this, this._onClicked));
|
||||
this._box.add_actor(this._button,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._box.add_actor(this._button);
|
||||
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
this._box.add_actor(this._scrollView,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._box.add_actor(this._scrollView);
|
||||
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
|
||||
vertical: true });
|
||||
this._scrollView.add_actor(this._itemList,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._scrollView.add_actor(this._itemList);
|
||||
this._scrollView.hide();
|
||||
this.isOpen = false;
|
||||
this._populate();
|
||||
@ -698,7 +634,7 @@ const SessionList = new Lang.Class({
|
||||
this._activeSessionId = null;
|
||||
this._items = {};
|
||||
|
||||
let ids = GdmGreeter.get_session_ids();
|
||||
let ids = Gdm.get_session_ids();
|
||||
ids.sort();
|
||||
|
||||
if (ids.length <= 1) {
|
||||
@ -710,14 +646,10 @@ const SessionList = new Lang.Class({
|
||||
}
|
||||
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]);
|
||||
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]);
|
||||
|
||||
let item = new SessionListItem(ids[i], sessionName);
|
||||
this._itemList.add_actor(item.actor,
|
||||
{ x_align: St.Align.START,
|
||||
y_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._itemList.add_actor(item.actor);
|
||||
this._items[ids[i]] = item;
|
||||
|
||||
if (!this._activeSessionId)
|
||||
@ -736,52 +668,55 @@ const LoginDialog = new Lang.Class({
|
||||
Name: 'LoginDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ shellReactive: true, styleClass: 'login-dialog' });
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
parentActor: parentActor
|
||||
});
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
this.connect('opened',
|
||||
Lang.bind(this, this._onOpened));
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default()
|
||||
this._greeterClient = new GdmGreeter.Client();
|
||||
this._greeterClient = new Gdm.Client();
|
||||
|
||||
this._greeterClient.open_connection();
|
||||
this._greeter = this._greeterClient.get_greeter_sync(null);
|
||||
|
||||
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
|
||||
this._greeter.connect('default-session-name-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
|
||||
this._greeterClient.connect('reset',
|
||||
Lang.bind(this, this._onReset));
|
||||
this._greeterClient.connect('default-session-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
this._greeterClient.connect('info',
|
||||
Lang.bind(this, this._onInfo));
|
||||
this._greeterClient.connect('problem',
|
||||
Lang.bind(this, this._onProblem));
|
||||
this._greeterClient.connect('info-query',
|
||||
Lang.bind(this, this._onInfoQuery));
|
||||
this._greeterClient.connect('secret-info-query',
|
||||
Lang.bind(this, this._onSecretInfoQuery));
|
||||
this._greeterClient.connect('session-opened',
|
||||
Lang.bind(this, this._onSessionOpened));
|
||||
this._greeterClient.connect('timed-login-requested',
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
this._greeterClient.connect('authentication-failed',
|
||||
Lang.bind(this, this._onAuthenticationFailed));
|
||||
this._greeterClient.connect('conversation-stopped',
|
||||
Lang.bind(this, this._onConversationStopped));
|
||||
this._greeter.connect('session-opened',
|
||||
Lang.bind(this, this._onSessionOpened));
|
||||
this._greeter.connect('timed-login-requested',
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: _LOGIN_SCREEN_SCHEMA });
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._startFingerprintConversationIfNeeded();
|
||||
this._settings.connect('changed::' + _LOGO_KEY,
|
||||
this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt));
|
||||
this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
|
||||
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
|
||||
Lang.bind(this, this._updateLogo));
|
||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
||||
Lang.bind(this, this._updateBanner));
|
||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
|
||||
Lang.bind(this, this._updateBanner));
|
||||
|
||||
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
|
||||
this.contentLayout.add(this._logoBox);
|
||||
this._updateLogo();
|
||||
|
||||
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||
text: '' });
|
||||
this.contentLayout.add(this._bannerLabel);
|
||||
this._updateBanner();
|
||||
|
||||
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
|
||||
text: C_("title", "Sign In") });
|
||||
|
||||
@ -836,7 +771,7 @@ const LoginDialog = new Lang.Class({
|
||||
this._sessionList = new SessionList();
|
||||
this._sessionList.connect('session-activated',
|
||||
Lang.bind(this, function(list, sessionId) {
|
||||
this._greeterClient.call_select_session (sessionId);
|
||||
this._greeter.call_select_session_sync (sessionId, null);
|
||||
}));
|
||||
|
||||
this._promptBox.add(this._sessionList.actor,
|
||||
@ -884,25 +819,9 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
},
|
||||
|
||||
_startFingerprintConversationIfNeeded: function() {
|
||||
this._haveFingerprintReader = false;
|
||||
|
||||
if (!this._settings.get_boolean(_FINGERPRINT_AUTHENTICATION_KEY))
|
||||
return;
|
||||
|
||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, Lang.bind(this,
|
||||
function(device, error) {
|
||||
if (!error && device)
|
||||
this._haveFingerprintReader = true;
|
||||
|
||||
if (this._haveFingerprintReader)
|
||||
this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateLogo: function() {
|
||||
this._logoBox.child = null;
|
||||
let path = this._settings.get_string(_LOGO_KEY);
|
||||
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
||||
|
||||
if (path) {
|
||||
let file = Gio.file_new_for_path(path);
|
||||
@ -914,10 +833,19 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
},
|
||||
|
||||
_onReset: function(client, serviceName) {
|
||||
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
|
||||
this._startFingerprintConversationIfNeeded();
|
||||
_updateBanner: function() {
|
||||
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
|
||||
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
|
||||
|
||||
if (enabled && text) {
|
||||
this._bannerLabel.set_text(text);
|
||||
this._fadeInBanner();
|
||||
} else {
|
||||
this._fadeOutBanner();
|
||||
}
|
||||
},
|
||||
|
||||
_onReset: function(client, serviceName) {
|
||||
let tasks = [this._hidePrompt,
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
|
||||
@ -947,43 +875,25 @@ const LoginDialog = new Lang.Class({
|
||||
this._sessionList.setActiveSession(sessionId);
|
||||
},
|
||||
|
||||
_onInfo: function(client, serviceName, info) {
|
||||
// We don't display fingerprint messages, because they
|
||||
// have words like UPEK in them. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
if (serviceName == _FINGERPRINT_SERVICE_NAME &&
|
||||
this._haveFingerprintReader &&
|
||||
(!this._promptFingerprintMessage.visible ||
|
||||
this._promptFingerprintMessage.opacity != 255)) {
|
||||
|
||||
_fadeInActor(this._promptFingerprintMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
Main.notifyError(info);
|
||||
_showFingerprintPrompt: function() {
|
||||
GdmUtil.fadeInActor(this._promptFingerprintMessage);
|
||||
},
|
||||
|
||||
_onProblem: function(client, serviceName, problem) {
|
||||
// we don't want to show auth failed messages to
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
Main.notifyError(problem);
|
||||
_hideFingerprintPrompt: function() {
|
||||
GdmUtil.fadeOutActor(this._promptFingerprintMessage);
|
||||
},
|
||||
|
||||
_onCancel: function(client) {
|
||||
this._greeterClient.call_cancel();
|
||||
cancel: function() {
|
||||
this._userVerifier.cancel();
|
||||
},
|
||||
|
||||
_fadeInPrompt: function() {
|
||||
let tasks = [function() {
|
||||
return _fadeInActor(this._promptLabel);
|
||||
return GdmUtil.fadeInActor(this._promptLabel);
|
||||
},
|
||||
|
||||
function() {
|
||||
return _fadeInActor(this._promptEntry);
|
||||
return GdmUtil.fadeInActor(this._promptEntry);
|
||||
},
|
||||
|
||||
function() {
|
||||
@ -994,14 +904,14 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
function() {
|
||||
return _fadeInActor(this._promptBox);
|
||||
return GdmUtil.fadeInActor(this._promptBox);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (this._user && this._user.is_logged_in())
|
||||
return null;
|
||||
|
||||
return _fadeInActor(this._sessionList.actor);
|
||||
return GdmUtil.fadeInActor(this._sessionList.actor);
|
||||
},
|
||||
|
||||
function() {
|
||||
@ -1016,13 +926,14 @@ const LoginDialog = new Lang.Class({
|
||||
_showPrompt: function() {
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
let buttons = [{ action: Lang.bind(this, this._onCancel),
|
||||
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||
label: _("Cancel"),
|
||||
key: Clutter.Escape },
|
||||
{ action: Lang.bind(this, function() {
|
||||
hold.release();
|
||||
}),
|
||||
label: C_("button", "Sign In") }];
|
||||
label: C_("button", "Sign In"),
|
||||
default: true }];
|
||||
|
||||
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
@ -1057,13 +968,12 @@ const LoginDialog = new Lang.Class({
|
||||
this.setButtons([]);
|
||||
|
||||
let tasks = [function() {
|
||||
return _fadeOutActor(this._promptBox);
|
||||
return GdmUtil.fadeOutActor(this._promptBox);
|
||||
},
|
||||
|
||||
function() {
|
||||
this._promptFingerprintMessage.hide();
|
||||
this._promptEntry.reactive = true;
|
||||
this._promptEntry.remove_style_pseudo_class('insensitive');
|
||||
this._promptEntry.set_text('');
|
||||
}];
|
||||
|
||||
@ -1072,43 +982,26 @@ const LoginDialog = new Lang.Class({
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_askQuestion: function(serviceName, question) {
|
||||
_askQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
this._promptLabel.set_text(question);
|
||||
|
||||
this._promptEntry.set_text('');
|
||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
||||
|
||||
let tasks = [this._showPrompt,
|
||||
|
||||
function() {
|
||||
let _text = this._promptEntry.get_text();
|
||||
this._promptEntry.reactive = false;
|
||||
this._promptEntry.add_style_pseudo_class('insensitive');
|
||||
this._greeterClient.call_answer_query(serviceName, _text);
|
||||
this._userVerifier.answerQuery(serviceName, _text);
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
return batch.run();
|
||||
},
|
||||
_onInfoQuery: function(client, serviceName, question) {
|
||||
// We only expect questions to come from the main auth service
|
||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
|
||||
this._promptEntry.set_text('');
|
||||
this._promptEntry.clutter_text.set_password_char('');
|
||||
this._askQuestion(serviceName, question);
|
||||
},
|
||||
|
||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||
// We only expect secret requests to come from the main auth service
|
||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
|
||||
this._promptEntry.set_text('');
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
this._askQuestion(serviceName, secretQuestion);
|
||||
},
|
||||
|
||||
_onSessionOpened: function(client, serviceName) {
|
||||
this._greeterClient.call_start_session_when_ready(serviceName, true);
|
||||
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||
},
|
||||
|
||||
_waitForItemForUser: function(userName) {
|
||||
@ -1135,7 +1028,7 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
_showTimedLoginAnimation: function() {
|
||||
this._timedLoginItem.actor.grab_key_focus();
|
||||
return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime);
|
||||
return this._timedLoginItem.showTimedLoginIndicator(this._timedLoginAnimationTime);
|
||||
},
|
||||
|
||||
_blockTimedLoginUntilIdle: function() {
|
||||
@ -1176,7 +1069,6 @@ const LoginDialog = new Lang.Class({
|
||||
// item.
|
||||
if (!this.is_loaded) {
|
||||
this._userList.jumpToItem(this._timedLoginItem);
|
||||
this._timedLoginItem.showFocusAnimation(0);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1190,7 +1082,7 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
function() {
|
||||
this._timedLoginBatch = null;
|
||||
this._greeterClient.call_begin_auto_login(userName);
|
||||
this._greeter.call_begin_auto_login_sync(userName, null);
|
||||
}];
|
||||
|
||||
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
@ -1204,6 +1096,9 @@ const LoginDialog = new Lang.Class({
|
||||
this._timedLoginBatch = null;
|
||||
}
|
||||
|
||||
if (this._timedLoginItem)
|
||||
this._timedLoginItem.hideTimedLoginIndicator();
|
||||
|
||||
let userName = this._timedLoginItem.user.get_user_name();
|
||||
|
||||
if (userName)
|
||||
@ -1233,19 +1128,8 @@ const LoginDialog = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_onAuthenticationFailed: function(client) {
|
||||
this._greeterClient.call_cancel();
|
||||
},
|
||||
|
||||
_onConversationStopped: function(client, serviceName) {
|
||||
// if the password service fails, then cancel everything.
|
||||
// But if, e.g., fingerprint fails, still give
|
||||
// password authentication a chance to succeed
|
||||
if (serviceName == _PASSWORD_SERVICE_NAME) {
|
||||
this._greeterClient.call_cancel();
|
||||
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
|
||||
_fadeOutActor(this._promptFingerprintMessage);
|
||||
}
|
||||
_onVerificationFailed: function() {
|
||||
this._userVerifier.cancel();
|
||||
},
|
||||
|
||||
_onNotListedClicked: function(user) {
|
||||
@ -1266,7 +1150,10 @@ const LoginDialog = new Lang.Class({
|
||||
this._fadeOutLogo]),
|
||||
|
||||
function() {
|
||||
this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME);
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
this._userVerifier.begin(null, hold);
|
||||
return hold;
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
@ -1274,30 +1161,47 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_fadeInLogo: function() {
|
||||
return _fadeInActor(this._logoBox);
|
||||
return GdmUtil.fadeInActor(this._logoBox);
|
||||
},
|
||||
|
||||
_fadeOutLogo: function() {
|
||||
return _fadeOutActor(this._logoBox);
|
||||
return GdmUtil.fadeOutActor(this._logoBox);
|
||||
},
|
||||
|
||||
_fadeInBanner: function() {
|
||||
return GdmUtil.fadeInActor(this._bannerLabel);
|
||||
},
|
||||
|
||||
_fadeOutBanner: function() {
|
||||
return GdmUtil.fadeOutActor(this._bannerLabel);
|
||||
},
|
||||
|
||||
_fadeInTitleLabel: function() {
|
||||
return _fadeInActor(this._titleLabel);
|
||||
return GdmUtil.fadeInActor(this._titleLabel);
|
||||
},
|
||||
|
||||
_fadeOutTitleLabel: function() {
|
||||
return _fadeOutActor(this._titleLabel);
|
||||
return GdmUtil.fadeOutActor(this._titleLabel);
|
||||
},
|
||||
|
||||
_fadeInNotListedButton: function() {
|
||||
return _fadeInActor(this._notListedButton);
|
||||
return GdmUtil.fadeInActor(this._notListedButton);
|
||||
},
|
||||
|
||||
_fadeOutNotListedButton: function() {
|
||||
return _fadeOutActor(this._notListedButton);
|
||||
return GdmUtil.fadeOutActor(this._notListedButton);
|
||||
},
|
||||
|
||||
_beginVerificationForUser: function(userName) {
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
this._userVerifier.begin(userName, hold);
|
||||
return hold;
|
||||
},
|
||||
|
||||
_onUserListActivated: function(activatedItem) {
|
||||
let userName;
|
||||
|
||||
let tasks = [function() {
|
||||
this._userList.actor.reactive = false;
|
||||
return this._userList.pinInPlace();
|
||||
@ -1324,12 +1228,9 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
function() {
|
||||
let userName = activatedItem.user.get_user_name();
|
||||
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME,
|
||||
userName);
|
||||
userName = activatedItem.user.get_user_name();
|
||||
|
||||
if (this._haveFingerprintReader)
|
||||
this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName);
|
||||
return this._beginVerificationForUser(userName);
|
||||
}];
|
||||
|
||||
this._user = activatedItem.user;
|
||||
|
@ -60,10 +60,8 @@ const PowerMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
|
||||
this.actor.hide();
|
||||
else
|
||||
this.actor.show();
|
||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||
this.actor.visible = shouldBeVisible;
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
@ -72,31 +70,22 @@ const PowerMenuButton = new Lang.Class({
|
||||
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveShutdown = result != 'no';
|
||||
this._haveShutdown = result[0] != 'no';
|
||||
else
|
||||
this._haveShutdown = false;
|
||||
|
||||
if (this._haveShutdown)
|
||||
this._powerOffItem.actor.show();
|
||||
else
|
||||
this._powerOffItem.actor.hide();
|
||||
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
} else {
|
||||
this._consoleKitManager.CanStopRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveShutdown = result;
|
||||
this._haveShutdown = result[0];
|
||||
else
|
||||
this._haveShutdown = false;
|
||||
|
||||
if (this._haveShutdown) {
|
||||
this._powerOffItem.actor.show();
|
||||
} else {
|
||||
this._powerOffItem.actor.hide();
|
||||
}
|
||||
|
||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
}
|
||||
@ -108,31 +97,22 @@ const PowerMenuButton = new Lang.Class({
|
||||
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveRestart = result != 'no';
|
||||
this._haveRestart = result[0] != 'no';
|
||||
else
|
||||
this._haveRestart = false;
|
||||
|
||||
if (this._haveRestart)
|
||||
this._restartItem.actor.show();
|
||||
else
|
||||
this._restartItem.actor.hide();
|
||||
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
} else {
|
||||
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error)
|
||||
this._haveRestart = result;
|
||||
this._haveRestart = result[0];
|
||||
else
|
||||
this._haveRestart = false;
|
||||
|
||||
if (this._haveRestart) {
|
||||
this._restartItem.actor.show();
|
||||
} else {
|
||||
this._restartItem.actor.hide();
|
||||
}
|
||||
|
||||
this._restartItem.actor.visible = this._haveRestart;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
}
|
||||
@ -140,12 +120,7 @@ const PowerMenuButton = new Lang.Class({
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
|
||||
if (this._haveSuspend)
|
||||
this._suspendItem.actor.show();
|
||||
else
|
||||
this._suspendItem.actor.hide();
|
||||
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
|
268
js/gdm/util.js
Normal file
@ -0,0 +1,268 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const Fprint = imports.gdm.fingerprint;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||
const FADE_ANIMATION_TIME = 0.16;
|
||||
|
||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||
|
||||
const LOGO_KEY = 'logo';
|
||||
|
||||
function fadeInActor(actor) {
|
||||
if (actor.opacity == 255 && actor.visible)
|
||||
return null;
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
actor.show();
|
||||
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
|
||||
|
||||
actor.opacity = 0;
|
||||
actor.set_height(0);
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 255,
|
||||
height: naturalHeight,
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this.set_height(-1);
|
||||
hold.release();
|
||||
},
|
||||
});
|
||||
|
||||
return hold;
|
||||
}
|
||||
|
||||
function fadeOutActor(actor) {
|
||||
if (!actor.visible || actor.opacity == 0) {
|
||||
actor.opacity = 0;
|
||||
actor.hide();
|
||||
return null;
|
||||
}
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 0,
|
||||
height: 0,
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this.hide();
|
||||
this.set_height(-1);
|
||||
hold.release();
|
||||
},
|
||||
});
|
||||
return hold;
|
||||
}
|
||||
|
||||
const ShellUserVerifier = new Lang.Class({
|
||||
Name: 'ShellUserVerifier',
|
||||
|
||||
_init: function(client, params) {
|
||||
params = Params.parse(params, { reauthenticationOnly: false });
|
||||
this._reauthOnly = params.reauthenticationOnly;
|
||||
|
||||
this._client = client;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._checkForFingerprintReader();
|
||||
},
|
||||
|
||||
begin: function(userName, hold) {
|
||||
this._hold = hold;
|
||||
this._userName = userName;
|
||||
|
||||
if (userName) {
|
||||
// If possible, reauthenticate an already running session,
|
||||
// so any session specific credentials get updated appropriately
|
||||
this._client.open_reauthentication_channel(userName, this._cancellable,
|
||||
Lang.bind(this, this._reauthenticationChannelOpened));
|
||||
} else {
|
||||
this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._userVerifier)
|
||||
this._userVerifier.call_cancel_sync(null);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._userVerifier) {
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
}
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
},
|
||||
|
||||
_checkForFingerprintReader: function() {
|
||||
this._haveFingerprintReader = false;
|
||||
|
||||
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
||||
return;
|
||||
|
||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
||||
function(device, error) {
|
||||
if (!error && device)
|
||||
this._haveFingerprintReader = true;
|
||||
}));
|
||||
},
|
||||
|
||||
_reauthenticationChannelOpened: function(client, result) {
|
||||
try {
|
||||
this._userVerifier = client.open_reauthentication_channel_finish(result);
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
|
||||
this._hold.release();
|
||||
} catch (e) {
|
||||
if (this._reauthOnly) {
|
||||
logError(e, 'Failed to open reauthentication channel');
|
||||
|
||||
this._hold.release();
|
||||
this.emit('verification-failed');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no session running, or it otherwise fails, then fall back
|
||||
// to performing verification from this login session
|
||||
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||
}
|
||||
},
|
||||
|
||||
_userVerifierGot: function(client, result) {
|
||||
this._userVerifier = client.get_user_verifier_finish(result);
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
|
||||
this._hold.release();
|
||||
},
|
||||
|
||||
_connectSignals: function() {
|
||||
this._userVerifier.connect('info', Lang.bind(this, this._onInfo));
|
||||
this._userVerifier.connect('problem', Lang.bind(this, this._onProblem));
|
||||
this._userVerifier.connect('info-query', Lang.bind(this, this._onInfoQuery));
|
||||
this._userVerifier.connect('secret-info-query', Lang.bind(this, this._onSecretInfoQuery));
|
||||
this._userVerifier.connect('conversation-stopped', Lang.bind(this, this._onConversationStopped));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
},
|
||||
|
||||
_beginVerification: function() {
|
||||
this._hold.acquire();
|
||||
|
||||
if (this._userName) {
|
||||
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
||||
this._userName,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
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) {
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
this._hold.release();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
||||
this._cancellable,
|
||||
Lang.bind(this, function(obj, result) {
|
||||
obj.call_begin_verification_finish(result);
|
||||
this._hold.release();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onInfo: function(client, serviceName, info) {
|
||||
// We don't display fingerprint messages, because they
|
||||
// have words like UPEK in them. Instead we use the messages
|
||||
// as a cue to display our own message.
|
||||
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||
this._haveFingerprintReader) {
|
||||
this.emit('show-fingerprint-prompt');
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
Main.notifyError(info);
|
||||
}
|
||||
},
|
||||
|
||||
_onProblem: function(client, serviceName, problem) {
|
||||
// we don't want to show auth failed messages to
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
Main.notifyError(problem);
|
||||
},
|
||||
|
||||
_onInfoQuery: function(client, serviceName, question) {
|
||||
// We only expect questions to come from the main auth service
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
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)
|
||||
return;
|
||||
|
||||
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
|
||||
this._checkForFingerprintReader();
|
||||
|
||||
this.emit('reset');
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this.emit('verification-complete');
|
||||
},
|
||||
|
||||
_onConversationStopped: function(client, serviceName) {
|
||||
// if the password service fails, then cancel everything.
|
||||
// But if, e.g., fingerprint fails, still give
|
||||
// password authentication a chance to succeed
|
||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this.emit('verification-failed');
|
||||
} else if (serviceName == FINGERPRINT_SERVICE_NAME) {
|
||||
this.emit('hide-fingerprint-prompt');
|
||||
}
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
@ -4,12 +4,8 @@
|
||||
const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||
/* The version of this package */
|
||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||
/* The version of GJS we're linking to */
|
||||
const GJS_VERSION = '@GJS_VERSION@';
|
||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
/* The system TLS CA list */
|
||||
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
|
||||
/* gettext package */
|
||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
||||
/* locale dir */
|
||||
|
@ -3,6 +3,9 @@
|
||||
// Common utils for the extension system and the extension
|
||||
// preferences tool
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const ShellJS = imports.gi.ShellJS;
|
||||
@ -14,9 +17,6 @@ const ExtensionType = {
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensions = {};
|
||||
|
||||
@ -40,13 +40,18 @@ function getCurrentExtension() {
|
||||
throw new Error('Could not find current extension');
|
||||
|
||||
let path = match[1];
|
||||
let uuid = GLib.path_get_basename(GLib.path_get_dirname(path));
|
||||
let file = Gio.File.new_for_path(path);
|
||||
|
||||
let extension = extensions[uuid];
|
||||
if (extension === undefined)
|
||||
throw new Error('Could not find current extension');
|
||||
// Walk up the directory tree, looking for an extesion with
|
||||
// the same UUID as a directory name.
|
||||
while (file != null) {
|
||||
let extension = extensions[file.get_basename()];
|
||||
if (extension !== undefined)
|
||||
return extension;
|
||||
file = file.get_parent();
|
||||
}
|
||||
|
||||
return extension;
|
||||
throw new Error('Could not find current extension');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,9 +88,6 @@ function isOutOfDate(extension) {
|
||||
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
|
||||
return true;
|
||||
|
||||
if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -120,7 +122,7 @@ function createExtensionObject(uuid, dir, type) {
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta.url) {
|
||||
global.log('Warning: Missing "url" property in metadata.json');
|
||||
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
@ -150,45 +152,55 @@ function installImporter(extension) {
|
||||
_extension = null;
|
||||
}
|
||||
|
||||
function init() {
|
||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||
try {
|
||||
if (!userExtensionsDir.query_exists(null))
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
}
|
||||
}
|
||||
const ExtensionFinder = new Lang.Class({
|
||||
Name: 'ExtensionFinder',
|
||||
|
||||
function scanExtensionsInDirectory(callback, dir, type) {
|
||||
let fileEnum;
|
||||
let file, info;
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
global.logError('' + e);
|
||||
return;
|
||||
}
|
||||
_scanExtensionsInDirectory: function(dir, type) {
|
||||
let fileEnum;
|
||||
let file, info;
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
logError(e, 'Could not enumerate extensions directory');
|
||||
return;
|
||||
}
|
||||
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let uuid = info.get_name();
|
||||
let extensionDir = dir.get_child(uuid);
|
||||
callback(uuid, extensionDir, type);
|
||||
}
|
||||
fileEnum.close(null);
|
||||
}
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let uuid = info.get_name();
|
||||
let extensionDir = dir.get_child(uuid);
|
||||
|
||||
function scanExtensions(callback) {
|
||||
let systemDataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
|
||||
let dir = Gio.file_new_for_path(dirPath);
|
||||
if (dir.query_exists(null))
|
||||
scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
|
||||
let existing = extensions[uuid];
|
||||
if (existing) {
|
||||
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
|
||||
continue;
|
||||
}
|
||||
|
||||
let extension;
|
||||
try {
|
||||
extension = createExtensionObject(uuid, extensionDir, type);
|
||||
} catch(e) {
|
||||
logError(e, 'Could not load extension %s'.format(uuid));
|
||||
continue;
|
||||
}
|
||||
this.emit('extension-found', extension);
|
||||
}
|
||||
fileEnum.close(null);
|
||||
},
|
||||
|
||||
scanExtensions: function() {
|
||||
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
|
||||
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
|
||||
|
||||
let systemDataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
|
||||
let dir = Gio.file_new_for_path(dirPath);
|
||||
if (dir.query_exists(null))
|
||||
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
|
||||
}
|
||||
}
|
||||
scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ExtensionFinder.prototype);
|
||||
|
@ -28,7 +28,7 @@ function deleteGFile(file) {
|
||||
return file['delete'](null);
|
||||
}
|
||||
|
||||
function recursivelyDeleteDir(dir) {
|
||||
function recursivelyDeleteDir(dir, deleteParent) {
|
||||
let children = dir.enumerate_children('standard::name,standard::type',
|
||||
Gio.FileQueryInfoFlags.NONE, null);
|
||||
|
||||
@ -39,8 +39,29 @@ function recursivelyDeleteDir(dir) {
|
||||
if (type == Gio.FileType.REGULAR)
|
||||
deleteGFile(child);
|
||||
else if (type == Gio.FileType.DIRECTORY)
|
||||
recursivelyDeleteDir(child);
|
||||
recursivelyDeleteDir(child, true);
|
||||
}
|
||||
|
||||
deleteGFile(dir);
|
||||
if (deleteParent)
|
||||
deleteGFile(dir);
|
||||
}
|
||||
|
||||
function recursivelyMoveDir(srcDir, destDir) {
|
||||
let children = srcDir.enumerate_children('standard::name,standard::type',
|
||||
Gio.FileQueryInfoFlags.NONE, null);
|
||||
|
||||
if (!destDir.query_exists(null))
|
||||
destDir.make_directory_with_parents(null);
|
||||
|
||||
let info, child;
|
||||
while ((info = children.next_file(null)) != null) {
|
||||
let type = info.get_file_type();
|
||||
let srcChild = srcDir.get_child(info.get_name());
|
||||
let destChild = destDir.get_child(info.get_name());
|
||||
log([srcChild.get_path(), destChild.get_path()]);
|
||||
if (type == Gio.FileType.REGULAR)
|
||||
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
|
||||
else if (type == Gio.FileType.DIRECTORY)
|
||||
recursivelyMoveDir(srcChild, destChild);
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
/*
|
||||
* This function is intended to extend the String object and provide
|
||||
* an String.format API for string formatting.
|
||||
* It has to be set up using String.prototype.format = Format.format;
|
||||
* Usage:
|
||||
* "somestring %s %d".format('hello', 5);
|
||||
* It supports %s, %d, %x and %f, for %f it also support precisions like
|
||||
* "%.2f".format(1.526). All specifiers can be prefixed with a minimum
|
||||
* field width, e.g. "%5s".format("foo"). Unless the width is prefixed
|
||||
* with '0', the formatted string will be padded with spaces.
|
||||
*/
|
||||
|
||||
function format() {
|
||||
let str = this;
|
||||
let i = 0;
|
||||
let args = arguments;
|
||||
|
||||
return str.replace(/%([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, widthGroup, precisionGroup, genericGroup) {
|
||||
|
||||
if (precisionGroup != '' && genericGroup != 'f')
|
||||
throw new Error("Precision can only be specified for 'f'");
|
||||
|
||||
let fillChar = (widthGroup[0] == '0') ? '0' : ' ';
|
||||
let width = parseInt(widthGroup, 10) || 0;
|
||||
|
||||
function fillWidth(s, c, w) {
|
||||
let fill = '';
|
||||
for (let i = 0; i < w; i++)
|
||||
fill += c;
|
||||
return fill.substr(s.length) + s;
|
||||
}
|
||||
|
||||
let s = '';
|
||||
switch (genericGroup) {
|
||||
case '%':
|
||||
return '%';
|
||||
break;
|
||||
case 's':
|
||||
s = args[i++].toString();
|
||||
break;
|
||||
case 'd':
|
||||
s = parseInt(args[i++]).toString();
|
||||
break;
|
||||
case 'x':
|
||||
s = parseInt(args[i++]).toString(16);
|
||||
break;
|
||||
case 'f':
|
||||
if (precisionGroup == '')
|
||||
s = parseFloat(args[i++]).toString();
|
||||
else
|
||||
s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported conversion character %' + genericGroup);
|
||||
}
|
||||
return fillWidth(s, fillChar, width);
|
||||
});
|
||||
}
|
@ -50,9 +50,20 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
<method name="Shutdown" />
|
||||
<method name="Reboot" />
|
||||
<method name="CanShutdown">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="IsInhibited">
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<signal name="InhibitorAdded">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
<signal name="InhibitorRemoved">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
||||
|
@ -1,48 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
|
||||
<method name="GetActive">
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<method name="Lock" />
|
||||
<method name="SetActive">
|
||||
<arg type="b" direction="in" />
|
||||
</method>
|
||||
<signal name="ActiveChanged">
|
||||
<arg type="b" direction="out" />
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface);
|
||||
|
||||
function ScreenSaverProxy() {
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||
g_interface_name: ScreenSaverInfo.name,
|
||||
g_interface_info: ScreenSaverInfo,
|
||||
g_name: 'org.gnome.ScreenSaver',
|
||||
g_object_path: '/org/gnome/ScreenSaver',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
self.init(null);
|
||||
self.screenSaverActive = false;
|
||||
|
||||
self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) {
|
||||
self.screenSaverActive = isActive;
|
||||
});
|
||||
self.connect('notify::g-name-owner', function() {
|
||||
if (self.g_name_owner) {
|
||||
self.GetActiveRemote(function(result, excp) {
|
||||
if (result) {
|
||||
let [isActive] = result;
|
||||
self.screenSaverActive = isActive;
|
||||
}
|
||||
});
|
||||
} else
|
||||
self.screenSaverActive = false;
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
@ -83,24 +80,33 @@ function spawnCommandLine(command_line) {
|
||||
// this will throw an error.
|
||||
function trySpawn(argv)
|
||||
{
|
||||
var success, pid;
|
||||
try {
|
||||
GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH,
|
||||
null, null);
|
||||
[success, pid] = GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
} catch (err) {
|
||||
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
|
||||
err.message = _("Command not found");
|
||||
} else {
|
||||
/* Rewrite the error in case of ENOENT */
|
||||
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
|
||||
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
|
||||
message: _("Command not found") });
|
||||
} else if (err instanceof GLib.Error) {
|
||||
// The exception from gjs contains an error string like:
|
||||
// Error invoking GLib.spawn_command_line_async: Failed to
|
||||
// execute child process "foo" (No such file or directory)
|
||||
// We are only interested in the part in the parentheses. (And
|
||||
// we can't pattern match the text, since it gets localized.)
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
let message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
throw new (err.constructor)({ code: err.code,
|
||||
message: message });
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
// Dummy child watch; we don't want to double-fork internally
|
||||
// because then we lose the parent-child relationship, which
|
||||
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
|
||||
}
|
||||
|
||||
// trySpawnCommandLine:
|
||||
@ -144,7 +150,7 @@ function killall(processName) {
|
||||
// whatever...
|
||||
|
||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||
// It might be useful to return success/failure, but we'd need
|
||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||
// the current callers care, we don't bother.
|
||||
|
@ -1,5 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const System = imports.system;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Scripting = imports.ui.scripting;
|
||||
|
||||
@ -99,7 +101,7 @@ function run() {
|
||||
Main.overview.hide();
|
||||
yield Scripting.waitLeisure();
|
||||
|
||||
global.gc();
|
||||
System.gc();
|
||||
yield Scripting.sleep(1000);
|
||||
Scripting.collectStatistics();
|
||||
Scripting.scriptEvent('afterShowHide');
|
||||
|
@ -22,6 +22,7 @@ const Search = imports.ui.search;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MAX_APPLICATION_WORK_MILLIS = 75;
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
@ -36,6 +37,7 @@ const AlphabeticalView = new Lang.Class({
|
||||
|
||||
this._pendingAppLaterId = 0;
|
||||
this._appIcons = {}; // desktop file id
|
||||
this._allApps = [];
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
@ -60,16 +62,22 @@ const AlphabeticalView = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
removeAll: function() {
|
||||
this._grid.removeAll();
|
||||
this._appIcons = {};
|
||||
this._allApps = [];
|
||||
},
|
||||
|
||||
_addApp: function(app) {
|
||||
addApp: function(app) {
|
||||
var id = app.get_id();
|
||||
let appIcon = new AppWellIcon(app);
|
||||
if (this._appIcons[id] !== undefined)
|
||||
return;
|
||||
|
||||
this._grid.addItem(appIcon.actor);
|
||||
let appIcon = new AppWellIcon(app);
|
||||
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
});
|
||||
this._grid.addItem(appIcon.actor, pos);
|
||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||
|
||||
this._appIcons[id] = appIcon;
|
||||
@ -120,14 +128,6 @@ const AlphabeticalView = new Lang.Class({
|
||||
icon.actor.visible = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setAppList: function(apps) {
|
||||
this._removeAll();
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
var app = apps[i];
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -147,7 +147,6 @@ const ViewByCategories = new Lang.Class({
|
||||
// (used only before the actor is mapped the first time)
|
||||
this._currentCategory = -2;
|
||||
this._categories = [];
|
||||
this._apps = null;
|
||||
|
||||
this._categoryBox = new St.BoxLayout({ vertical: true,
|
||||
reactive: true,
|
||||
@ -204,16 +203,19 @@ const ViewByCategories = new Lang.Class({
|
||||
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||
var entry = iter.get_entry();
|
||||
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
||||
if (!entry.get_app_info().get_nodisplay())
|
||||
if (!entry.get_app_info().get_nodisplay()) {
|
||||
this._view.addApp(app);
|
||||
appList.push(app);
|
||||
}
|
||||
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
if (!dir.get_is_nodisplay())
|
||||
this._loadCategory(iter.get_directory(), appList);
|
||||
var itemDir = iter.get_directory();
|
||||
if (!itemDir.get_is_nodisplay())
|
||||
this._loadCategory(itemDir, appList);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addCategory: function(name, index, dir, allApps) {
|
||||
_addCategory: function(name, index, dir) {
|
||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START,
|
||||
@ -225,7 +227,6 @@ const ViewByCategories = new Lang.Class({
|
||||
|
||||
var apps;
|
||||
if (dir == null) {
|
||||
apps = allApps;
|
||||
this._allCategoryButton = button;
|
||||
} else {
|
||||
apps = [];
|
||||
@ -239,6 +240,7 @@ const ViewByCategories = new Lang.Class({
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
this._view.removeAll();
|
||||
this._categories = [];
|
||||
this._categoryBox.destroy_all_children();
|
||||
},
|
||||
@ -246,13 +248,8 @@ const ViewByCategories = new Lang.Class({
|
||||
refresh: function() {
|
||||
this._removeAll();
|
||||
|
||||
var allApps = Shell.AppSystem.get_default().get_all();
|
||||
allApps.sort(function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
});
|
||||
|
||||
/* Translators: Filter to display all applications */
|
||||
this._addCategory(_("All"), -1, null, allApps);
|
||||
this._addCategory(_("All"), -1, null);
|
||||
|
||||
var tree = this._appSystem.get_tree();
|
||||
var root = tree.get_root_directory();
|
||||
@ -270,7 +267,6 @@ const ViewByCategories = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
this._view.setAppList(allApps);
|
||||
this._selectCategory(-1);
|
||||
|
||||
if (this._focusDummy) {
|
||||
@ -312,11 +308,10 @@ const AppSearchProvider = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("APPLICATIONS"));
|
||||
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMetas: function(apps) {
|
||||
getResultMetas: function(apps, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
@ -327,15 +322,15 @@ const AppSearchProvider = new Lang.Class({
|
||||
}
|
||||
});
|
||||
}
|
||||
return metas;
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.initial_search(terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(previousResults, terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
|
||||
},
|
||||
|
||||
activateResult: function(app, params) {
|
||||
@ -378,7 +373,7 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
||||
},
|
||||
|
||||
getResultMetas: function(prefs) {
|
||||
getResultMetas: function(prefs, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < prefs.length; i++) {
|
||||
let pref = prefs[i];
|
||||
@ -389,15 +384,15 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
}
|
||||
});
|
||||
}
|
||||
return metas;
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.search_settings(terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.search_settings(terms);
|
||||
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||
},
|
||||
|
||||
activateResult: function(pref, params) {
|
||||
|
@ -9,7 +9,9 @@ const Params = imports.misc.params;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Main = imports.ui.main;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
|
||||
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
||||
|
||||
// GSettings keys
|
||||
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
||||
@ -79,13 +81,17 @@ const AutomountManager = new Lang.Class({
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
this._volumeQueue = [];
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._session.connectSignal('InhibitorAdded',
|
||||
Lang.bind(this, this._InhibitorsChanged));
|
||||
this._session.connectSignal('InhibitorRemoved',
|
||||
Lang.bind(this, this._InhibitorsChanged));
|
||||
this._inhibited = false;
|
||||
|
||||
if (!haveSystemd())
|
||||
this.ckListener = new ConsoleKitManager();
|
||||
|
||||
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._ssProxy.connectSignal('ActiveChanged',
|
||||
Lang.bind(this, this._screenSaverActiveChanged));
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged));
|
||||
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
@ -108,8 +114,18 @@ const AutomountManager = new Lang.Class({
|
||||
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||
},
|
||||
|
||||
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
|
||||
if (!isActive) {
|
||||
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
|
||||
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT,
|
||||
Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._inhibited = result[0];
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_lockStatusChanged: function(shield, locked) {
|
||||
if (!locked) {
|
||||
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
|
||||
this._checkAndMountVolume(volume);
|
||||
}));
|
||||
@ -123,7 +139,8 @@ const AutomountManager = new Lang.Class({
|
||||
let volumes = this._volumeMonitor.get_volumes();
|
||||
volumes.forEach(Lang.bind(this, function(volume) {
|
||||
this._checkAndMountVolume(volume, { checkSession: false,
|
||||
useMountOp: false });
|
||||
useMountOp: false,
|
||||
allowAutorun: false });
|
||||
}));
|
||||
|
||||
return false;
|
||||
@ -146,7 +163,7 @@ const AutomountManager = new Lang.Class({
|
||||
if (!this.isSessionActive())
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive)
|
||||
if (Main.screenShield.locked)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-added-media');
|
||||
@ -158,7 +175,7 @@ const AutomountManager = new Lang.Class({
|
||||
if (!this.isSessionActive())
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive)
|
||||
if (Main.screenShield.locked)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-removed-media');
|
||||
@ -201,7 +218,8 @@ const AutomountManager = new Lang.Class({
|
||||
|
||||
_checkAndMountVolume: function(volume, params) {
|
||||
params = Params.parse(params, { checkSession: true,
|
||||
useMountOp: true });
|
||||
useMountOp: true,
|
||||
allowAutorun: true });
|
||||
|
||||
if (params.checkSession) {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
@ -209,7 +227,7 @@ const AutomountManager = new Lang.Class({
|
||||
if (!this.isSessionActive())
|
||||
return;
|
||||
|
||||
if (this._ssProxy.screenSaverActive) {
|
||||
if (Main.screenShield.locked) {
|
||||
if (this._volumeQueue.indexOf(volume) == -1)
|
||||
this._volumeQueue.push(volume);
|
||||
|
||||
@ -217,6 +235,9 @@ const AutomountManager = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inhibited)
|
||||
return;
|
||||
|
||||
// Volume is already mounted, don't bother.
|
||||
if (volume.get_mount())
|
||||
return;
|
||||
@ -236,15 +257,20 @@ const AutomountManager = new Lang.Class({
|
||||
|
||||
if (params.useMountOp) {
|
||||
let operation = new ShellMountOperation.ShellMountOperation(volume);
|
||||
this._mountVolume(volume, operation.mountOp);
|
||||
this._mountVolume(volume, operation, params.allowAutorun);
|
||||
} else {
|
||||
this._mountVolume(volume, null);
|
||||
this._mountVolume(volume, null, params.allowAutorun);
|
||||
}
|
||||
},
|
||||
|
||||
_mountVolume: function(volume, operation) {
|
||||
this._allowAutorun(volume);
|
||||
volume.mount(0, operation, null,
|
||||
_mountVolume: function(volume, operation, allowAutorun) {
|
||||
if (allowAutorun)
|
||||
this._allowAutorun(volume);
|
||||
|
||||
let mountOp = operation ? operation.mountOp : null;
|
||||
volume._operation = operation;
|
||||
|
||||
volume.mount(0, mountOp, null,
|
||||
Lang.bind(this, this._onVolumeMounted));
|
||||
},
|
||||
|
||||
@ -253,15 +279,19 @@ const AutomountManager = new Lang.Class({
|
||||
|
||||
try {
|
||||
volume.mount_finish(res);
|
||||
this._closeOperation(volume);
|
||||
} catch (e) {
|
||||
let string = e.toString();
|
||||
|
||||
// FIXME: needs proper error code handling instead of this
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
if (string.indexOf('No key available with this passphrase') != -1)
|
||||
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
|
||||
// backend in this case, see
|
||||
// https://bugs.freedesktop.org/show_bug.cgi?id=51271
|
||||
if (e.message.indexOf('No key available with this passphrase') != -1) {
|
||||
this._reaskPassword(volume);
|
||||
else
|
||||
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
|
||||
} else {
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
|
||||
|
||||
this._closeOperation(volume);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -273,8 +303,16 @@ const AutomountManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_reaskPassword: function(volume) {
|
||||
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
|
||||
this._mountVolume(volume, operation.mountOp);
|
||||
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null;
|
||||
let operation =
|
||||
new ShellMountOperation.ShellMountOperation(volume,
|
||||
{ existingDialog: existingDialog });
|
||||
this._mountVolume(volume, operation);
|
||||
},
|
||||
|
||||
_closeOperation: function(volume) {
|
||||
if (volume._operation)
|
||||
volume._operation.close();
|
||||
},
|
||||
|
||||
_allowAutorun: function(volume) {
|
||||
|
@ -23,12 +23,14 @@ const AutorunSetting = {
|
||||
};
|
||||
|
||||
// misc utils
|
||||
function ignoreAutorunForMount(mount) {
|
||||
function shouldAutorunMount(mount, forTransient) {
|
||||
let root = mount.get_root();
|
||||
let volume = mount.get_volume();
|
||||
|
||||
if ((root.is_native() && !isMountRootHidden(root)) ||
|
||||
(volume && volume.allowAutorun && volume.should_automount()))
|
||||
if (!volume || (!volume.allowAutorun && forTransient))
|
||||
return false;
|
||||
|
||||
if (!root.is_native() || isMountRootHidden(root))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -224,11 +226,9 @@ const AutorunManager = new Lang.Class({
|
||||
try {
|
||||
mount.unmount_with_operation_finish(res);
|
||||
} catch (e) {
|
||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||
// but we can't access the error code from JS.
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
log('Unable to eject the mount ' + mount.get_name()
|
||||
+ ': ' + e.toString());
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to eject the mount ' + mount.get_name()
|
||||
+ ': ' + e.toString());
|
||||
}
|
||||
},
|
||||
|
||||
@ -236,11 +236,9 @@ const AutorunManager = new Lang.Class({
|
||||
try {
|
||||
source.eject_with_operation_finish(res);
|
||||
} catch (e) {
|
||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||
// but we can't access the error code from JS.
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
log('Unable to eject the drive ' + source.get_name()
|
||||
+ ': ' + e.toString());
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to eject the drive ' + source.get_name()
|
||||
+ ': ' + e.toString());
|
||||
}
|
||||
},
|
||||
|
||||
@ -248,11 +246,9 @@ const AutorunManager = new Lang.Class({
|
||||
try {
|
||||
drive.stop_finish(res);
|
||||
} catch (e) {
|
||||
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
|
||||
// but we can't access the error code from JS.
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
|
||||
log('Unable to stop the drive ' + drive.get_name()
|
||||
+ ': ' + e.toString());
|
||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||
log('Unable to stop the drive ' + drive.get_name()
|
||||
+ ': ' + e.toString());
|
||||
}
|
||||
},
|
||||
});
|
||||
@ -262,16 +258,15 @@ const AutorunResidentSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Removable Devices"));
|
||||
this.parent(_("Removable Devices"), 'media-removable', St.IconType.FULLCOLOR);
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
this._notification = new AutorunResidentNotification(this);
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
addMount: function(mount, apps) {
|
||||
if (ignoreAutorunForMount(mount))
|
||||
if (!shouldAutorunMount(mount, false))
|
||||
return;
|
||||
|
||||
let filtered = this._mounts.filter(function (element) {
|
||||
@ -310,12 +305,6 @@ const AutorunResidentSource = new Lang.Class({
|
||||
Main.messageTray.add(this);
|
||||
this.pushNotification(this._notification);
|
||||
}
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon ({ icon_name: 'media-removable',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
});
|
||||
|
||||
@ -455,7 +444,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
||||
return;
|
||||
|
||||
// if the mount doesn't want to be autorun, return
|
||||
if (ignoreAutorunForMount(mount))
|
||||
if (!shouldAutorunMount(mount, true))
|
||||
return;
|
||||
|
||||
let setting = this._getAutorunSettingForType(contentTypes[0]);
|
||||
@ -500,22 +489,21 @@ const AutorunTransientSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(mount, apps) {
|
||||
this.parent(mount.get_name());
|
||||
|
||||
this.mount = mount;
|
||||
this.apps = apps;
|
||||
|
||||
this.parent(mount.get_name());
|
||||
|
||||
this._notification = new AutorunTransientNotification(this);
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
// add ourselves as a source, and popup the notification
|
||||
Main.messageTray.add(this);
|
||||
this.notify(this._notification);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
createIcon: function(size) {
|
||||
return new St.Icon({ gicon: this.mount.get_icon(),
|
||||
icon_size: this.ICON_SIZE });
|
||||
icon_size: size });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9,6 +9,13 @@ const Shell = imports.gi.Shell;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PopupAnimation = {
|
||||
NONE: 0,
|
||||
SLIDE: 1 << 0,
|
||||
FADE: 1 << 1,
|
||||
FULL: ~0,
|
||||
};
|
||||
|
||||
const POPUP_ANIMATION_TIME = 0.15;
|
||||
|
||||
/**
|
||||
@ -18,7 +25,10 @@ const POPUP_ANIMATION_TIME = 0.15;
|
||||
*
|
||||
* An actor which displays a triangle "arrow" pointing to a given
|
||||
* side. The .bin property is a container in which content can be
|
||||
* placed. The arrow position may be controlled via setArrowOrigin().
|
||||
* placed. The arrow position may be controlled via
|
||||
* setArrowOrigin(). The arrow side might be temporarily flipped
|
||||
* depending on the box size and source position to keep the box
|
||||
* totally inside the monitor if possible.
|
||||
*
|
||||
*/
|
||||
const BoxPointer = new Lang.Class({
|
||||
@ -26,6 +36,7 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
_init: function(arrowSide, binProperties) {
|
||||
this._arrowSide = arrowSide;
|
||||
this._userArrowSide = arrowSide;
|
||||
this._arrowOrigin = 0;
|
||||
this.actor = new St.Bin({ x_fill: true,
|
||||
y_fill: true });
|
||||
@ -65,11 +76,16 @@ const BoxPointer = new Lang.Class({
|
||||
show: function(animate, onComplete) {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
||||
|
||||
if (animate & PopupAnimation.FADE)
|
||||
this.opacity = 0;
|
||||
else
|
||||
this.opacity = 255;
|
||||
|
||||
this.opacity = 0;
|
||||
this.actor.show();
|
||||
|
||||
if (animate) {
|
||||
if (animate & PopupAnimation.SLIDE) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.yOffset = -rise;
|
||||
@ -95,7 +111,7 @@ const BoxPointer = new Lang.Class({
|
||||
if (onComplete)
|
||||
onComplete();
|
||||
}),
|
||||
time: POPUP_ANIMATION_TIME });
|
||||
time: animationTime });
|
||||
},
|
||||
|
||||
hide: function(animate, onComplete) {
|
||||
@ -103,8 +119,10 @@ const BoxPointer = new Lang.Class({
|
||||
let yOffset = 0;
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
let fade = (animate & PopupAnimation.FADE);
|
||||
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
||||
|
||||
if (animate) {
|
||||
if (animate & PopupAnimation.SLIDE) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
yOffset = rise;
|
||||
@ -123,13 +141,14 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
this._muteInput();
|
||||
|
||||
Tweener.addTween(this, { opacity: 0,
|
||||
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
||||
xOffset: xOffset,
|
||||
yOffset: yOffset,
|
||||
transition: 'linear',
|
||||
time: POPUP_ANIMATION_TIME,
|
||||
time: animationTime,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
this.opacity = 0;
|
||||
this.xOffset = 0;
|
||||
this.yOffset = 0;
|
||||
if (onComplete)
|
||||
@ -199,8 +218,27 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped)
|
||||
if (this._sourceActor && this._sourceActor.mapped) {
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
|
||||
if (this._shouldFlip()) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this._arrowSide = St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this._arrowSide = St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this._arrowSide = St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this._arrowSide = St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_drawBorder: function(area) {
|
||||
@ -214,7 +252,6 @@ const BoxPointer = new Lang.Class({
|
||||
let halfBorder = borderWidth / 2;
|
||||
let halfBase = Math.floor(base/2);
|
||||
|
||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
||||
|
||||
let [width, height] = area.get_surface_size();
|
||||
@ -225,7 +262,6 @@ const BoxPointer = new Lang.Class({
|
||||
boxWidth -= rise;
|
||||
}
|
||||
let cr = area.get_context();
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
|
||||
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
||||
// with the arrow poking out of that
|
||||
@ -321,12 +357,18 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
cr.fillPreserve();
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
|
||||
if (borderWidth > 0) {
|
||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
}
|
||||
},
|
||||
|
||||
setPosition: function(sourceActor, alignment) {
|
||||
this._arrowSide = this._userArrowSide;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
@ -343,11 +385,7 @@ const BoxPointer = new Lang.Class({
|
||||
if (!this._sourceActor)
|
||||
return;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||
},
|
||||
|
||||
_reposition: function(sourceActor, alignment) {
|
||||
@ -446,6 +484,39 @@ const BoxPointer = new Lang.Class({
|
||||
-(this._yPosition + this._yOffset));
|
||||
},
|
||||
|
||||
_shouldFlip: function() {
|
||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||
return true;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
if (boxAllocation.y1 < monitor.y &&
|
||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||
return true;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||
return true;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (boxAllocation.x1 < monitor.x &&
|
||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
set xOffset(offset) {
|
||||
this._xOffset = offset;
|
||||
this._shiftActor();
|
||||
|
@ -448,7 +448,7 @@ const Calendar = new Lang.Class({
|
||||
}
|
||||
|
||||
// All the children after this are days, and get removed when we update the calendar
|
||||
this._firstDayIndex = this.actor.get_children().length;
|
||||
this._firstDayIndex = this.actor.get_n_children();
|
||||
},
|
||||
|
||||
_onStyleChange: function(actor, event) {
|
||||
@ -551,6 +551,7 @@ const Calendar = new Lang.Class({
|
||||
let row = 2;
|
||||
while (true) {
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
if (!this._eventSource)
|
||||
button.reactive = false;
|
||||
@ -571,7 +572,10 @@ const Calendar = new Lang.Class({
|
||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||
if (row == 2)
|
||||
styleClass = 'calendar-day-top ' + styleClass;
|
||||
if (iter.getDay() == this._weekStart)
|
||||
|
||||
let leftMost = rtl ? iter.getDay() == (this._weekStart + 6) % 7
|
||||
: iter.getDay() == this._weekStart;
|
||||
if (leftMost)
|
||||
styleClass = 'calendar-day-left ' + styleClass;
|
||||
|
||||
if (_sameDay(now, iter))
|
||||
|
@ -1,196 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Folks = imports.gi.Folks
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const Util = imports.misc.util;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
const ICON_SIZE = 81;
|
||||
|
||||
function launchContact(id) {
|
||||
Util.spawn(['gnome-contacts', '-i', id]);
|
||||
}
|
||||
|
||||
|
||||
/* This class represents a shown contact search result in the overview */
|
||||
const Contact = new Lang.Class({
|
||||
Name: 'Contact',
|
||||
|
||||
_init: function(id) {
|
||||
this._contactSys = Shell.ContactSystem.get_default();
|
||||
this.individual = this._contactSys.get_individual(id);
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'contact',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
accessible_role: Atk.Role.PUSH_BUTTON });
|
||||
|
||||
let content = new St.BoxLayout( { style_class: 'contact-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: ICON_SIZE,
|
||||
style_class: 'contact-icon' });
|
||||
if (this.individual.avatar != null)
|
||||
icon.gicon = this.individual.avatar;
|
||||
else
|
||||
icon.icon_name = 'avatar-default';
|
||||
|
||||
content.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let details = new St.BoxLayout({ style_class: 'contact-details',
|
||||
vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let email = this._contactSys.get_email_for_display(this.individual);
|
||||
let aliasText = this.individual.alias ||
|
||||
this.individual.full_name ||
|
||||
this.individual.nickname ||
|
||||
email ||
|
||||
_("Unknown");
|
||||
let aliasLabel = new St.Label({ text: aliasText,
|
||||
style_class: 'contact-details-alias' });
|
||||
details.add(aliasLabel, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this.actor.label_actor = aliasLabel;
|
||||
|
||||
let presence = this._createPresence(this.individual.presence_type);
|
||||
details.add(presence, { x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
},
|
||||
|
||||
_createPresence: function(presence) {
|
||||
let text;
|
||||
let iconName;
|
||||
|
||||
switch(presence) {
|
||||
case Folks.PresenceType.AVAILABLE:
|
||||
text = _("Available");
|
||||
iconName = 'user-available';
|
||||
break;
|
||||
case Folks.PresenceType.AWAY:
|
||||
case Folks.PresenceType.EXTENDED_AWAY:
|
||||
text = _("Away");
|
||||
iconName = 'user-away';
|
||||
break;
|
||||
case Folks.PresenceType.BUSY:
|
||||
text = _("Busy");
|
||||
iconName = 'user-busy';
|
||||
break;
|
||||
case Folks.PresenceType.OFFLINE:
|
||||
text = _("Offline");
|
||||
iconName = 'user-offline';
|
||||
break;
|
||||
default:
|
||||
text = '';
|
||||
iconName = null;
|
||||
}
|
||||
|
||||
let box = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'contact-details-status' });
|
||||
|
||||
if (iconName) {
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: 16,
|
||||
style_class: 'contact-details-status-icon' });
|
||||
box.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
}
|
||||
|
||||
let label = new St.Label({ text: text });
|
||||
|
||||
box.add(label, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
return box;
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
let tc = St.TextureCache.get_default();
|
||||
let icon = this.individual.avatar;
|
||||
|
||||
if (icon != null) {
|
||||
return tc.load_gicon(null, icon, size);
|
||||
} else {
|
||||
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/* Searches for and returns contacts */
|
||||
const ContactSearchProvider = new Lang.Class({
|
||||
Name: 'ContactSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("CONTACTS"));
|
||||
this._contactSys = Shell.ContactSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMetas: function(ids) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
let contact = new Contact(ids[i]);
|
||||
metas.push({ 'id': ids[i],
|
||||
'name': contact.alias,
|
||||
'createIcon': function(size) {
|
||||
return contact.createIcon(size);
|
||||
}
|
||||
});
|
||||
}
|
||||
return metas;
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._contactSys.initial_search(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._contactSys.subsearch(previousResults, terms);
|
||||
},
|
||||
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
let contact = new Contact(resultMeta.id);
|
||||
return contact.actor;
|
||||
},
|
||||
|
||||
createResultContainerActor: function() {
|
||||
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
grid.actor.style_class = 'contact-grid';
|
||||
|
||||
let actor = new SearchDisplay.GridSearchResults(this, grid);
|
||||
return actor;
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
launchContact(id);
|
||||
}
|
||||
});
|
@ -133,7 +133,6 @@ const DashItemContainer = new Lang.Class({
|
||||
},
|
||||
|
||||
hideLabel: function () {
|
||||
this.label.opacity = 255;
|
||||
Tweener.addTween(this.label,
|
||||
{ opacity: 0,
|
||||
time: DASH_ITEM_LABEL_HIDE_TIME,
|
||||
@ -168,7 +167,17 @@ const DashItemContainer = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.label)
|
||||
this.label.destroy();
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
animateOutAndDestroy: function() {
|
||||
if (this.label)
|
||||
this.label.destroy();
|
||||
|
||||
if (this.child == null) {
|
||||
this.actor.destroy();
|
||||
return;
|
||||
@ -449,6 +458,13 @@ const Dash = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._onHover(item, display)
|
||||
}));
|
||||
|
||||
Main.overview.connect('hiding',
|
||||
Lang.bind(this, function() {
|
||||
this._labelShowing = false;
|
||||
item.hideLabel();
|
||||
}));
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
@ -464,7 +480,7 @@ const Dash = new Lang.Class({
|
||||
}));
|
||||
if (this._resetHoverTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||||
this._resetHoverTimeoutId = 0;
|
||||
this._resetHoverTimeoutId = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -691,7 +707,7 @@ const Dash = new Lang.Class({
|
||||
if (Main.overview.visible)
|
||||
item.animateOutAndDestroy();
|
||||
else
|
||||
item.actor.destroy();
|
||||
item.destroy();
|
||||
}
|
||||
|
||||
this._adjustIconSize();
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Cairo = imports.cairo;
|
||||
@ -16,14 +17,6 @@ const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
// in org.gnome.shell.clock
|
||||
const CLOCK_SHOW_DATE_KEY = 'show-date';
|
||||
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
function _onVertSepRepaint (area)
|
||||
{
|
||||
@ -45,9 +38,7 @@ const DateMenuButton = new Lang.Class({
|
||||
Name: 'DateMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { showEvents: true });
|
||||
|
||||
_init: function() {
|
||||
let item;
|
||||
let hbox;
|
||||
let vbox;
|
||||
@ -62,8 +53,8 @@ const DateMenuButton = new Lang.Class({
|
||||
// role ATK_ROLE_MENU like other elements of the panel.
|
||||
this.actor.accessible_role = Atk.Role.LABEL;
|
||||
|
||||
this._clock = new St.Label();
|
||||
this.actor.add_actor(this._clock);
|
||||
this._clockDisplay = new St.Label();
|
||||
this.actor.add_actor(this._clockDisplay);
|
||||
|
||||
hbox = new St.BoxLayout({name: 'calendarArea' });
|
||||
this.menu.addActor(hbox);
|
||||
@ -75,11 +66,11 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
// Date
|
||||
this._date = new St.Label();
|
||||
this.actor.label_actor = this._date;
|
||||
this.actor.label_actor = this._clockDisplay;
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
if (params.showEvents) {
|
||||
if (Main.sessionMode.showCalendarEvents) {
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
} else {
|
||||
@ -110,7 +101,7 @@ const DateMenuButton = new Lang.Class({
|
||||
item.actor.reparent(vbox);
|
||||
}
|
||||
|
||||
if (params.showEvents) {
|
||||
if (Main.sessionMode.showCalendarEvents) {
|
||||
// Add vertical separator
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
@ -157,77 +148,29 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
// Track changes to clock settings
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// Start the clock
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
||||
this._updateClockAndDate();
|
||||
},
|
||||
|
||||
_updateClockAndDate: function() {
|
||||
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
|
||||
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
|
||||
|
||||
let clockFormat;
|
||||
let dateFormat;
|
||||
|
||||
switch (format) {
|
||||
case '24h':
|
||||
if (showDate)
|
||||
/* Translators: This is the time format with date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||
: _("%a %b %e, %R");
|
||||
else
|
||||
/* Translators: This is the time format without date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %R:%S")
|
||||
: _("%a %R");
|
||||
break;
|
||||
case '12h':
|
||||
default:
|
||||
if (showDate)
|
||||
/* Translators: This is a time format with date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||
: _("%a %b %e, %l:%M %p");
|
||||
else
|
||||
/* Translators: This is a time format without date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||
: _("%a %l:%M %p");
|
||||
break;
|
||||
}
|
||||
|
||||
let displayDate = new Date();
|
||||
|
||||
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
|
||||
|
||||
this._clockDisplay.set_text(this._clock.clock);
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
dateFormat = _("%A %B %e, %Y");
|
||||
let dateFormat = _("%A %B %e, %Y");
|
||||
let displayDate = new Date();
|
||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||
|
||||
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
|
||||
return false;
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
let tool = calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool == 'evolution') {
|
||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
Util.spawn(['evolution', '-c', 'calendar']);
|
||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||
app.activate();
|
||||
} else {
|
||||
let needTerm = calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
|
@ -31,7 +31,6 @@ const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -280,21 +279,17 @@ const EndSessionDialog = new Lang.Class({
|
||||
scrollView.hide();
|
||||
|
||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(this._applicationList,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
scrollView.add_actor(this._applicationList);
|
||||
|
||||
this._applicationList.connect('actor-added',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 1)
|
||||
if (this._applicationList.get_n_children() == 1)
|
||||
scrollView.show();
|
||||
}));
|
||||
|
||||
this._applicationList.connect('actor-removed',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 0)
|
||||
if (this._applicationList.get_n_children() == 0)
|
||||
scrollView.hide();
|
||||
}));
|
||||
|
||||
@ -342,7 +337,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateContent: function() {
|
||||
_updateDescription: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
@ -352,17 +347,6 @@ const EndSessionDialog = new Lang.Class({
|
||||
let subject = dialogContent.subject;
|
||||
let description;
|
||||
|
||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
||||
else
|
||||
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
|
||||
} else if (dialogContent.iconName) {
|
||||
this._setIconFromName(dialogContent.iconName,
|
||||
dialogContent.iconStyleClass);
|
||||
}
|
||||
|
||||
if (this._inhibitors.length > 0) {
|
||||
this._stopTimer();
|
||||
description = dialogContent.inhibitedDescription;
|
||||
@ -395,6 +379,27 @@ const EndSessionDialog = new Lang.Class({
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
},
|
||||
|
||||
_updateContent: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
||||
else
|
||||
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
|
||||
} else if (dialogContent.iconName) {
|
||||
this._setIconFromName(dialogContent.iconName,
|
||||
dialogContent.iconStyleClass);
|
||||
}
|
||||
|
||||
this._updateDescription();
|
||||
},
|
||||
|
||||
_updateButtons: function() {
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||
@ -441,7 +446,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
{ _secondsLeft: 0,
|
||||
time: this._secondsLeft,
|
||||
transition: 'linear',
|
||||
onUpdate: Lang.bind(this, this._updateContent),
|
||||
onUpdate: Lang.bind(this, this._updateDescription),
|
||||
onComplete: Lang.bind(this, function() {
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||
|
@ -39,11 +39,19 @@ function _patchContainerClass(containerClass) {
|
||||
};
|
||||
}
|
||||
|
||||
function _makeLoggingFunc(func) {
|
||||
return function() {
|
||||
return func([].join.call(arguments, ', '));
|
||||
};
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Add some bindings to the global JS namespace; (gjs keeps the web
|
||||
// browser convention of having that namespace be called 'window'.)
|
||||
window.global = Shell.Global.get();
|
||||
|
||||
window.log = _makeLoggingFunc(window.log);
|
||||
|
||||
window._ = Gettext.gettext;
|
||||
window.C_ = Gettext.pgettext;
|
||||
window.ngettext = Gettext.ngettext;
|
||||
@ -82,7 +90,7 @@ function init() {
|
||||
}
|
||||
|
||||
// OK, now things are initialized enough that we can import shell JS
|
||||
const Format = imports.misc.format;
|
||||
const Format = imports.format;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
Tweener.init();
|
||||
|
270
js/ui/extensionDownloader.js
Normal file
@ -0,0 +1,270 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Soup = imports.gi.Soup;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const _signals = ExtensionSystem._signals;
|
||||
|
||||
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
||||
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
||||
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||
const REPOSITORY_URL_UPDATE = REPOSITORY_URL_BASE + '/update-info/';
|
||||
|
||||
let _httpSession;
|
||||
|
||||
function installExtension(uuid, invocation) {
|
||||
let params = { uuid: uuid,
|
||||
shell_version: Config.PACKAGE_VERSION };
|
||||
|
||||
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
|
||||
|
||||
_httpSession.queue_message(message, function(session, message) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code);
|
||||
invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let info;
|
||||
try {
|
||||
info = JSON.parse(message.response_body.data);
|
||||
} catch (e) {
|
||||
ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e);
|
||||
invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let dialog = new InstallExtensionDialog(uuid, info, invocation);
|
||||
dialog.open(global.get_current_time());
|
||||
});
|
||||
}
|
||||
|
||||
function uninstallExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
// Don't try to uninstall system extensions
|
||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||
return false;
|
||||
|
||||
if (!ExtensionSystem.unloadExtension(uuid))
|
||||
return false;
|
||||
|
||||
FileUtils.recursivelyDeleteDir(extension.dir, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
errback('DownloadExtensionError', message.status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!dir.query_exists(null))
|
||||
dir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
errback('CreateExtensionDirectoryError', e);
|
||||
return;
|
||||
}
|
||||
|
||||
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
|
||||
let contents = message.response_body.flatten().as_bytes();
|
||||
stream.output_stream.write_bytes(contents, null);
|
||||
stream.close(null);
|
||||
let [success, pid] = GLib.spawn_async(null,
|
||||
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
|
||||
null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
|
||||
if (!success) {
|
||||
errback('ExtractExtensionError');
|
||||
return;
|
||||
}
|
||||
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||
GLib.spawn_close_pid(pid);
|
||||
|
||||
if (status != 0)
|
||||
errback('ExtractExtensionError');
|
||||
else
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function updateExtension(uuid) {
|
||||
// This gets a bit tricky. We want the update to be seamless -
|
||||
// if we have any error during downloading or extracting, we
|
||||
// want to not unload the current version.
|
||||
|
||||
let oldExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
|
||||
let newExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
|
||||
|
||||
let params = { shell_version: Config.PACKAGE_VERSION };
|
||||
|
||||
let url = REPOSITORY_URL_DOWNLOAD.format(uuid);
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
|
||||
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, function() {
|
||||
let oldExtension = ExtensionUtils.extensions[uuid];
|
||||
let extensionDir = oldExtension.dir;
|
||||
|
||||
if (!ExtensionSystem.unloadExtension(uuid))
|
||||
return;
|
||||
|
||||
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
|
||||
FileUtils.recursivelyMoveDir(newExtensionTmpDir, extensionDir);
|
||||
|
||||
let extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
|
||||
|
||||
try {
|
||||
ExtensionSystem.loadExtension(extension);
|
||||
} catch(e) {
|
||||
ExtensionSystem.unloadExtension(uuid);
|
||||
|
||||
logError(e, 'Error loading extension %s'.format(uuid));
|
||||
|
||||
FileUtils.recursivelyDeleteDir(extensionDir, false);
|
||||
FileUtils.recursivelyMoveDir(oldExtensionTmpDir, extensionDir);
|
||||
|
||||
// Restore what was there before. We can't do much if we
|
||||
// fail here.
|
||||
ExtensionSystem.loadExtension(oldExtension);
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true);
|
||||
}, function(code, message) {
|
||||
log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : ''));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function checkForUpdates() {
|
||||
let metadatas = {};
|
||||
for (let uuid in ExtensionUtils.extensions) {
|
||||
metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata;
|
||||
}
|
||||
|
||||
let params = { shell_version: Config.PACKAGE_VERSION,
|
||||
installed: JSON.stringify(metadatas) };
|
||||
|
||||
let url = REPOSITORY_URL_UPDATE;
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
_httpSession.queue_message(message, function(session, message) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK)
|
||||
return;
|
||||
|
||||
let operations = JSON.parse(message.response_body.data);
|
||||
for (let uuid in operations) {
|
||||
let operation = operations[uuid];
|
||||
if (operation == 'blacklist')
|
||||
uninstallExtension(uuid);
|
||||
else if (operation == 'upgrade' || operation == 'downgrade')
|
||||
updateExtension(uuid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const InstallExtensionDialog = new Lang.Class({
|
||||
Name: 'InstallExtensionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(uuid, info, invocation) {
|
||||
this.parent({ styleClass: 'extension-dialog' });
|
||||
|
||||
this._uuid = uuid;
|
||||
this._info = info;
|
||||
this._invocation = invocation;
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this._onCancelButtonPressed),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Install"),
|
||||
action: Lang.bind(this, this._onInstallButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
|
||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
|
||||
|
||||
let box = new St.BoxLayout();
|
||||
this.contentLayout.add(box);
|
||||
|
||||
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
|
||||
let icon = new St.Icon({ gicon: gicon });
|
||||
box.add(icon);
|
||||
|
||||
let label = new St.Label({ text: message });
|
||||
box.add(label);
|
||||
},
|
||||
|
||||
_onCancelButtonPressed: function(button, event) {
|
||||
this.close(global.get_current_time());
|
||||
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||
},
|
||||
|
||||
_onInstallButtonPressed: function(button, event) {
|
||||
let params = { shell_version: Config.PACKAGE_VERSION };
|
||||
|
||||
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
|
||||
let uuid = this._uuid;
|
||||
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
|
||||
let invocation = this._invocation;
|
||||
function errback(code, message) {
|
||||
invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
|
||||
}
|
||||
|
||||
function callback() {
|
||||
// Add extension to 'enabled-extensions' for the user, always...
|
||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1) {
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
}
|
||||
|
||||
let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
|
||||
|
||||
try {
|
||||
ExtensionSystem.loadExtension(extension);
|
||||
} catch(e) {
|
||||
uninstallExtension(uuid);
|
||||
errback('LoadExtensionError', e);
|
||||
return;
|
||||
}
|
||||
|
||||
invocation.return_value(GLib.Variant.new('(s)', 'successful'));
|
||||
}
|
||||
|
||||
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
||||
}));
|
||||
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
||||
function init() {
|
||||
_httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
|
||||
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
|
||||
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||
}
|
@ -3,19 +3,11 @@
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Soup = imports.gi.Soup;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const API_VERSION = 1;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
@ -30,29 +22,6 @@ const ExtensionState = {
|
||||
UNINSTALLED: 99
|
||||
};
|
||||
|
||||
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
||||
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
||||
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||
|
||||
const _httpSession = new Soup.SessionAsync();
|
||||
|
||||
// The unfortunate state of gjs, gobject-introspection and libsoup
|
||||
// means that I have to do a hack to add a feature.
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||
|
||||
if (Soup.Session.prototype.add_feature != null)
|
||||
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||
|
||||
function _getCertFile() {
|
||||
let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']);
|
||||
if (GLib.file_test(localCert, GLib.FileTest.EXISTS))
|
||||
return localCert;
|
||||
else
|
||||
return Config.SHELL_SYSTEM_CA_FILE;
|
||||
}
|
||||
|
||||
_httpSession.ssl_ca_file = _getCertFile();
|
||||
|
||||
// Arrays of uuids
|
||||
var enabledExtensions;
|
||||
// Contains the order that extensions were enabled in.
|
||||
@ -69,90 +38,6 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
|
||||
function installExtensionFromUUID(uuid, version_tag) {
|
||||
let params = { uuid: uuid,
|
||||
version_tag: version_tag,
|
||||
shell_version: Config.PACKAGE_VERSION,
|
||||
api_version: API_VERSION.toString() };
|
||||
|
||||
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
|
||||
|
||||
_httpSession.queue_message(message,
|
||||
function(session, message) {
|
||||
let info = JSON.parse(message.response_body.data);
|
||||
let dialog = new InstallExtensionDialog(uuid, version_tag, info.name);
|
||||
dialog.open(global.get_current_time());
|
||||
});
|
||||
}
|
||||
|
||||
function uninstallExtensionFromUUID(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||
// but it will be removed on next reboot, and hopefully nothing
|
||||
// broke too much.
|
||||
disableExtension(uuid);
|
||||
|
||||
// Don't try to uninstall system extensions
|
||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||
return false;
|
||||
|
||||
extension.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
|
||||
delete ExtensionUtils.extensions[uuid];
|
||||
|
||||
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function gotExtensionZipFile(session, message, uuid) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: use a GFile mkstemp-type method once one exists
|
||||
let fd, tmpzip;
|
||||
try {
|
||||
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'tempfile: ' + e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let stream = new Gio.UnixOutputStream({ fd: fd });
|
||||
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
|
||||
Shell.write_soup_message_to_stream(stream, message);
|
||||
stream.close(null);
|
||||
let [success, pid] = GLib.spawn_async(null,
|
||||
['unzip', '-uod', dir.get_path(), '--', tmpzip],
|
||||
null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
|
||||
if (!success) {
|
||||
logExtensionError(uuid, 'extract: could not extract');
|
||||
return;
|
||||
}
|
||||
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||
GLib.spawn_close_pid(pid);
|
||||
|
||||
// Add extension to 'enabled-extensions' for the user, always...
|
||||
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1) {
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
}
|
||||
|
||||
loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
|
||||
});
|
||||
}
|
||||
|
||||
function disableExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
@ -178,23 +63,23 @@ function disableExtension(uuid) {
|
||||
try {
|
||||
ExtensionUtils.extensions[uuid].stateObj.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
extension.stateObj.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
if (extension.stylesheet) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||
}
|
||||
|
||||
extension.stateObj.disable();
|
||||
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
let uuid = order[i];
|
||||
try {
|
||||
ExtensionUtils.extensions[uuid].stateObj.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,67 +102,77 @@ function enableExtension(uuid) {
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
try {
|
||||
extension.stateObj.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
extension.stateObj.enable();
|
||||
|
||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
extension.stylesheet = stylesheetFile;
|
||||
}
|
||||
|
||||
extension.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function logExtensionError(uuid, message, state) {
|
||||
function logExtensionError(uuid, error) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
let message = '' + error;
|
||||
|
||||
if (error.state)
|
||||
extension.state = error.state;
|
||||
else
|
||||
extension.state = ExtensionState.ERROR;
|
||||
|
||||
if (!extension.errors)
|
||||
extension.errors = [];
|
||||
|
||||
extension.errors.push(message);
|
||||
global.logError('Extension "%s" had error: %s'.format(uuid, message));
|
||||
state = state || ExtensionState.ERROR;
|
||||
log('Extension "%s" had error: %s'.format(uuid, message));
|
||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||
error: message,
|
||||
state: state });
|
||||
state: extension.state });
|
||||
}
|
||||
|
||||
function loadExtension(dir, type, enabled) {
|
||||
let uuid = dir.get_basename();
|
||||
let extension;
|
||||
|
||||
if (ExtensionUtils.extensions[uuid] != undefined) {
|
||||
throw new Error('extension already loaded');
|
||||
}
|
||||
|
||||
try {
|
||||
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
function loadExtension(extension) {
|
||||
// Default to error, we set success as the last step
|
||||
extension.state = ExtensionState.ERROR;
|
||||
|
||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
||||
extension.state = ExtensionState.OUT_OF_DATE;
|
||||
return;
|
||||
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version');
|
||||
error.state = ExtensionState.OUT_OF_DATE;
|
||||
throw error;
|
||||
}
|
||||
|
||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||
if (enabled) {
|
||||
initExtension(uuid);
|
||||
initExtension(extension.uuid);
|
||||
if (extension.state == ExtensionState.DISABLED)
|
||||
enableExtension(uuid);
|
||||
enableExtension(extension.uuid);
|
||||
} else {
|
||||
extension.state = ExtensionState.INITIALIZED;
|
||||
}
|
||||
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
global.log('Loaded extension ' + uuid);
|
||||
}
|
||||
|
||||
function unloadExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||
// but it will be removed on next reboot, and hopefully nothing
|
||||
// broke too much.
|
||||
disableExtension(uuid);
|
||||
|
||||
extension.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
|
||||
delete ExtensionUtils.extensions[uuid];
|
||||
return true;
|
||||
}
|
||||
|
||||
function initExtension(uuid) {
|
||||
@ -288,64 +183,24 @@ function initExtension(uuid) {
|
||||
throw new Error("Extension was not properly created. Call loadExtension first");
|
||||
|
||||
let extensionJs = dir.get_child('extension.js');
|
||||
if (!extensionJs.query_exists(null)) {
|
||||
logExtensionError(uuid, 'Missing extension.js');
|
||||
return;
|
||||
}
|
||||
let stylesheetPath = null;
|
||||
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||
let theme = themeContext.get_theme();
|
||||
let stylesheetFile = dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
try {
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!extensionJs.query_exists(null))
|
||||
throw new Error('Missing extension.js');
|
||||
|
||||
let extensionModule;
|
||||
let extensionState = null;
|
||||
try {
|
||||
ExtensionUtils.installImporter(extension);
|
||||
extensionModule = extension.imports.extension;
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
logExtensionError(uuid, '' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extensionModule.init) {
|
||||
logExtensionError(uuid, 'missing \'init\' function');
|
||||
return;
|
||||
}
|
||||
ExtensionUtils.installImporter(extension);
|
||||
extensionModule = extension.imports.extension;
|
||||
|
||||
try {
|
||||
if (extensionModule.init) {
|
||||
extensionState = extensionModule.init(extension);
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extensionState)
|
||||
extensionState = extensionModule;
|
||||
extension.stateObj = extensionState;
|
||||
|
||||
if (!extensionState.enable) {
|
||||
logExtensionError(uuid, 'missing \'enable\' function');
|
||||
return;
|
||||
}
|
||||
if (!extensionState.disable) {
|
||||
logExtensionError(uuid, 'missing \'disable\' function');
|
||||
return;
|
||||
}
|
||||
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
|
||||
_signals.emit('extension-loaded', uuid);
|
||||
}
|
||||
|
||||
@ -357,7 +212,11 @@ function onEnabledExtensionsChanged() {
|
||||
newEnabledExtensions.filter(function(uuid) {
|
||||
return enabledExtensions.indexOf(uuid) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
enableExtension(uuid);
|
||||
try {
|
||||
enableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||
@ -365,87 +224,27 @@ function onEnabledExtensionsChanged() {
|
||||
enabledExtensions.filter(function(item) {
|
||||
return newEnabledExtensions.indexOf(item) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
disableExtension(uuid);
|
||||
try {
|
||||
disableExtension(uuid);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e);
|
||||
}
|
||||
});
|
||||
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
}
|
||||
|
||||
function init() {
|
||||
ExtensionUtils.init();
|
||||
|
||||
function loadExtensions() {
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
}
|
||||
|
||||
function loadExtensions() {
|
||||
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
|
||||
let enabled = enabledExtensions.indexOf(uuid) != -1;
|
||||
loadExtension(dir, type, enabled);
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', function(signals, extension) {
|
||||
try {
|
||||
loadExtension(extension);
|
||||
} catch(e) {
|
||||
logExtensionError(extension.uuid, e);
|
||||
}
|
||||
});
|
||||
finder.scanExtensions();
|
||||
}
|
||||
|
||||
const InstallExtensionDialog = new Lang.Class({
|
||||
Name: 'InstallExtensionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(uuid, version_tag, name) {
|
||||
this.parent({ styleClass: 'extension-dialog' });
|
||||
|
||||
this._uuid = uuid;
|
||||
this._version_tag = version_tag;
|
||||
this._name = name;
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this._onCancelButtonPressed),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Install"),
|
||||
action: Lang.bind(this, this._onInstallButtonPressed)
|
||||
}]);
|
||||
|
||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(name);
|
||||
|
||||
this._descriptionLabel = new St.Label({ text: message });
|
||||
|
||||
this.contentLayout.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
},
|
||||
|
||||
_onCancelButtonPressed: function(button, event) {
|
||||
this.close(global.get_current_time());
|
||||
|
||||
// Even though the extension is already "uninstalled", send through
|
||||
// a state-changed signal for any users who want to know if the install
|
||||
// went through correctly -- using proper async DBus would block more
|
||||
// traditional clients like the plugin
|
||||
let meta = { uuid: this._uuid,
|
||||
state: ExtensionState.UNINSTALLED,
|
||||
error: '' };
|
||||
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
},
|
||||
|
||||
_onInstallButtonPressed: function(button, event) {
|
||||
let state = { uuid: this._uuid,
|
||||
state: ExtensionState.DOWNLOADING,
|
||||
error: '' };
|
||||
|
||||
_signals.emit('extension-state-changed', state);
|
||||
|
||||
let params = { version_tag: this._version_tag,
|
||||
shell_version: Config.PACKAGE_VERSION,
|
||||
api_version: API_VERSION.toString() };
|
||||
|
||||
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
|
||||
let message = Soup.form_request_new_from_hash('GET', url, params);
|
||||
|
||||
_httpSession.queue_message(message,
|
||||
Lang.bind(this, function(session, message) {
|
||||
gotExtensionZipFile(session, message, this._uuid);
|
||||
}));
|
||||
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
228
js/ui/ibusCandidatePopup.js
Normal file
@ -0,0 +1,228 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const IBus = imports.gi.IBus;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const MAX_CANDIDATES_PER_PAGE = 16;
|
||||
|
||||
const CandidateArea = new Lang.Class({
|
||||
Name: 'CandidateArea',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ reactive: false });
|
||||
|
||||
// St.Table exhibits some sizing problems so let's go with a
|
||||
// clutter layout manager for now.
|
||||
this._table = new Clutter.Actor();
|
||||
this.addActor(this._table);
|
||||
|
||||
this._tableLayout = new Clutter.TableLayout();
|
||||
this._table.set_layout_manager(this._tableLayout);
|
||||
|
||||
this._indexLabels = [];
|
||||
this._candidateLabels = [];
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
|
||||
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
|
||||
}
|
||||
|
||||
this._orientation = -1;
|
||||
this._cursorPosition = 0;
|
||||
},
|
||||
|
||||
_setOrientation: function(orientation) {
|
||||
if (this._orientation == orientation)
|
||||
return;
|
||||
|
||||
this._orientation = orientation;
|
||||
|
||||
this._table.remove_all_children();
|
||||
|
||||
if (this._orientation == IBus.Orientation.HORIZONTAL)
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
|
||||
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
|
||||
}
|
||||
else // VERTICAL || SYSTEM
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._tableLayout.pack(this._indexLabels[i], 0, i);
|
||||
this._tableLayout.pack(this._candidateLabels[i], 1, i);
|
||||
}
|
||||
},
|
||||
|
||||
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
|
||||
this._setOrientation(orientation);
|
||||
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
let visible = i < candidates.length;
|
||||
this._indexLabels[i].visible = visible;
|
||||
this._candidateLabels[i].visible = visible;
|
||||
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
|
||||
this._candidateLabels[i].text = candidates[i];
|
||||
}
|
||||
|
||||
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
|
||||
this._cursorPosition = cursorPosition;
|
||||
if (cursorVisible)
|
||||
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
|
||||
},
|
||||
});
|
||||
|
||||
const CandidatePopup = new Lang.Class({
|
||||
Name: 'CandidatePopup',
|
||||
Extends: PopupMenu.PopupMenu,
|
||||
|
||||
_init: function() {
|
||||
this._cursor = new St.Bin({ opacity: 0 });
|
||||
Main.uiGroup.add_actor(this._cursor);
|
||||
|
||||
this.parent(this._cursor, 0, St.Side.TOP);
|
||||
this.actor.hide();
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
|
||||
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._preeditTextItem.actor.hide();
|
||||
this.addMenuItem(this._preeditTextItem);
|
||||
|
||||
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._auxTextItem.actor.hide();
|
||||
this.addMenuItem(this._auxTextItem);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._lookupTableItem = new CandidateArea();
|
||||
this._lookupTableItem.actor.hide();
|
||||
this.addMenuItem(this._lookupTableItem);
|
||||
|
||||
this._panelService = null;
|
||||
},
|
||||
|
||||
setPanelService: function(panelService) {
|
||||
this._panelService = panelService;
|
||||
if (!panelService)
|
||||
return;
|
||||
|
||||
panelService.connect('set-cursor-location',
|
||||
Lang.bind(this, function(ps, x, y, w, h) {
|
||||
this._cursor.set_position(x, y);
|
||||
this._cursor.set_size(w, h);
|
||||
}));
|
||||
panelService.connect('update-preedit-text',
|
||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||
if (visible)
|
||||
this._preeditTextItem.actor.show();
|
||||
else
|
||||
this._preeditTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
this._preeditTextItem.actor.label_actor.text = text.get_text();
|
||||
|
||||
let attrs = text.get_attributes();
|
||||
if (attrs)
|
||||
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
|
||||
attrs);
|
||||
}));
|
||||
panelService.connect('show-preedit-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._preeditTextItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-preedit-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._preeditTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('update-auxiliary-text',
|
||||
Lang.bind(this, function(ps, text, visible) {
|
||||
if (visible)
|
||||
this._auxTextItem.actor.show();
|
||||
else
|
||||
this._auxTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
this._auxTextItem.actor.label_actor.text = text.get_text();
|
||||
}));
|
||||
panelService.connect('show-auxiliary-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._auxTextItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-auxiliary-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._auxTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('update-lookup-table',
|
||||
Lang.bind(this, function(ps, lookupTable, visible) {
|
||||
if (visible)
|
||||
this._lookupTableItem.actor.show();
|
||||
else
|
||||
this._lookupTableItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
let cursorPos = lookupTable.get_cursor_pos();
|
||||
let pageSize = lookupTable.get_page_size();
|
||||
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
||||
let startIndex = page * pageSize;
|
||||
let endIndex = Math.min((page + 1) * pageSize,
|
||||
lookupTable.get_number_of_candidates());
|
||||
let indexes = [];
|
||||
let indexLabel;
|
||||
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
||||
indexes.push(indexLabel.get_text());
|
||||
|
||||
let candidates = [];
|
||||
for (let i = startIndex; i < endIndex; ++i)
|
||||
candidates.push(lookupTable.get_candidate(i).get_text());
|
||||
|
||||
this._lookupTableItem.setCandidates(indexes,
|
||||
candidates,
|
||||
lookupTable.get_orientation(),
|
||||
cursorPos % pageSize,
|
||||
lookupTable.is_cursor_visible());
|
||||
}));
|
||||
panelService.connect('show-lookup-table',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._lookupTableItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-lookup-table',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._lookupTableItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('focus-out',
|
||||
Lang.bind(this, function(ps) {
|
||||
this.close(BoxPointer.PopupAnimation.NONE);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let isVisible = (this._preeditTextItem.actor.visible ||
|
||||
this._auxTextItem.actor.visible ||
|
||||
this._lookupTableItem.actor.visible);
|
||||
|
||||
if (isVisible)
|
||||
this.open(BoxPointer.PopupAnimation.NONE);
|
||||
else
|
||||
this.close(BoxPointer.PopupAnimation.NONE);
|
||||
},
|
||||
|
||||
_setTextAttributes: function(clutterText, ibusAttrList) {
|
||||
let attr;
|
||||
for (let i = 0; attr = ibusAttrList.get(i); ++i)
|
||||
if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
|
||||
clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
|
||||
}
|
||||
});
|
@ -146,11 +146,6 @@ const BaseIcon = new Lang.Class({
|
||||
size = found ? len : ICON_SIZE;
|
||||
}
|
||||
|
||||
// don't create icons unnecessarily
|
||||
if (size == this.iconSize &&
|
||||
this._iconBin.child)
|
||||
return;
|
||||
|
||||
this._createIconTexture(size);
|
||||
}
|
||||
});
|
||||
@ -287,6 +282,10 @@ const IconGrid = new Lang.Class({
|
||||
return this._computeLayout(rowWidth)[0];
|
||||
},
|
||||
|
||||
getRowLimit: function() {
|
||||
return this._rowLimit;
|
||||
},
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
@ -310,21 +309,22 @@ const IconGrid = new Lang.Class({
|
||||
this._grid.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function () {
|
||||
this._grid.get_children().forEach(Lang.bind(this, function (child) {
|
||||
child.destroy();
|
||||
}));
|
||||
removeAll: function() {
|
||||
this._grid.destroy_all_children();
|
||||
},
|
||||
|
||||
addItem: function(actor) {
|
||||
this._grid.add_actor(actor);
|
||||
addItem: function(actor, index) {
|
||||
if (index !== undefined)
|
||||
this._grid.insert_child_at_index(actor, index);
|
||||
else
|
||||
this._grid.add_actor(actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
return this._grid.get_children()[index];
|
||||
return this._grid.get_child_at_index(index);
|
||||
},
|
||||
|
||||
visibleItemsCount: function() {
|
||||
return this._grid.get_children().length - this._grid.get_n_skip_paint();
|
||||
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
|
||||
}
|
||||
});
|
||||
|
@ -93,7 +93,7 @@ const Key = new Lang.Class({
|
||||
this._getExtendedKeys();
|
||||
this.actor._extended_keys = this._extended_keyboard;
|
||||
this._boxPointer.actor.hide();
|
||||
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
|
||||
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||
}
|
||||
},
|
||||
|
||||
@ -175,7 +175,7 @@ const Key = new Lang.Class({
|
||||
this.actor.fake_release();
|
||||
this._boxPointer.actor.raise_top();
|
||||
this._boxPointer.setPosition(this.actor, 0.5);
|
||||
this._boxPointer.show(true);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
this.actor.set_hover(false);
|
||||
if (!this._grabbed) {
|
||||
Main.pushModal(this.actor);
|
||||
@ -186,7 +186,7 @@ const Key = new Lang.Class({
|
||||
} else {
|
||||
if (this._grabbed)
|
||||
this._ungrab();
|
||||
this._boxPointer.hide(true);
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -541,16 +541,8 @@ const KeyboardSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(keyboard) {
|
||||
this.parent(_("Keyboard"));
|
||||
this._keyboard = keyboard;
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'input-keyboard',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
this.parent(_("Keyboard"), 'input-keyboard', St.IconType.SYMBOLIC);
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
|
@ -65,7 +65,8 @@ const KeyringDialog = new Lang.Class({
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton)
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true
|
||||
}]
|
||||
|
||||
this.setButtons(buttons);
|
||||
|
103
js/ui/layout.js
@ -11,7 +11,6 @@ const St = imports.gi.St;
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
@ -33,26 +32,37 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._chrome = new Chrome(this);
|
||||
|
||||
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
|
||||
visible: false,
|
||||
clip_to_allocation: true,
|
||||
});
|
||||
this.addChrome(this.screenShieldGroup);
|
||||
|
||||
this.panelBox = new St.BoxLayout({ name: 'panelBox',
|
||||
vertical: true });
|
||||
this.addChrome(this.panelBox, { affectsStruts: true });
|
||||
this.addChrome(this.panelBox, { affectsStruts: true,
|
||||
trackFullscreen: true });
|
||||
this.panelBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updatePanelBarriers));
|
||||
|
||||
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
|
||||
this.addChrome(this.trayBox, { visibleInFullscreen: true });
|
||||
this.addChrome(this.trayBox);
|
||||
this.trayBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updateTrayBarrier));
|
||||
|
||||
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
|
||||
this.addChrome(this.keyboardBox);
|
||||
this._keyboardHeightNotifyId = 0;
|
||||
|
||||
global.screen.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
|
||||
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
|
||||
this.emit('primary-fullscreen-changed', state);
|
||||
}));
|
||||
},
|
||||
|
||||
// This is called by Main after everything else is constructed;
|
||||
@ -145,6 +155,9 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateBoxes: function() {
|
||||
this.screenShieldGroup.set_position(0, 0);
|
||||
this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
|
||||
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||
|
||||
@ -224,26 +237,9 @@ const LayoutManager = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
get focusIndex() {
|
||||
let focusWindow = global.display.focus_window;
|
||||
|
||||
if (focusWindow) {
|
||||
let wrect = focusWindow.get_outer_rect();
|
||||
for (let i = 0; i < this.monitors.length; i++) {
|
||||
let monitor = this.monitors[i];
|
||||
|
||||
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
|
||||
monitor.x + monitor.width > wrect.x &&
|
||||
monitor.y + monitor.height > wrect.y)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this.primaryIndex;
|
||||
},
|
||||
|
||||
get focusMonitor() {
|
||||
return this.monitors[this.focusIndex];
|
||||
get currentMonitor() {
|
||||
let index = global.screen.get_current_monitor();
|
||||
return Main.layoutManager.monitors[index];
|
||||
},
|
||||
|
||||
_startupAnimation: function() {
|
||||
@ -331,8 +327,10 @@ const LayoutManager = new Lang.Class({
|
||||
// the window manager struts. Changes to @actor's visibility will
|
||||
// NOT affect whether or not the strut is present, however.
|
||||
//
|
||||
// If %visibleInFullscreen in @params is %true, the actor will be
|
||||
// visible even when a fullscreen window should be covering it.
|
||||
// If %trackFullscreen in @params is %true, the actor's visibility
|
||||
// will be bound to the presence of fullscreen windows on the same
|
||||
// monitor (it will be hidden whenever a fullscreen window is visible,
|
||||
// and shown otherwise)
|
||||
addChrome: function(actor, params) {
|
||||
this._chrome.addActor(actor, params);
|
||||
},
|
||||
@ -346,10 +344,8 @@ const LayoutManager = new Lang.Class({
|
||||
// struts or input region to cover specific children.
|
||||
//
|
||||
// @params can have any of the same values as in addChrome(),
|
||||
// though some possibilities don't make sense (eg, trying to have
|
||||
// a %visibleInFullscreen child of a non-%visibleInFullscreen
|
||||
// parent). By default, @actor has the same params as its chrome
|
||||
// ancestor.
|
||||
// though some possibilities don't make sense. By default, @actor has
|
||||
// the same params as its chrome ancestor.
|
||||
trackChrome: function(actor, params) {
|
||||
this._chrome.trackActor(actor, params);
|
||||
},
|
||||
@ -555,7 +551,7 @@ const HotCorner = new Lang.Class({
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
trackFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
@ -568,6 +564,7 @@ const Chrome = new Lang.Class({
|
||||
|
||||
this._monitors = [];
|
||||
this._inOverview = false;
|
||||
this._isLocked = false;
|
||||
this._updateRegionIdle = 0;
|
||||
this._freezeUpdateCount = 0;
|
||||
|
||||
@ -582,16 +579,6 @@ const Chrome = new Lang.Class({
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
|
||||
this._screenSaverActive = false;
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, function(proxy, senderName, [isActive]) {
|
||||
this._onScreenSaverActiveChanged(isActive);
|
||||
}));
|
||||
this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) {
|
||||
if (!err)
|
||||
this._onScreenSaverActiveChanged(result[0]);
|
||||
}));
|
||||
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
@ -600,6 +587,8 @@ const Chrome = new Lang.Class({
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
Main.screenShield.connect('lock-status-changed',
|
||||
Lang.bind(this, this._lockStatusChanged));
|
||||
},
|
||||
|
||||
addActor: function(actor, params) {
|
||||
@ -694,19 +683,18 @@ const Chrome = new Lang.Class({
|
||||
_updateVisibility: function() {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i], visible;
|
||||
if (!actorData.trackFullscreen)
|
||||
continue;
|
||||
if (!actorData.isToplevel)
|
||||
continue;
|
||||
|
||||
if (this._screenSaverActive)
|
||||
visible = false;
|
||||
else if (this._inOverview)
|
||||
if (this._inOverview || this._isLocked)
|
||||
visible = true;
|
||||
else if (!actorData.visibleInFullscreen &&
|
||||
this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
visible = false;
|
||||
else
|
||||
visible = true;
|
||||
Main.uiGroup.set_skip_paint(actorData.actor, !visible);
|
||||
actorData.actor.visible = visible;
|
||||
}
|
||||
},
|
||||
|
||||
@ -722,6 +710,12 @@ const Chrome = new Lang.Class({
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_lockStatusChanged: function(shield, locked) {
|
||||
this._isLocked = locked;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_relayout: function() {
|
||||
this._monitors = this._layoutManager.monitors;
|
||||
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
||||
@ -731,12 +725,6 @@ const Chrome = new Lang.Class({
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_onScreenSaverActiveChanged: function(screenSaverActive) {
|
||||
this._screenSaverActive = screenSaverActive;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_findMonitorForRect: function(x, y, w, h) {
|
||||
// First look at what monitor the center of the rectangle is at
|
||||
let cx = x + w/2;
|
||||
@ -852,6 +840,8 @@ const Chrome = new Lang.Class({
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
@ -861,10 +851,15 @@ const Chrome = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
|
||||
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
|
||||
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
|
||||
}
|
||||
},
|
||||
|
||||
updateRegions: function() {
|
||||
@ -979,3 +974,5 @@ const Chrome = new Lang.Class({
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Signals.addSignalMethods(Chrome.prototype);
|
||||
|
@ -8,6 +8,8 @@ const St = imports.gi.St;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const DEFAULT_FADE_FACTOR = 0.4;
|
||||
|
||||
/**
|
||||
* Lightbox:
|
||||
* @container: parent Clutter.Container
|
||||
@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener;
|
||||
* - inhibitEvents: whether to inhibit events for @container
|
||||
* - width: shade actor width
|
||||
* - height: shade actor height
|
||||
* - fadeTime: seconds used to fade in/out
|
||||
* - fadeInTime: seconds used to fade in
|
||||
* - fadeOutTime: seconds used to fade out
|
||||
*
|
||||
* Lightbox creates a dark translucent "shade" actor to hide the
|
||||
* contents of @container, and allows you to specify particular actors
|
||||
@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({
|
||||
params = Params.parse(params, { inhibitEvents: false,
|
||||
width: null,
|
||||
height: null,
|
||||
fadeTime: null
|
||||
fadeInTime: null,
|
||||
fadeOutTime: null,
|
||||
fadeFactor: DEFAULT_FADE_FACTOR
|
||||
});
|
||||
|
||||
this._container = container;
|
||||
this._children = container.get_children();
|
||||
this._fadeTime = params.fadeTime;
|
||||
this._fadeInTime = params.fadeInTime;
|
||||
this._fadeOutTime = params.fadeOutTime;
|
||||
this._fadeFactor = params.fadeFactor;
|
||||
this.actor = new St.Bin({ x: 0,
|
||||
y: 0,
|
||||
style_class: 'lightbox',
|
||||
@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({
|
||||
container.add_actor(this.actor);
|
||||
this.actor.raise_top();
|
||||
this.actor.hide();
|
||||
this.shown = false;
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this._fadeTime) {
|
||||
if (this._fadeInTime) {
|
||||
this.shown = false;
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: this._fadeTime,
|
||||
transition: 'easeOutQuad'
|
||||
{ opacity: 255 * this._fadeFactor,
|
||||
time: this._fadeInTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.shown = true;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.opacity = 255;
|
||||
this.actor.opacity = 255 * this._fadeFactor;
|
||||
this.shown = true;
|
||||
}
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this._fadeTime) {
|
||||
this.shown = false;
|
||||
if (this._fadeOutTime) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: this._fadeTime,
|
||||
time: this._fadeOutTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.actor.hide();
|
||||
|
@ -1,21 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Link = new Lang.Class({
|
||||
Name: 'Link',
|
||||
|
||||
_init : function(props) {
|
||||
let realProps = { reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link' };
|
||||
// The user can pass in reactive: false to override the above and get
|
||||
// a non-reactive link (a link to the current page, perhaps)
|
||||
Lang.copyProperties(props, realProps);
|
||||
|
||||
this.actor = new St.Button(realProps);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Link.prototype);
|
@ -12,16 +12,18 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const System = imports.system;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Link = imports.ui.link;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
const JsParse = imports.misc.jsParse;
|
||||
|
||||
const CHEVRON = '>>> ';
|
||||
|
||||
/* Imports...feel free to add here as needed */
|
||||
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const GLib = imports.gi.GLib; ' +
|
||||
@ -38,7 +40,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const stage = global.stage; ' +
|
||||
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
|
||||
/* Special lookingGlass functions */
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
|
||||
const HISTORY_KEY = 'looking-glass-history';
|
||||
@ -261,9 +263,8 @@ function objectToString(o) {
|
||||
|
||||
const ObjLink = new Lang.Class({
|
||||
Name: 'ObjLink',
|
||||
Extends: Link.Link,
|
||||
|
||||
_init: function(o, title) {
|
||||
_init: function(lookingGlass, o, title) {
|
||||
let text;
|
||||
if (title)
|
||||
text = title;
|
||||
@ -272,24 +273,30 @@ const ObjLink = new Lang.Class({
|
||||
text = GLib.markup_escape_text(text, -1);
|
||||
this._obj = o;
|
||||
|
||||
this.parent({ label: text });
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: text });
|
||||
this.actor.get_child().single_line_mode = true;
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
_onClicked: function (link) {
|
||||
Main.lookingGlass.inspectObject(this._obj, this.actor);
|
||||
this._lookingGlass.inspectObject(this._obj, this.actor);
|
||||
}
|
||||
});
|
||||
|
||||
const Result = new Lang.Class({
|
||||
Name: 'Result',
|
||||
|
||||
_init : function(command, o, index) {
|
||||
_init: function(lookingGlass, command, o, index) {
|
||||
this.index = index;
|
||||
this.o = o;
|
||||
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this._lookingGlass = lookingGlass;
|
||||
|
||||
let cmdTxt = new St.Label({ text: command });
|
||||
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||
@ -299,7 +306,7 @@ const Result = new Lang.Class({
|
||||
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
|
||||
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||
box.add(resultTxt);
|
||||
let objLink = new ObjLink(o);
|
||||
let objLink = new ObjLink(this._lookingGlass, o);
|
||||
box.add(objLink.actor);
|
||||
let line = new Clutter.Rectangle({ name: 'Separator' });
|
||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
||||
@ -311,16 +318,18 @@ const Result = new Lang.Class({
|
||||
const WindowList = new Lang.Class({
|
||||
Name: 'WindowList',
|
||||
|
||||
_init : function () {
|
||||
_init: function(lookingGlass) {
|
||||
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
||||
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
|
||||
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
_updateWindowList: function() {
|
||||
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
||||
this.actor.destroy_all_children();
|
||||
let windows = global.get_window_actors();
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
@ -332,7 +341,7 @@ const WindowList = new Lang.Class({
|
||||
}
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this.actor.add(box);
|
||||
let windowLink = new ObjLink(metaWindow, metaWindow.title);
|
||||
let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
|
||||
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
|
||||
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
|
||||
box.add(propsBox);
|
||||
@ -343,7 +352,7 @@ const WindowList = new Lang.Class({
|
||||
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
||||
propsBox.add(propBox);
|
||||
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
|
||||
let appLink = new ObjLink(app, app.get_id());
|
||||
let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
|
||||
propBox.add(appLink.actor, { y_fill: false });
|
||||
propBox.add(icon, { y_fill: false });
|
||||
} else {
|
||||
@ -357,7 +366,7 @@ Signals.addSignalMethods(WindowList.prototype);
|
||||
const ObjInspector = new Lang.Class({
|
||||
Name: 'ObjInspector',
|
||||
|
||||
_init : function () {
|
||||
_init: function(lookingGlass) {
|
||||
this._obj = null;
|
||||
this._previousObj = null;
|
||||
|
||||
@ -369,6 +378,8 @@ const ObjInspector = new Lang.Class({
|
||||
style_class: 'lg-dialog',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._container);
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
selectObject: function(obj, skipPrevious) {
|
||||
@ -378,7 +389,7 @@ const ObjInspector = new Lang.Class({
|
||||
this._previousObj = null;
|
||||
this._obj = obj;
|
||||
|
||||
this._container.get_children().forEach(function (child) { child.destroy(); });
|
||||
this._container.destroy_all_children();
|
||||
|
||||
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
|
||||
this._container.add_actor(hbox);
|
||||
@ -400,12 +411,19 @@ const ObjInspector = new Lang.Class({
|
||||
button.connect('clicked', Lang.bind(this, this.close));
|
||||
hbox.add(button);
|
||||
if (typeof(obj) == typeof({})) {
|
||||
let properties = [];
|
||||
for (let propName in obj) {
|
||||
properties.push(propName);
|
||||
}
|
||||
properties.sort();
|
||||
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
let propName = properties[i];
|
||||
let valueStr;
|
||||
let link;
|
||||
try {
|
||||
let prop = obj[propName];
|
||||
link = new ObjLink(prop).actor;
|
||||
link = new ObjLink(this._lookingGlass, prop).actor;
|
||||
} catch (e) {
|
||||
link = new St.Label({ text: '<error>' });
|
||||
}
|
||||
@ -450,7 +468,7 @@ const ObjInspector = new Lang.Class({
|
||||
_onInsert: function() {
|
||||
let obj = this._obj;
|
||||
this.close();
|
||||
Main.lookingGlass.insertObject(obj);
|
||||
this._lookingGlass.insertObject(obj);
|
||||
},
|
||||
|
||||
_onBack: function() {
|
||||
@ -458,34 +476,36 @@ const ObjInspector = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
function addBorderPaintHook(actor) {
|
||||
let signalId = actor.connect_after('paint',
|
||||
function () {
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
const RedBorderEffect = new Lang.Class({
|
||||
Name: 'RedBorderEffect',
|
||||
Extends: Clutter.Effect,
|
||||
|
||||
let geom = actor.get_allocation_geometry();
|
||||
let width = 2;
|
||||
vfunc_paint: function() {
|
||||
let actor = this.get_actor();
|
||||
actor.continue_paint();
|
||||
|
||||
// clockwise order
|
||||
Cogl.rectangle(0, 0, geom.width, width);
|
||||
Cogl.rectangle(geom.width - width, width,
|
||||
geom.width, geom.height);
|
||||
Cogl.rectangle(0, geom.height,
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
});
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
|
||||
actor.queue_redraw();
|
||||
return signalId;
|
||||
}
|
||||
let geom = actor.get_allocation_geometry();
|
||||
let width = 2;
|
||||
|
||||
// clockwise order
|
||||
Cogl.rectangle(0, 0, geom.width, width);
|
||||
Cogl.rectangle(geom.width - width, width,
|
||||
geom.width, geom.height);
|
||||
Cogl.rectangle(0, geom.height,
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
},
|
||||
});
|
||||
|
||||
const Inspector = new Lang.Class({
|
||||
Name: 'Inspector',
|
||||
|
||||
_init: function() {
|
||||
_init: function(lookingGlass) {
|
||||
let container = new Shell.GenericContainer({ width: 0,
|
||||
height: 0 });
|
||||
container.connect('allocate', Lang.bind(this, this._allocate));
|
||||
@ -499,9 +519,6 @@ const Inspector = new Lang.Class({
|
||||
this._displayText = new St.Label();
|
||||
eventHandler.add(this._displayText, { expand: true });
|
||||
|
||||
this._borderPaintTarget = null;
|
||||
this._borderPaintId = null;
|
||||
eventHandler.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
eventHandler.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
|
||||
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
@ -516,6 +533,8 @@ const Inspector = new Lang.Class({
|
||||
// out, or move the pointer outside of _pointerTarget.
|
||||
this._target = null;
|
||||
this._pointerTarget = null;
|
||||
|
||||
this._lookingGlass = lookingGlass;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
@ -536,18 +555,13 @@ const Inspector = new Lang.Class({
|
||||
},
|
||||
|
||||
_close: function() {
|
||||
Clutter.ungrab_pointer(this._eventHandler);
|
||||
Clutter.ungrab_keyboard(this._eventHandler);
|
||||
Clutter.ungrab_pointer();
|
||||
Clutter.ungrab_keyboard();
|
||||
this._eventHandler.destroy();
|
||||
this._eventHandler = null;
|
||||
this.emit('closed');
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function (actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._close();
|
||||
@ -616,56 +630,12 @@ const Inspector = new Lang.Class({
|
||||
this._displayText.text = '';
|
||||
this._displayText.text = position + ' ' + this._target;
|
||||
|
||||
if (this._borderPaintTarget != this._target) {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = this._target;
|
||||
this._borderPaintId = addBorderPaintHook(this._target);
|
||||
}
|
||||
this._lookingGlass.setBorderPaintTarget(this._target);
|
||||
}
|
||||
});
|
||||
|
||||
Signals.addSignalMethods(Inspector.prototype);
|
||||
|
||||
const ErrorLog = new Lang.Class({
|
||||
Name: 'ErrorLog',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout();
|
||||
this.text = new St.Label();
|
||||
this.actor.add(this.text);
|
||||
// We need to override StLabel's default ellipsization when
|
||||
// using line_wrap; otherwise ClutterText's layout is going
|
||||
// to constrain both the width and height, which prevents
|
||||
// scrolling.
|
||||
this.text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this.text.clutter_text.line_wrap = true;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
||||
},
|
||||
|
||||
_formatTime: function(d){
|
||||
function pad(n) { return n < 10 ? '0' + n : n; }
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z';
|
||||
},
|
||||
|
||||
_renderText: function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
let text = this.text.text;
|
||||
let stack = Main._getAndClearErrorStack();
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
let logItem = stack[i];
|
||||
text += logItem.category + ' t=' + this._formatTime(new Date(logItem.timestamp)) + ' ' + logItem.message + '\n';
|
||||
}
|
||||
this.text.text = text;
|
||||
}
|
||||
});
|
||||
|
||||
const Memory = new Lang.Class({
|
||||
Name: 'Memory',
|
||||
|
||||
@ -694,7 +664,7 @@ const Memory = new Lang.Class({
|
||||
|
||||
this._gcbutton = new St.Button({ label: 'Full GC',
|
||||
style_class: 'lg-obj-inspector-button' });
|
||||
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
|
||||
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
|
||||
this.actor.add(this._gcbutton, { x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
|
||||
@ -755,13 +725,13 @@ const Extensions = new Lang.Class({
|
||||
let extension = actor._extension;
|
||||
let uri = extension.dir.get_uri();
|
||||
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
this._lookingGlass.close();
|
||||
},
|
||||
|
||||
_onWebPage: function (actor) {
|
||||
let extension = actor._extension;
|
||||
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
this._lookingGlass.close();
|
||||
},
|
||||
|
||||
_onViewErrors: function (actor) {
|
||||
@ -825,24 +795,33 @@ const Extensions = new Lang.Class({
|
||||
text: this._stateToString(extension.state) });
|
||||
metaBox.add(state);
|
||||
|
||||
let viewsource = new Link.Link({ label: _("View Source") });
|
||||
viewsource.actor._extension = extension;
|
||||
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||
metaBox.add(viewsource.actor);
|
||||
let viewsource = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: _("View Source") });
|
||||
viewsource._extension = extension;
|
||||
viewsource.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||
metaBox.add(viewsource);
|
||||
|
||||
if (extension.metadata.url) {
|
||||
let webpage = new Link.Link({ label: _("Web Page") });
|
||||
webpage.actor._extension = extension;
|
||||
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
metaBox.add(webpage.actor);
|
||||
let webpage = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: _("Web Page") });
|
||||
webpage._extension = extension;
|
||||
webpage.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
metaBox.add(webpage);
|
||||
}
|
||||
|
||||
let viewerrors = new Link.Link({ label: _("Show Errors") });
|
||||
viewerrors.actor._extension = extension;
|
||||
viewerrors.actor._parentBox = box;
|
||||
viewerrors.actor._isShowing = false;
|
||||
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
|
||||
metaBox.add(viewerrors.actor);
|
||||
let viewerrors = new St.Button({ reactive: true,
|
||||
track_hover: true,
|
||||
style_class: 'shell-link',
|
||||
label: _("Show Errors") });
|
||||
viewerrors._extension = extension;
|
||||
viewerrors._parentBox = box;
|
||||
viewerrors._isShowing = false;
|
||||
viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors));
|
||||
metaBox.add(viewerrors);
|
||||
|
||||
return box;
|
||||
}
|
||||
@ -853,8 +832,7 @@ const LookingGlass = new Lang.Class({
|
||||
|
||||
_init : function() {
|
||||
this._borderPaintTarget = null;
|
||||
this._borderPaintId = 0;
|
||||
this._borderDestroyId = 0;
|
||||
this._redBorderEffect = new RedBorderEffect();
|
||||
|
||||
this._open = false;
|
||||
|
||||
@ -884,7 +862,7 @@ const LookingGlass = new Lang.Class({
|
||||
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._queueResize));
|
||||
|
||||
this._objInspector = new ObjInspector();
|
||||
this._objInspector = new ObjInspector(this);
|
||||
Main.uiGroup.add_actor(this._objInspector.actor);
|
||||
this._objInspector.actor.hide();
|
||||
|
||||
@ -896,7 +874,7 @@ const LookingGlass = new Lang.Class({
|
||||
toolbar.add_actor(inspectIcon);
|
||||
inspectIcon.reactive = true;
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector();
|
||||
let inspector = new Inspector(this);
|
||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||
target);
|
||||
@ -926,23 +904,16 @@ const LookingGlass = new Lang.Class({
|
||||
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
|
||||
this._evalBox.add_actor(this._entryArea);
|
||||
|
||||
let label = new St.Label({ text: 'js>>> ' });
|
||||
let label = new St.Label({ text: CHEVRON });
|
||||
this._entryArea.add(label);
|
||||
|
||||
this._entry = new St.Entry({ can_focus: true });
|
||||
ShellEntry.addContextMenu(this._entry);
|
||||
this._entryArea.add(this._entry, { expand: true });
|
||||
|
||||
this._windowList = new WindowList();
|
||||
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
|
||||
notebook.selectIndex(0);
|
||||
this._pushResult('<window selection>', window);
|
||||
}));
|
||||
this._windowList = new WindowList(this);
|
||||
notebook.appendPage('Windows', this._windowList.actor);
|
||||
|
||||
this._errorLog = new ErrorLog();
|
||||
notebook.appendPage('Errors', this._errorLog.actor);
|
||||
|
||||
this._memory = new Memory();
|
||||
notebook.appendPage('Memory', this._memory.actor);
|
||||
|
||||
@ -994,23 +965,22 @@ const LookingGlass = new Lang.Class({
|
||||
+ 'font-family: "' + fontDesc.get_family() + '";';
|
||||
},
|
||||
|
||||
setBorderPaintTarget: function(obj) {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.remove_effect(this._redBorderEffect);
|
||||
this._borderPaintTarget = obj;
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.add_effect(this._redBorderEffect);
|
||||
},
|
||||
|
||||
_pushResult: function(command, obj) {
|
||||
let index = this._results.length + this._offset;
|
||||
let result = new Result('>>> ' + command, obj, index);
|
||||
let result = new Result(this, CHEVRON + command, obj, index);
|
||||
this._results.push(result);
|
||||
this._resultsArea.add(result.actor);
|
||||
if (this._borderPaintTarget != null) {
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = null;
|
||||
}
|
||||
if (obj instanceof Clutter.Actor) {
|
||||
this._borderPaintTarget = obj;
|
||||
this._borderPaintId = addBorderPaintHook(obj);
|
||||
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
|
||||
this._borderDestroyId = 0;
|
||||
this._borderPaintTarget = null;
|
||||
}));
|
||||
}
|
||||
if (obj instanceof Clutter.Actor)
|
||||
this.setBorderPaintTarget(obj);
|
||||
|
||||
let children = this._resultsArea.get_children();
|
||||
if (children.length > this._maxItems) {
|
||||
this._results.shift();
|
||||
@ -1191,11 +1161,7 @@ const LookingGlass = new Lang.Class({
|
||||
this._open = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
if (this._borderPaintTarget != null) {
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget.disconnect(this._borderDestroyId);
|
||||
this._borderPaintTarget = null;
|
||||
}
|
||||
this.setBorderPaintTarget(null);
|
||||
|
||||
Main.popModal(this._entry);
|
||||
|
||||
|
@ -16,6 +16,7 @@ const Params = imports.misc.params;
|
||||
|
||||
const MOUSE_POLL_FREQUENCY = 50;
|
||||
const CROSSHAIRS_CLIP_SIZE = [100, 100];
|
||||
const NO_CHANGE = 0.0;
|
||||
|
||||
// Settings
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
@ -24,6 +25,14 @@ const SHOW_KEY = 'screen-magnifier-enabled';
|
||||
const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
|
||||
const SCREEN_POSITION_KEY = 'screen-position';
|
||||
const MAG_FACTOR_KEY = 'mag-factor';
|
||||
const INVERT_LIGHTNESS_KEY = 'invert-lightness';
|
||||
const COLOR_SATURATION_KEY = 'color-saturation';
|
||||
const BRIGHT_RED_KEY = 'brightness-red';
|
||||
const BRIGHT_GREEN_KEY = 'brightness-green';
|
||||
const BRIGHT_BLUE_KEY = 'brightness-blue';
|
||||
const CONTRAST_RED_KEY = 'contrast-red';
|
||||
const CONTRAST_GREEN_KEY = 'contrast-green';
|
||||
const CONTRAST_BLUE_KEY = 'contrast-blue';
|
||||
const LENS_MODE_KEY = 'lens-mode';
|
||||
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
||||
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
||||
@ -443,6 +452,25 @@ const Magnifier = new Lang.Class({
|
||||
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);
|
||||
}
|
||||
|
||||
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
||||
@ -465,6 +493,25 @@ const Magnifier = new Lang.Class({
|
||||
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
||||
Lang.bind(this, this._updateMouseTrackingMode));
|
||||
|
||||
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
|
||||
Lang.bind(this, this._updateInvertLightness));
|
||||
this._settings.connect('changed::' + COLOR_SATURATION_KEY,
|
||||
Lang.bind(this, this._updateColorSaturation));
|
||||
|
||||
this._settings.connect('changed::' + BRIGHT_RED_KEY,
|
||||
Lang.bind(this, this._updateBrightness));
|
||||
this._settings.connect('changed::' + BRIGHT_GREEN_KEY,
|
||||
Lang.bind(this, this._updateBrightness));
|
||||
this._settings.connect('changed::' + BRIGHT_BLUE_KEY,
|
||||
Lang.bind(this, this._updateBrightness));
|
||||
|
||||
this._settings.connect('changed::' + CONTRAST_RED_KEY,
|
||||
Lang.bind(this, this._updateContrast));
|
||||
this._settings.connect('changed::' + CONTRAST_GREEN_KEY,
|
||||
Lang.bind(this, this._updateContrast));
|
||||
this._settings.connect('changed::' + CONTRAST_BLUE_KEY,
|
||||
Lang.bind(this, this._updateContrast));
|
||||
|
||||
this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
|
||||
Lang.bind(this, function() {
|
||||
this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
|
||||
@ -540,7 +587,47 @@ const Magnifier = new Lang.Class({
|
||||
this._settings.get_enum(MOUSE_TRACKING_KEY)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateInvertLightness: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
this._zoomRegions[0].setInvertLightness(
|
||||
this._settings.get_boolean(INVERT_LIGHTNESS_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateColorSaturation: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
this._zoomRegions[0].setColorSaturation(
|
||||
this._settings.get_double(COLOR_SATURATION_KEY)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_updateBrightness: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
let brightness = {};
|
||||
brightness.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
brightness.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
brightness.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
this._zoomRegions[0].setBrightness(brightness);
|
||||
}
|
||||
},
|
||||
|
||||
_updateContrast: function() {
|
||||
// Applies only to the first zoom region.
|
||||
if (this._zoomRegions.length) {
|
||||
let contrast = {};
|
||||
contrast.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
contrast.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
this._zoomRegions[0].setContrast(contrast);
|
||||
}
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(Magnifier.prototype);
|
||||
|
||||
@ -554,6 +641,10 @@ const ZoomRegion = new Lang.Class({
|
||||
this._clampScrollingAtEdges = false;
|
||||
this._lensMode = false;
|
||||
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
||||
this._invertLightness = false;
|
||||
this._colorSaturation = 1.0;
|
||||
this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
|
||||
this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
|
||||
|
||||
this._magView = null;
|
||||
this._background = null;
|
||||
@ -879,6 +970,107 @@ const ZoomRegion = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* setInvertLightness:
|
||||
* Set whether to invert the lightness of the magnified view.
|
||||
* @flag Boolean to either invert brightness (true), or not (false).
|
||||
*/
|
||||
setInvertLightness: function(flag) {
|
||||
this._invertLightness = flag;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setInvertLightness(this._invertLightness);
|
||||
},
|
||||
|
||||
/**
|
||||
* getInvertLightness:
|
||||
* Retrieve whether the lightness is inverted.
|
||||
* @return Boolean indicating inversion (true), or not (false).
|
||||
*/
|
||||
getInvertLightness: function() {
|
||||
return this._invertLightness;
|
||||
},
|
||||
|
||||
/**
|
||||
* setColorSaturation:
|
||||
* Set the color saturation of the magnified view.
|
||||
* @sauration A value from 0.0 to 1.0 that defines the color
|
||||
* saturation, with 0.0 defining no color (grayscale),
|
||||
* and 1.0 defining full color.
|
||||
*/
|
||||
setColorSaturation: function(saturation) {
|
||||
this._colorSaturation = saturation;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setColorSaturation(this._colorSaturation);
|
||||
},
|
||||
|
||||
/**
|
||||
* getColorSaturation:
|
||||
* Retrieve the color saturation of the magnified view.
|
||||
*/
|
||||
getColorSaturation: function() {
|
||||
return this._colorSaturation;
|
||||
},
|
||||
|
||||
/**
|
||||
* setBrightness:
|
||||
* Alter the brightness of the magnified view.
|
||||
* @brightness Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||
*/
|
||||
setBrightness: function(brightness) {
|
||||
this._brightness.r = brightness.r;
|
||||
this._brightness.g = brightness.g;
|
||||
this._brightness.b = brightness.b;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setBrightness(this._brightness);
|
||||
},
|
||||
|
||||
/**
|
||||
* getBrightness:
|
||||
* Retrive the current brightness of the Zoom Region.
|
||||
* @return Object containing the brightness change for the red, green,
|
||||
* and blue channels.
|
||||
*/
|
||||
getBrightness: function() {
|
||||
let brightness = {};
|
||||
brightness.r = this._brightness.r;
|
||||
brightness.g = this._brightness.g;
|
||||
brightness.b = this._brightness.b;
|
||||
return brightness;
|
||||
},
|
||||
|
||||
/**
|
||||
* setContrast:
|
||||
* Alter the contrast of the magnified view.
|
||||
* @contrast Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
setContrast: function(contrast) {
|
||||
this._contrast.r = contrast.r;
|
||||
this._contrast.g = contrast.g;
|
||||
this._contrast.b = contrast.b;
|
||||
if (this._magShaderEffects)
|
||||
this._magShaderEffects.setContrast(this._contrast);
|
||||
},
|
||||
|
||||
/**
|
||||
* getContrast:
|
||||
* Retreive the contrast of the magnified view.
|
||||
* @return Object containing the contrast for the red, green,
|
||||
* and blue channels.
|
||||
*/
|
||||
getContrast: function() {
|
||||
let contrast = {};
|
||||
contrast.r = this._contrast.r;
|
||||
contrast.g = this._contrast.g;
|
||||
contrast.b = this._contrast.b;
|
||||
return contrast;
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_createActors: function() {
|
||||
@ -917,6 +1109,13 @@ const ZoomRegion = new Lang.Class({
|
||||
this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
|
||||
else
|
||||
this._crossHairsActor = null;
|
||||
|
||||
// Contrast and brightness effects.
|
||||
this._magShaderEffects = new MagShaderEffects(this._uiGroupClone);
|
||||
this._magShaderEffects.setColorSaturation(this._colorSaturation);
|
||||
this._magShaderEffects.setInvertLightness(this._invertLightness);
|
||||
this._magShaderEffects.setBrightness(this._brightness);
|
||||
this._magShaderEffects.setContrast(this._contrast);
|
||||
},
|
||||
|
||||
_destroyActors: function() {
|
||||
@ -925,6 +1124,8 @@ const ZoomRegion = new Lang.Class({
|
||||
if (this._crossHairs)
|
||||
this._crossHairs.removeFromParent(this._crossHairsActor);
|
||||
|
||||
this._magShaderEffects.destroyEffects();
|
||||
this._magShaderEffects = null;
|
||||
this._magView.destroy();
|
||||
this._magView = null;
|
||||
this._background = null;
|
||||
@ -1228,10 +1429,7 @@ const Crosshairs = new Lang.Class({
|
||||
crosshairsActor = new Clutter.Clone({ source: this._actor });
|
||||
this._clones.push(crosshairsActor);
|
||||
}
|
||||
if (this._actor.visible)
|
||||
crosshairsActor.show();
|
||||
else
|
||||
crosshairsActor.hide();
|
||||
crosshairsActor.visible = this._actor.visible;
|
||||
|
||||
container.add_actor(crosshairsActor);
|
||||
container.raise_child(magnifiedMouse, crosshairsActor);
|
||||
@ -1436,3 +1634,144 @@ const Crosshairs = new Lang.Class({
|
||||
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
|
||||
}
|
||||
});
|
||||
|
||||
const MagShaderEffects = new Lang.Class({
|
||||
Name: 'MagShaderEffects',
|
||||
|
||||
_init: function(uiGroupClone) {
|
||||
this._inverse = new Shell.InvertLightnessEffect();
|
||||
this._brightnessContrast = new Clutter.BrightnessContrastEffect();
|
||||
this._colorDesaturation = new Clutter.DesaturateEffect();
|
||||
this._inverse.set_enabled(false);
|
||||
this._brightnessContrast.set_enabled(false);
|
||||
|
||||
this._magView = uiGroupClone;
|
||||
this._magView.add_effect(this._inverse);
|
||||
this._magView.add_effect(this._brightnessContrast);
|
||||
this._magView.add_effect(this._colorDesaturation);
|
||||
},
|
||||
|
||||
/**
|
||||
* destroyEffects:
|
||||
* Remove contrast and brightness effects from the magnified view, and
|
||||
* lose the reference to the actor they were applied to. Don't use this
|
||||
* object after calling this.
|
||||
*/
|
||||
destroyEffects: function() {
|
||||
this._magView.clear_effects();
|
||||
this._colorDesaturation = null;
|
||||
this._brightnessContrast = null;
|
||||
this._inverse = null;
|
||||
this._magView = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* setInvertLightness:
|
||||
* Enable/disable invert lightness effect.
|
||||
* @invertFlag: Enabled flag.
|
||||
*/
|
||||
setInvertLightness: function(invertFlag) {
|
||||
this._inverse.set_enabled(invertFlag);
|
||||
},
|
||||
|
||||
/**
|
||||
* getInvertLightness:
|
||||
* Report whether the inversion effect is enabled.
|
||||
* @return: Boolean.
|
||||
*/
|
||||
getInvertLightness: function() {
|
||||
return this._inverse.get_enabled();
|
||||
},
|
||||
|
||||
setColorSaturation: function(factor) {
|
||||
this._colorDesaturation.set_factor(1.0 - factor);
|
||||
},
|
||||
|
||||
getColorSaturation: function() {
|
||||
return 1.0 - this._colorDesaturation.get_factor();
|
||||
},
|
||||
|
||||
/**
|
||||
* setBrightness:
|
||||
* Set the brightness of the magnified view.
|
||||
* @brightness: Object containing the brightness for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness,
|
||||
* respectively.
|
||||
*/
|
||||
setBrightness: function(brightness) {
|
||||
let bRed = brightness.r;
|
||||
let bGreen = brightness.g;
|
||||
let bBlue = brightness.b;
|
||||
this._brightnessContrast.set_brightness_full(bRed, bGreen, bBlue);
|
||||
|
||||
// Enable the effect if the brightness OR contrast change are such that
|
||||
// it modifies the brightness and/or contrast.
|
||||
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||
this._brightnessContrast.set_enabled(
|
||||
(bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
|
||||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* getBrightness:
|
||||
* Retrieve current brightness of the magnified view.
|
||||
* @return: Object containing the brightness for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* brightness (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed brightness, respectively.
|
||||
*/
|
||||
getBrightness: function() {
|
||||
let result = {};
|
||||
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||
result.r = bRed;
|
||||
result.g = bGreen;
|
||||
result.b = bBlue;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the contrast of the magnified view.
|
||||
* @contrast: Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
setContrast: function(contrast) {
|
||||
let cRed = contrast.r;
|
||||
let cGreen = contrast.g;
|
||||
let cBlue = contrast.b;
|
||||
|
||||
this._brightnessContrast.set_contrast_full(cRed, cGreen, cBlue);
|
||||
|
||||
// Enable the effect if the contrast OR brightness change are such that
|
||||
// it modifies the brightness and/or contrast.
|
||||
// should be able to use Clutter.color_equal(), but that complains of
|
||||
// a null first argument.
|
||||
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
||||
this._brightnessContrast.set_enabled(
|
||||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE ||
|
||||
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve current contrast of the magnified view.
|
||||
* @return: Object containing the contrast for the red, green,
|
||||
* and blue channels. Values of 0.0 represent "standard"
|
||||
* contrast (no change), whereas values less or greater than
|
||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
||||
*/
|
||||
getContrast: function() {
|
||||
let resutl = {};
|
||||
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
||||
result.r = cRed;
|
||||
result.g = cGreen;
|
||||
result.b = cBlue;
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
234
js/ui/main.js
@ -18,6 +18,7 @@ const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
const KeyringPrompt = imports.ui.keyringPrompt;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const Keyboard = imports.ui.keyboard;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Overview = imports.ui.overview;
|
||||
@ -29,13 +30,16 @@ const LookingGlass = imports.ui.lookingGlass;
|
||||
const NetworkAgent = imports.ui.networkAgent;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
const ScreenShield = imports.ui.screenShield;
|
||||
const Scripting = imports.ui.scripting;
|
||||
const SessionMode = imports.ui.sessionMode;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const TelepathyClient = imports.ui.telepathyClient;
|
||||
const UnlockDialog = imports.ui.unlockDialog;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||
@ -46,71 +50,87 @@ let automountManager = null;
|
||||
let autorunManager = null;
|
||||
let panel = null;
|
||||
let hotCorners = [];
|
||||
let placesManager = null;
|
||||
let overview = null;
|
||||
let runDialog = null;
|
||||
let lookingGlass = null;
|
||||
let wm = null;
|
||||
let messageTray = null;
|
||||
let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let telepathyClient = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let recorder = null;
|
||||
let sessionMode = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
let modalCount = 0;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
let xdndHandler = null;
|
||||
let statusIconDispatcher = null;
|
||||
let keyboard = null;
|
||||
let layoutManager = null;
|
||||
let networkAgent = null;
|
||||
let _errorLogStack = [];
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _gdmCssStylesheet = null;
|
||||
let _overridesSettings = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
function _createUserSession() {
|
||||
function createUserSession() {
|
||||
// Load the calendar server. Note that we are careful about
|
||||
// not loading any events until the user presses the clock
|
||||
global.launch_calendar_server();
|
||||
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
automountManager = new AutomountManager.AutomountManager();
|
||||
autorunManager = new AutorunManager.AutorunManager();
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
|
||||
_initRecorder();
|
||||
}
|
||||
|
||||
function _createGDMSession() {
|
||||
function createGDMSession() {
|
||||
screenShield.showDialog();
|
||||
}
|
||||
|
||||
function createGDMLoginDialog(parentActor) {
|
||||
// We do this this here instead of at the top to prevent GDM
|
||||
// related code from getting loaded in normal user sessions
|
||||
const LoginDialog = imports.gdm.loginDialog;
|
||||
|
||||
let loginDialog = new LoginDialog.LoginDialog();
|
||||
loginDialog.connect('loaded', function() {
|
||||
loginDialog.open();
|
||||
});
|
||||
let loginDialog = new LoginDialog.LoginDialog(parentActor);
|
||||
return [loginDialog, true];
|
||||
}
|
||||
|
||||
function createSessionUnlockDialog(parentActor) {
|
||||
let dialog = new UnlockDialog.UnlockDialog(parentActor);
|
||||
return [dialog, false];
|
||||
}
|
||||
|
||||
function createInitialSetupSession() {
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
}
|
||||
|
||||
function _initRecorder() {
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||
|
||||
global.screen.connect('toggle-recording', function() {
|
||||
global.display.add_keybinding('toggle-recording',
|
||||
bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE, function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
if (recorder.is_recording()) {
|
||||
recorder.pause();
|
||||
recorder.close();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else {
|
||||
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||
/* Translators: this is a filename used for screencast recording */
|
||||
@ -129,39 +149,19 @@ function _initRecorder() {
|
||||
});
|
||||
}
|
||||
|
||||
function _initUserSession() {
|
||||
_initRecorder();
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
||||
getRunDialog().open();
|
||||
});
|
||||
|
||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
|
||||
}
|
||||
|
||||
function start() {
|
||||
// Monkey patch utility functions into the global proxy;
|
||||
// This is easier and faster than indirecting down into global
|
||||
// if we want to call back up into JS.
|
||||
global.logError = _logError;
|
||||
global.log = _logDebug;
|
||||
// These are here so we don't break compatibility.
|
||||
global.logError = window.log;
|
||||
global.log = window.log;
|
||||
|
||||
// Chain up async errors reported from C
|
||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
sessionMode = new SessionMode.SessionMode();
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
@ -183,7 +183,6 @@ function start() {
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
|
||||
loadTheme();
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
@ -194,9 +193,16 @@ function start() {
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
});
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.SIZE });
|
||||
uiGroup.add_constraint(constraint);
|
||||
uiGroup.connect('get-preferred-width',
|
||||
function(actor, forHeight, alloc) {
|
||||
let width = global.stage.width;
|
||||
[alloc.min_size, alloc.natural_size] = [width, width];
|
||||
});
|
||||
uiGroup.connect('get-preferred-height',
|
||||
function(actor, forWidth, alloc) {
|
||||
let height = global.stage.height;
|
||||
[alloc.min_size, alloc.natural_size] = [height, height];
|
||||
});
|
||||
global.window_group.reparent(uiGroup);
|
||||
global.overlay_group.reparent(uiGroup);
|
||||
global.stage.add_actor(uiGroup);
|
||||
@ -204,10 +210,10 @@ function start() {
|
||||
layoutManager = new Layout.LayoutManager();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
// This overview object is just a stub for non-user sessions
|
||||
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
|
||||
overview = new Overview.Overview();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
screenSaverDBus = new ShellDBus.ScreenSaverDBus();
|
||||
panel = new Panel.Panel();
|
||||
wm = new WindowManager.WindowManager();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
@ -215,10 +221,7 @@ function start() {
|
||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
_createUserSession();
|
||||
else if (global.session_type == Shell.SessionType.GDM)
|
||||
_createGDMSession();
|
||||
sessionMode.createSession();
|
||||
|
||||
panel.startStatusArea();
|
||||
|
||||
@ -226,9 +229,29 @@ function start() {
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
_initUserSession();
|
||||
statusIconDispatcher.start(messageTray.actor);
|
||||
if (sessionMode.hasWorkspaces)
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
|
||||
if (sessionMode.allowExtensions) {
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
}
|
||||
|
||||
if (sessionMode.hasRunDialog) {
|
||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
||||
getRunDialog().open();
|
||||
});
|
||||
}
|
||||
|
||||
if (sessionMode.hasOverview) {
|
||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
|
||||
global.display.connect('overlay-key',
|
||||
Lang.bind(overview, overview.toggle));
|
||||
}
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
@ -244,7 +267,6 @@ function start() {
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
|
||||
_log('info', 'loaded at ' + _startDate);
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
|
||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||
@ -487,8 +509,8 @@ function loadTheme() {
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||
|
||||
if (global.session_type == Shell.SessionType.GDM)
|
||||
theme.load_stylesheet(_gdmCssStylesheet);
|
||||
if (sessionMode.extraStylesheet)
|
||||
theme.load_stylesheet(sessionMode.extraStylesheet);
|
||||
|
||||
if (previousTheme) {
|
||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||
@ -510,6 +532,7 @@ function notify(msg, details) {
|
||||
messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, msg, details);
|
||||
notification.setTransient(true);
|
||||
notification.setShowWhenLocked(true);
|
||||
source.notify(notification);
|
||||
}
|
||||
|
||||
@ -530,59 +553,6 @@ function notifyError(msg, details) {
|
||||
notify(msg, details);
|
||||
}
|
||||
|
||||
/**
|
||||
* _log:
|
||||
* @category: string message type ('info', 'error')
|
||||
* @msg: A message string
|
||||
* ...: Any further arguments are converted into JSON notation,
|
||||
* and appended to the log message, separated by spaces.
|
||||
*
|
||||
* Log a message into the LookingGlass error
|
||||
* stream. This is primarily intended for use by the
|
||||
* extension system as well as debugging.
|
||||
*/
|
||||
function _log(category, msg) {
|
||||
let text = msg;
|
||||
if (arguments.length > 2) {
|
||||
text += ': ';
|
||||
for (let i = 2; i < arguments.length; i++) {
|
||||
text += JSON.stringify(arguments[i]);
|
||||
if (i < arguments.length - 1)
|
||||
text += ' ';
|
||||
}
|
||||
}
|
||||
_errorLogStack.push({timestamp: new Date().getTime(),
|
||||
category: category,
|
||||
message: text });
|
||||
}
|
||||
|
||||
function _logError(msg) {
|
||||
return _log('error', msg);
|
||||
}
|
||||
|
||||
function _logDebug(msg) {
|
||||
return _log('debug', msg);
|
||||
}
|
||||
|
||||
// Used by the error display in lookingGlass.js
|
||||
function _getAndClearErrorStack() {
|
||||
let errors = _errorLogStack;
|
||||
_errorLogStack = [];
|
||||
return errors;
|
||||
}
|
||||
|
||||
function logStackTrace(msg) {
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
// e.stack must have at least two lines, with the first being
|
||||
// logStackTrace() (which we strip off), and the second being
|
||||
// our caller.
|
||||
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
|
||||
log(msg ? (msg + '\n' + trace) : trace);
|
||||
}
|
||||
}
|
||||
|
||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||
return win.get_workspace() == workspaceIndex ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
@ -605,6 +575,11 @@ function _globalKeyPressHandler(actor, event) {
|
||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
||||
return false;
|
||||
|
||||
if (!sessionMode.allowKeybindingsWhenModal) {
|
||||
if (modalCount > (overview.visible ? 1 : 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
let keyCode = event.get_key_code();
|
||||
let ignoredModifiers = global.display.get_ignored_modifier_mask();
|
||||
@ -613,11 +588,6 @@ function _globalKeyPressHandler(actor, event) {
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
||||
|
||||
// Other bindings are only available to the user session when the overview is up and
|
||||
// no modal dialog is present.
|
||||
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
|
||||
return false;
|
||||
|
||||
// This isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
overview.hide();
|
||||
@ -630,28 +600,39 @@ function _globalKeyPressHandler(actor, event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// None of the other bindings are relevant outside of the user's session
|
||||
if (global.session_type != Shell.SessionType.USER)
|
||||
return false;
|
||||
|
||||
switch (action) {
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing; we also disable them in the main view.
|
||||
//
|
||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
// if (!sessionMode.hasWorkspaces)
|
||||
// return false;
|
||||
//
|
||||
// wm.actionMoveWorkspaceLeft();
|
||||
// return true;
|
||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
// if (!sessionMode.hasWorkspaces)
|
||||
// return false;
|
||||
//
|
||||
// wm.actionMoveWorkspaceRight();
|
||||
// return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
||||
wm.actionMoveWorkspaceUp();
|
||||
if (!sessionMode.hasWorkspaces)
|
||||
return false;
|
||||
|
||||
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
||||
wm.actionMoveWorkspaceDown();
|
||||
if (!sessionMode.hasWorkspaces)
|
||||
return false;
|
||||
|
||||
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
||||
case Meta.KeyBindingAction.COMMAND_2:
|
||||
if (!sessionMode.hasRunDialog)
|
||||
return false;
|
||||
|
||||
getRunDialog().open();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
@ -670,6 +651,10 @@ function _findModal(actor) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function isInModalStack(actor) {
|
||||
return _findModal(actor) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushModal:
|
||||
* @actor: #ClutterActor which will be given keyboard focus
|
||||
@ -702,6 +687,7 @@ function pushModal(actor, timestamp, options) {
|
||||
log('pushModal: invocation of begin_modal failed');
|
||||
return false;
|
||||
}
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
}
|
||||
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
||||
@ -710,7 +696,7 @@ function pushModal(actor, timestamp, options) {
|
||||
let actorDestroyId = actor.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack.splice(index, 1);
|
||||
popModal(actor);
|
||||
});
|
||||
let curFocus = global.stage.get_key_focus();
|
||||
let curFocusDestroyId;
|
||||
@ -782,6 +768,7 @@ function popModal(actor, timestamp) {
|
||||
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
}
|
||||
|
||||
function createLookingGlass() {
|
||||
@ -924,7 +911,8 @@ function initializeDeferredWork(actor, callback, props) {
|
||||
function queueDeferredWork(workId) {
|
||||
let data = _deferredWorkData[workId];
|
||||
if (!data) {
|
||||
global.logError('invalid work id ', workId);
|
||||
let message = 'Invalid work id %d'.format(workId);
|
||||
logError(new Error(message), message);
|
||||
return;
|
||||
}
|
||||
if (_deferredWorkQueue.indexOf(workId) < 0)
|
||||
|
@ -394,7 +394,7 @@ Signals.addSignalMethods(FocusGrabber.prototype);
|
||||
// the content area (as with addBody()).
|
||||
//
|
||||
// By default, the icon shown is created by calling
|
||||
// source.createNotificationIcon(). However, if @params contains an 'icon'
|
||||
// source.createIcon(). However, if @params contains an 'icon'
|
||||
// parameter, the passed in icon will be used.
|
||||
//
|
||||
// If @params contains a 'titleMarkup', 'bannerMarkup', or
|
||||
@ -420,6 +420,8 @@ const Notification = new Lang.Class({
|
||||
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
|
||||
this.isTransient = false;
|
||||
this.expanded = false;
|
||||
this.showWhenLocked = false;
|
||||
this.acknowledged = false;
|
||||
this._destroyed = false;
|
||||
this._useActionIcons = false;
|
||||
this._customContent = false;
|
||||
@ -441,7 +443,7 @@ const Notification = new Lang.Class({
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._table = new St.Table({ name: 'notification',
|
||||
this._table = new St.Table({ style_class: 'notification',
|
||||
reactive: true });
|
||||
this._table.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||
this.actor.set_child(this._table);
|
||||
@ -493,6 +495,7 @@ const Notification = new Lang.Class({
|
||||
params = Params.parse(params, { customContent: false,
|
||||
body: null,
|
||||
icon: null,
|
||||
secondaryIcon: null,
|
||||
titleMarkup: false,
|
||||
bannerMarkup: false,
|
||||
bodyMarkup: false,
|
||||
@ -507,6 +510,11 @@ const Notification = new Lang.Class({
|
||||
this._icon = null;
|
||||
}
|
||||
|
||||
if (this._secondaryIcon && (params.secondaryIcon || params.clear)) {
|
||||
this._secondaryIcon.destroy();
|
||||
this._secondaryIcon = null;
|
||||
}
|
||||
|
||||
// We always clear the content area if we don't have custom
|
||||
// content because it might contain the @banner that didn't
|
||||
// fit in the banner mode.
|
||||
@ -533,7 +541,7 @@ const Notification = new Lang.Class({
|
||||
this._table.remove_style_class_name('multi-line-notification');
|
||||
|
||||
if (!this._icon) {
|
||||
this._icon = params.icon || this.source.createNotificationIcon();
|
||||
this._icon = params.icon || this.source.createIcon(this.source.ICON_SIZE);
|
||||
this._table.add(this._icon, { row: 0,
|
||||
col: 0,
|
||||
x_expand: false,
|
||||
@ -542,6 +550,13 @@ const Notification = new Lang.Class({
|
||||
y_align: St.Align.START });
|
||||
}
|
||||
|
||||
if (!this._secondaryIcon) {
|
||||
this._secondaryIcon = params.secondaryIcon;
|
||||
|
||||
if (this._secondaryIcon)
|
||||
this._bannerBox.add_actor(this._secondaryIcon);
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
|
||||
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
|
||||
@ -577,7 +592,7 @@ const Notification = new Lang.Class({
|
||||
|
||||
if (params.body)
|
||||
this.addBody(params.body, params.bodyMarkup);
|
||||
this._updated();
|
||||
this.updated();
|
||||
},
|
||||
|
||||
setIconVisible: function(visible) {
|
||||
@ -586,19 +601,21 @@ const Notification = new Lang.Class({
|
||||
|
||||
enableScrolling: function(enableScrolling) {
|
||||
this._scrollPolicy = enableScrolling ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
|
||||
if (this._scrollArea)
|
||||
if (this._scrollArea) {
|
||||
this._scrollArea.vscrollbar_policy = this._scrollPolicy;
|
||||
this._scrollArea.enable_mouse_scrolling = enableScrolling;
|
||||
}
|
||||
},
|
||||
|
||||
_createScrollArea: function() {
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
|
||||
this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview',
|
||||
vscrollbar_policy: this._scrollPolicy,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
this._table.add(this._scrollArea, { row: 1,
|
||||
col: 2 });
|
||||
this._updateLastColumnSettings();
|
||||
this._contentArea = new St.BoxLayout({ name: 'notification-body',
|
||||
this._contentArea = new St.BoxLayout({ style_class: 'notification-body',
|
||||
vertical: true });
|
||||
this._scrollArea.add_actor(this._contentArea);
|
||||
// If we know the notification will be expandable, we need to add
|
||||
@ -616,7 +633,7 @@ const Notification = new Lang.Class({
|
||||
}
|
||||
|
||||
this._contentArea.add(actor, style ? style : {});
|
||||
this._updated();
|
||||
this.updated();
|
||||
},
|
||||
|
||||
// addBody:
|
||||
@ -679,7 +696,7 @@ const Notification = new Lang.Class({
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._table.add(this._actionArea, props);
|
||||
this._updateLastColumnSettings();
|
||||
this._updated();
|
||||
this.updated();
|
||||
},
|
||||
|
||||
_updateLastColumnSettings: function() {
|
||||
@ -734,7 +751,7 @@ const Notification = new Lang.Class({
|
||||
addButton: function(id, label) {
|
||||
if (!this._buttonBox) {
|
||||
|
||||
let box = new St.BoxLayout({ name: 'notification-actions' });
|
||||
let box = new St.BoxLayout({ style_class: 'notification-actions' });
|
||||
this.setActionArea(box, { x_expand: false,
|
||||
y_expand: false,
|
||||
x_fill: false,
|
||||
@ -744,6 +761,7 @@ const Notification = new Lang.Class({
|
||||
}
|
||||
|
||||
let button = new St.Button({ can_focus: true });
|
||||
button._actionId = id;
|
||||
|
||||
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) {
|
||||
button.add_style_class_name('notification-icon-button');
|
||||
@ -753,14 +771,35 @@ const Notification = new Lang.Class({
|
||||
button.label = label;
|
||||
}
|
||||
|
||||
if (this._buttonBox.get_children().length > 0)
|
||||
if (this._buttonBox.get_n_children() > 0)
|
||||
this._buttonFocusManager.remove_group(this._buttonBox);
|
||||
|
||||
this._buttonBox.add(button);
|
||||
this._buttonFocusManager.add_group(this._buttonBox);
|
||||
button.connect('clicked', Lang.bind(this, this._onActionInvoked, id));
|
||||
|
||||
this._updated();
|
||||
this.updated();
|
||||
},
|
||||
|
||||
// setButtonSensitive:
|
||||
// @id: the action ID
|
||||
// @sensitive: whether the button should be sensitive
|
||||
//
|
||||
// If the notification contains a button with action ID @id,
|
||||
// its sensitivity will be set to @sensitive. Insensitive
|
||||
// buttons cannot be clicked.
|
||||
setButtonSensitive: function(id, sensitive) {
|
||||
if (!this._buttonBox)
|
||||
return;
|
||||
|
||||
let button = this._buttonBox.get_children().filter(function(b) {
|
||||
return b._actionId == id;
|
||||
})[0];
|
||||
|
||||
if (!button || button.reactive == sensitive)
|
||||
return;
|
||||
|
||||
button.reactive = sensitive;
|
||||
},
|
||||
|
||||
setUrgency: function(urgency) {
|
||||
@ -775,6 +814,14 @@ const Notification = new Lang.Class({
|
||||
this.isTransient = isTransient;
|
||||
},
|
||||
|
||||
setShowWhenLocked: function(show) {
|
||||
if (show && !this.isTransient) {
|
||||
throw new Error('ShowWhenLocked can only be set on a transient notification');
|
||||
}
|
||||
|
||||
this.showWhenLocked = show;
|
||||
},
|
||||
|
||||
setUseActionIcons: function(useIcons) {
|
||||
this._useActionIcons = useIcons;
|
||||
},
|
||||
@ -787,8 +834,15 @@ const Notification = new Lang.Class({
|
||||
let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight);
|
||||
let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight);
|
||||
|
||||
alloc.min_size = titleMin;
|
||||
alloc.natural_size = titleNat + this._spacing + bannerNat;
|
||||
if (this._secondaryIcon) {
|
||||
let [secondaryIconMin, secondaryIconNat] = this._secondaryIcon.get_preferred_width(forHeight);
|
||||
|
||||
alloc.min_size = secondaryIconMin + this._spacing + titleMin;
|
||||
alloc.natural_size = secondaryIconNat + this._spacing + titleNat + this._spacing + bannerNat;
|
||||
} else {
|
||||
alloc.min_size = titleMin;
|
||||
alloc.natural_size = titleNat + this._spacing + bannerNat;
|
||||
}
|
||||
},
|
||||
|
||||
_bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) {
|
||||
@ -803,14 +857,42 @@ const Notification = new Lang.Class({
|
||||
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth);
|
||||
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth);
|
||||
|
||||
let rtl = (this._titleDirection == Clutter.TextDirection.RTL);
|
||||
let x = rtl ? availWidth : 0;
|
||||
|
||||
if (this._secondaryIcon) {
|
||||
let [iconMinW, iconNatW] = this._secondaryIcon.get_preferred_width(-1);
|
||||
let [iconMinH, iconNatH] = this._secondaryIcon.get_preferred_height(availWidth);
|
||||
|
||||
let secondaryIconBox = new Clutter.ActorBox();
|
||||
let secondaryIconBoxW = Math.min(iconNatW, availWidth);
|
||||
|
||||
// allocate secondary icon box
|
||||
if (rtl) {
|
||||
secondaryIconBox.x1 = x - secondaryIconBoxW;
|
||||
secondaryIconBox.x2 = x;
|
||||
x = x - (secondaryIconBoxW + this._spacing);
|
||||
} else {
|
||||
secondaryIconBox.x1 = x;
|
||||
secondaryIconBox.x2 = x + secondaryIconBoxW;
|
||||
x = x + secondaryIconBoxW + this._spacing;
|
||||
}
|
||||
secondaryIconBox.y1 = 0;
|
||||
// Using titleNatH ensures that the secondary icon is centered vertically
|
||||
secondaryIconBox.y2 = titleNatH;
|
||||
|
||||
availWidth = availWidth - (secondaryIconBoxW + this._spacing);
|
||||
this._secondaryIcon.allocate(secondaryIconBox, flags);
|
||||
}
|
||||
|
||||
let titleBox = new Clutter.ActorBox();
|
||||
let titleBoxW = Math.min(titleNatW, availWidth);
|
||||
if (this._titleDirection == Clutter.TextDirection.RTL) {
|
||||
if (rtl) {
|
||||
titleBox.x1 = availWidth - titleBoxW;
|
||||
titleBox.x2 = availWidth;
|
||||
} else {
|
||||
titleBox.x1 = 0;
|
||||
titleBox.x2 = titleBoxW;
|
||||
titleBox.x1 = x;
|
||||
titleBox.x2 = titleBox.x1 + titleBoxW;
|
||||
}
|
||||
titleBox.y1 = 0;
|
||||
titleBox.y2 = titleNatH;
|
||||
@ -824,7 +906,7 @@ const Notification = new Lang.Class({
|
||||
} else {
|
||||
let bannerBox = new Clutter.ActorBox();
|
||||
|
||||
if (this._titleDirection == Clutter.TextDirection.RTL) {
|
||||
if (rtl) {
|
||||
bannerBox.x1 = 0;
|
||||
bannerBox.x2 = titleBox.x1 - this._spacing;
|
||||
|
||||
@ -856,7 +938,7 @@ const Notification = new Lang.Class({
|
||||
if (this._canExpandContent()) {
|
||||
this._addBannerBody();
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._updated();
|
||||
this.updated();
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
@ -867,7 +949,7 @@ const Notification = new Lang.Class({
|
||||
(!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification'));
|
||||
},
|
||||
|
||||
_updated: function() {
|
||||
updated: function() {
|
||||
if (this.expanded)
|
||||
this.expand(false);
|
||||
},
|
||||
@ -953,22 +1035,19 @@ const Notification = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(Notification.prototype);
|
||||
|
||||
const Source = new Lang.Class({
|
||||
Name: 'MessageTraySource',
|
||||
const SourceActor = new Lang.Class({
|
||||
Name: 'SourceActor',
|
||||
|
||||
ICON_SIZE: 24,
|
||||
|
||||
_init: function(title) {
|
||||
this.title = title;
|
||||
_init: function(source, size) {
|
||||
this._source = source;
|
||||
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
this._actorDestroyed = true;
|
||||
}));
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
}));
|
||||
this._actorDestroyed = false;
|
||||
|
||||
this._counterLabel = new St.Label();
|
||||
@ -976,19 +1055,20 @@ const Source = new Lang.Class({
|
||||
child: this._counterLabel });
|
||||
this._counterBin.hide();
|
||||
|
||||
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
|
||||
height: this.ICON_SIZE,
|
||||
this._iconBin = new St.Bin({ width: size,
|
||||
height: size,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
this.actor.add_actor(this._iconBin);
|
||||
this.actor.add_actor(this._counterBin);
|
||||
|
||||
this.isTransient = false;
|
||||
this.isChat = false;
|
||||
this.isMuted = false;
|
||||
this._source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||
this._updateCount();
|
||||
},
|
||||
|
||||
this.notifications = [];
|
||||
setIcon: function(icon) {
|
||||
this._iconBin.child = icon;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
@ -1026,20 +1106,49 @@ const Source = new Lang.Class({
|
||||
this._counterBin.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_setCount: function(count, visible) {
|
||||
if (isNaN(parseInt(count)))
|
||||
throw new Error("Invalid notification count: " + count);
|
||||
|
||||
_updateCount: function() {
|
||||
if (this._actorDestroyed)
|
||||
return;
|
||||
|
||||
this._counterBin.visible = visible;
|
||||
this._counterLabel.set_text(count.toString());
|
||||
this._counterBin.visible = this._source.countVisible;
|
||||
this._counterLabel.set_text(this._source.count.toString());
|
||||
},
|
||||
});
|
||||
|
||||
const Source = new Lang.Class({
|
||||
Name: 'MessageTraySource',
|
||||
|
||||
ICON_SIZE: 24,
|
||||
|
||||
_init: function(title, iconName, iconType) {
|
||||
this.title = title;
|
||||
this.iconName = iconName;
|
||||
this.iconType = iconType;
|
||||
|
||||
this.isTransient = false;
|
||||
this.isChat = false;
|
||||
this.isMuted = false;
|
||||
|
||||
this.notifications = [];
|
||||
|
||||
this.mainIcon = new SourceActor(this, this.ICON_SIZE);
|
||||
this._setSummaryIcon(this.createIcon(this.ICON_SIZE));
|
||||
},
|
||||
|
||||
_updateCount: function() {
|
||||
let count = this.notifications.length;
|
||||
this._setCount(count, count > 1);
|
||||
get count() {
|
||||
return this.notifications.length;
|
||||
},
|
||||
|
||||
get unseenCount() {
|
||||
return this.notifications.filter(function(n) { return !n.acknowledged; }).length;
|
||||
},
|
||||
|
||||
get countVisible() {
|
||||
return this.count > 1;
|
||||
},
|
||||
|
||||
countUpdated: function() {
|
||||
this.emit('count-updated');
|
||||
},
|
||||
|
||||
setTransient: function(isTransient) {
|
||||
@ -1058,17 +1167,19 @@ const Source = new Lang.Class({
|
||||
this.emit('muted-changed');
|
||||
},
|
||||
|
||||
// Called to create a new icon actor (of size this.ICON_SIZE).
|
||||
// Must be overridden by the subclass if you do not pass icons
|
||||
// explicitly to the Notification() constructor.
|
||||
createNotificationIcon: function() {
|
||||
throw new Error('no implementation of createNotificationIcon in ' + this);
|
||||
// Called to create a new icon actor.
|
||||
// Provides a sane default implementation, override if you need
|
||||
// something more fancy.
|
||||
createIcon: function(size) {
|
||||
return new St.Icon({ icon_name: this.iconName,
|
||||
icon_type: this.iconType,
|
||||
icon_size: size });
|
||||
},
|
||||
|
||||
// Unlike createNotificationIcon, this always returns the same actor;
|
||||
// Unlike createIcon, this always returns the same actor;
|
||||
// there is only one summary icon actor for a Source.
|
||||
getSummaryIcon: function() {
|
||||
return this.actor;
|
||||
return this.mainIcon.actor;
|
||||
},
|
||||
|
||||
pushNotification: function(notification) {
|
||||
@ -1088,13 +1199,14 @@ const Source = new Lang.Class({
|
||||
if (this.notifications.length == 0)
|
||||
this._lastNotificationRemoved();
|
||||
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
}));
|
||||
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
notification.acknowledged = false;
|
||||
this.pushNotification(notification);
|
||||
if (!this.isMuted)
|
||||
this.emit('notify', notification);
|
||||
@ -1113,16 +1225,12 @@ const Source = new Lang.Class({
|
||||
},
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
// The subclass must call this at least once to set the summary icon.
|
||||
_setSummaryIcon: function(icon) {
|
||||
if (this._iconBin.child)
|
||||
this._iconBin.child.destroy();
|
||||
this._iconBin.child = icon;
|
||||
this.mainIcon.setIcon(icon);
|
||||
},
|
||||
|
||||
// Default implementation is to do nothing, but subclasses can override
|
||||
open: function(notification) {
|
||||
this.emit('opened', notification);
|
||||
},
|
||||
|
||||
destroyNonResidentNotifications: function() {
|
||||
@ -1130,12 +1238,16 @@ const Source = new Lang.Class({
|
||||
if (!this.notifications[i].resident)
|
||||
this.notifications[i].destroy();
|
||||
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
},
|
||||
|
||||
// Default implementation is to destroy this source, but subclasses can override
|
||||
_lastNotificationRemoved: function() {
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
hasResidentNotification: function() {
|
||||
return this.notifications.some(function(n) { return n.resident; });
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Source.prototype);
|
||||
@ -1174,12 +1286,12 @@ const SummaryItem = new Lang.Class({
|
||||
this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false });
|
||||
this.actor.child = this._sourceBox;
|
||||
|
||||
this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview',
|
||||
this.notificationStackView = new St.ScrollView({ style_class: source.isChat ? '' : 'summary-notification-stack-scrollview',
|
||||
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
style_class: 'vfade' });
|
||||
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
|
||||
vertical: true });
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
this.notificationStackView.add_style_class_name('vfade');
|
||||
this.notificationStack = new St.BoxLayout({ style_class: 'summary-notification-stack',
|
||||
vertical: true });
|
||||
this.notificationStackView.add_actor(this.notificationStack);
|
||||
this._stackedNotifications = [];
|
||||
|
||||
@ -1252,7 +1364,7 @@ const SummaryItem = new Lang.Class({
|
||||
},
|
||||
|
||||
prepareNotificationStackForShowing: function() {
|
||||
if (this.notificationStack.get_children().length > 0)
|
||||
if (this.notificationStack.get_n_children() > 0)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this.source.notifications.length; i++) {
|
||||
@ -1261,7 +1373,6 @@ const SummaryItem = new Lang.Class({
|
||||
},
|
||||
|
||||
doneShowingNotificationStack: function() {
|
||||
let notificationActors = this.notificationStack.get_children();
|
||||
for (let i = 0; i < this._stackedNotifications.length; i++) {
|
||||
let stackedNotification = this._stackedNotifications[i];
|
||||
let notification = stackedNotification.notification;
|
||||
@ -1291,7 +1402,7 @@ const SummaryItem = new Lang.Class({
|
||||
this._stackedNotifications.push(stackedNotification);
|
||||
if (!this.source.isChat)
|
||||
notification.enableScrolling(false);
|
||||
if (this.notificationStack.get_children().length > 0)
|
||||
if (this.notificationStack.get_n_children() > 0)
|
||||
notification.setIconVisible(false);
|
||||
this.notificationStack.add(notification.actor);
|
||||
notification.expand(false);
|
||||
@ -1378,7 +1489,7 @@ const MessageTray = new Lang.Class({
|
||||
track_hover: true });
|
||||
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
|
||||
Main.layoutManager.addChrome(this._summaryBoxPointer.actor);
|
||||
|
||||
this._summaryBoxPointerItem = null;
|
||||
this._summaryBoxPointerContentUpdatedId = 0;
|
||||
@ -1437,6 +1548,7 @@ const MessageTray = new Lang.Class({
|
||||
this._overviewVisible = Main.overview.visible;
|
||||
this._notificationRemoved = false;
|
||||
this._reNotifyAfterHideNotification = null;
|
||||
this._inFullscreen = false;
|
||||
|
||||
this._corner = new Clutter.Rectangle({ width: 1,
|
||||
height: 1,
|
||||
@ -1447,11 +1559,12 @@ const MessageTray = new Lang.Class({
|
||||
Main.layoutManager.trackChrome(this._corner);
|
||||
|
||||
Main.layoutManager.trayBox.add_actor(this.actor);
|
||||
this.actor.y = this.actor.height;
|
||||
this.actor.y = 0;
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
Main.layoutManager.trackChrome(this._notificationBin);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
|
||||
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
|
||||
|
||||
this._setSizePosition();
|
||||
|
||||
@ -1476,6 +1589,9 @@ const MessageTray = new Lang.Class({
|
||||
}
|
||||
}));
|
||||
|
||||
this._isScreenLocked = false;
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onScreenLockStatusChanged));
|
||||
|
||||
this._summaryItems = [];
|
||||
// We keep a list of new summary items that were added to the summary since the last
|
||||
// time it was shown to the user. We automatically show the summary to the user if there
|
||||
@ -1582,6 +1698,12 @@ const MessageTray = new Lang.Class({
|
||||
// *first* and not show the summary item until after it hides.
|
||||
// So postpone calling _updateState() a tiny bit.
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateState(); return false; }));
|
||||
|
||||
this.emit('summary-item-added', summaryItem);
|
||||
},
|
||||
|
||||
getSummaryItems: function() {
|
||||
return this._summaryItems;
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source) {
|
||||
@ -1656,6 +1778,11 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationQueue.splice(index, 1);
|
||||
},
|
||||
|
||||
_onScreenLockStatusChanged: function(screenShield, locked) {
|
||||
this._isScreenLocked = locked;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_lock: function() {
|
||||
this._locked = true;
|
||||
},
|
||||
@ -1682,7 +1809,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_onNotify: function(source, notification) {
|
||||
if (this._summaryBoxPointerItem && this._summaryBoxPointerItem.source == source) {
|
||||
if (this._summaryBoxPointerState == State.HIDING)
|
||||
if (this._summaryBoxPointerState == State.HIDING) {
|
||||
// We are in the process of hiding the summary box pointer.
|
||||
// If there is an update for one of the notifications or
|
||||
// a new notification to be added to the notification stack
|
||||
@ -1693,6 +1820,14 @@ const MessageTray = new Lang.Class({
|
||||
// need to be able to re-parent its actor to a different
|
||||
// part of the stage.
|
||||
this._reNotifyAfterHideNotification = notification;
|
||||
} else {
|
||||
// The summary box pointer is showing or shown (otherwise,
|
||||
// this._summaryBoxPointerItem would be null)
|
||||
// Immediately mark the notification as acknowledged, as it's
|
||||
// not going into the queue
|
||||
notification.acknowledged = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1854,7 +1989,7 @@ const MessageTray = new Lang.Class({
|
||||
_onTrayHoverChanged: function() {
|
||||
if (this.actor.hover) {
|
||||
// Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden.
|
||||
if (this._trayState == State.HIDDEN)
|
||||
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
|
||||
return;
|
||||
|
||||
// Don't do anything if this._useLongerTrayLeftTimeout is true, meaning the notification originally
|
||||
@ -1925,6 +2060,11 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onFullscreenChanged: function(obj, state) {
|
||||
this._inFullscreen = state;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onStatusChanged: function(status) {
|
||||
if (status == GnomeSession.PresenceStatus.BUSY) {
|
||||
// remove notification and allow the summary to be closed now
|
||||
@ -1981,18 +2121,36 @@ const MessageTray = new Lang.Class({
|
||||
// at the present time.
|
||||
_updateState: function() {
|
||||
// Notifications
|
||||
let notificationUrgent = this._notificationQueue.length > 0 && this._notificationQueue[0].urgency == Urgency.CRITICAL;
|
||||
let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent);
|
||||
let notificationQueue = this._notificationQueue.filter(Lang.bind(this, function(notification) {
|
||||
if (this._isScreenLocked)
|
||||
return notification.showWhenLocked;
|
||||
else
|
||||
return true;
|
||||
}));
|
||||
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
|
||||
// notificationsLimited is false when the screen is locked, because they go through
|
||||
// different filtering, and we want to show non urgent messages at times
|
||||
let notificationsLimited = (this._busy || this._inFullscreen) && !this._isScreenLocked;
|
||||
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent);
|
||||
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
|
||||
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
|
||||
let notificationExpanded = this._notificationBin.y < 0;
|
||||
let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved;
|
||||
let notificationExpanded = this._notificationBin.y < - this.actor.height;
|
||||
let notificationExpired = this._notificationTimeoutId == 0 &&
|
||||
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
|
||||
!this._pointerInTray &&
|
||||
!this._locked &&
|
||||
!(this._pointerInKeyboard && notificationExpanded);
|
||||
let notificationLockedOut = this._isScreenLocked && (this._notification && !this._notification.showWhenLocked);
|
||||
let notificationMustClose = this._notificationRemoved || notificationLockedOut || notificationExpired;
|
||||
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
|
||||
|
||||
if (this._notificationState == State.HIDDEN) {
|
||||
if (canShowNotification)
|
||||
this._showNotification();
|
||||
if (canShowNotification) {
|
||||
this._showNotification(nextNotification);
|
||||
this._notificationQueue.splice(this._notificationQueue.indexOf(nextNotification), 1);
|
||||
}
|
||||
} else if (this._notificationState == State.SHOWN) {
|
||||
if (notificationExpired)
|
||||
if (notificationMustClose)
|
||||
this._hideNotification();
|
||||
else if (notificationPinned && !notificationExpanded)
|
||||
this._expandNotification(false);
|
||||
@ -2013,12 +2171,12 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
|
||||
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|
||||
|| notificationsVisible;
|
||||
|| notificationsVisible || this._isScreenLocked;
|
||||
|
||||
if (this._summaryState == State.HIDDEN && !mustHideSummary) {
|
||||
if (summarySummoned) {
|
||||
this._showSummary(0);
|
||||
} else if (notificationsDone && !this._busy) {
|
||||
} else if (notificationsDone && !this._busy && !this._inFullscreen) {
|
||||
if (this._backFromAway && this._unseenNotifications.length > 0)
|
||||
this._showSummary(LONGER_SUMMARY_TIMEOUT);
|
||||
else if (this._newSummaryItems.length > 0)
|
||||
@ -2064,8 +2222,7 @@ const MessageTray = new Lang.Class({
|
||||
// Tray itself
|
||||
let trayIsVisible = (this._trayState == State.SHOWING ||
|
||||
this._trayState == State.SHOWN);
|
||||
let trayShouldBeVisible = (!notificationsDone ||
|
||||
this._summaryState == State.SHOWING ||
|
||||
let trayShouldBeVisible = (this._summaryState == State.SHOWING ||
|
||||
this._summaryState == State.SHOWN);
|
||||
if (!trayIsVisible && trayShouldBeVisible)
|
||||
this._showTray();
|
||||
@ -2105,7 +2262,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_hideTray: function() {
|
||||
this._tween(this.actor, '_trayState', State.HIDDEN,
|
||||
{ y: this.actor.height,
|
||||
{ y: 0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -2131,8 +2288,8 @@ const MessageTray = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_showNotification: function() {
|
||||
this._notification = this._notificationQueue.shift();
|
||||
_showNotification: function(notification) {
|
||||
this._notification = notification;
|
||||
this._unseenNotifications.push(this._notification);
|
||||
if (this._idleMonitorWatchId == 0)
|
||||
this._idleMonitorWatchId = this.idleMonitor.add_watch(1000,
|
||||
@ -2142,7 +2299,7 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationBin.child = this._notification.actor;
|
||||
|
||||
this._notificationBin.opacity = 0;
|
||||
this._notificationBin.y = this.actor.height;
|
||||
this._notificationBin.y = 0;
|
||||
this._notificationBin.show();
|
||||
|
||||
this._updateShowingNotification();
|
||||
@ -2162,6 +2319,8 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateShowingNotification: function() {
|
||||
this._notification.acknowledged = true;
|
||||
|
||||
Tweener.removeTweens(this._notificationBin);
|
||||
|
||||
// We auto-expand notifications with CRITICAL urgency.
|
||||
@ -2177,7 +2336,8 @@ const MessageTray = new Lang.Class({
|
||||
// We tween all notifications to full opacity. This ensures that both new notifications and
|
||||
// notifications that might have been in the process of hiding get full opacity.
|
||||
//
|
||||
// We tween any notification showing in the banner mode to banner height (this._notificationBin.y = 0).
|
||||
// We tween any notification showing in the banner mode to banner height
|
||||
// (this._notificationBin.y = -this.actor.height).
|
||||
// This ensures that both new notifications and notifications in the banner mode that might
|
||||
// have been in the process of hiding are shown with the banner height.
|
||||
//
|
||||
@ -2194,7 +2354,7 @@ const MessageTray = new Lang.Class({
|
||||
onCompleteScope: this
|
||||
};
|
||||
if (!this._notification.expanded)
|
||||
tweenParams.y = 0;
|
||||
tweenParams.y = - this.actor.height;
|
||||
|
||||
this._tween(this._notificationBin, '_notificationState', State.SHOWN, tweenParams);
|
||||
},
|
||||
@ -2283,7 +2443,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_onNotificationExpanded: function() {
|
||||
let expandedY = this.actor.height - this._notificationBin.height;
|
||||
let expandedY = - this._notificationBin.height;
|
||||
|
||||
// Don't animate the notification to its new position if it has shrunk:
|
||||
// there will be a very visible "gap" that breaks the illusion.
|
||||
@ -2363,10 +2523,17 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
if (this._clickedSummaryItemMouseButton == 1) {
|
||||
this._notificationQueue = this._notificationQueue.filter( Lang.bind(this,
|
||||
function(notification) {
|
||||
return this._summaryBoxPointerItem.source != notification.source;
|
||||
}));
|
||||
let newQueue = [];
|
||||
for (let i = 0; i < this._notificationQueue.length; i++) {
|
||||
let notification = this._notificationQueue[i];
|
||||
let sameSource = this._summaryBoxPointerItem.source == notification.source;
|
||||
if (sameSource)
|
||||
notification.acknowledged = true;
|
||||
else
|
||||
newQueue.push(notification);
|
||||
}
|
||||
this._notificationQueue = newQueue;
|
||||
|
||||
this._summaryBoxPointerItem.prepareNotificationStackForShowing();
|
||||
this._summaryBoxPointer.bin.child = this._summaryBoxPointerItem.notificationStackView;
|
||||
this._summaryBoxPointerItem.scrollTo(St.Side.BOTTOM);
|
||||
@ -2391,24 +2558,23 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._summaryBoxPointerState = State.SHOWING;
|
||||
this._clickedSummaryItem.actor.add_style_pseudo_class('selected');
|
||||
this._summaryBoxPointer.show(true, Lang.bind(this, function() {
|
||||
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
|
||||
this._summaryBoxPointerState = State.SHOWN;
|
||||
}));
|
||||
},
|
||||
|
||||
_onSummaryBoxPointerContentUpdated: function() {
|
||||
if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
|
||||
if (this._summaryBoxPointerItem.notificationStack.get_n_children() == 0)
|
||||
this._hideSummaryBoxPointer();
|
||||
this._adjustSummaryBoxPointerPosition();
|
||||
|
||||
},
|
||||
|
||||
_adjustSummaryBoxPointerPosition: function() {
|
||||
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
|
||||
if (!this._clickedSummaryItem)
|
||||
return;
|
||||
|
||||
this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
|
||||
this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0);
|
||||
},
|
||||
|
||||
_unsetClickedSummaryItem: function() {
|
||||
@ -2431,7 +2597,7 @@ const MessageTray = new Lang.Class({
|
||||
// We should be sure to hide the box pointer if all notifications in it are destroyed while
|
||||
// it is hiding, so that we don't show an an animation of an empty blob being hidden.
|
||||
if (this._summaryBoxPointerState == State.HIDING &&
|
||||
this._summaryBoxPointerItem.notificationStack.get_children().length == 0) {
|
||||
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) {
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
return;
|
||||
}
|
||||
@ -2446,7 +2612,7 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
this._hideSummaryBoxPointerCompleted();
|
||||
} else {
|
||||
this._summaryBoxPointer.hide(true, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
|
||||
this._summaryBoxPointer.hide(BoxPointer.PopupAnimation.FULL, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
|
||||
}
|
||||
},
|
||||
|
||||
@ -2481,21 +2647,15 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(MessageTray.prototype);
|
||||
|
||||
const SystemNotificationSource = new Lang.Class({
|
||||
Name: 'SystemNotificationSource',
|
||||
Extends: Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("System Information"));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'dialog-information',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
this.parent(_("System Information"), 'dialog-information', St.IconType.SYMBOLIC);
|
||||
this.setTransient(true);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
|
@ -35,7 +35,9 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null });
|
||||
styleClass: null,
|
||||
parentActor: Main.uiGroup
|
||||
});
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this._hasModal = false;
|
||||
@ -45,7 +47,7 @@ const ModalDialog = new Lang.Class({
|
||||
x: 0,
|
||||
y: 0,
|
||||
accessible_role: Atk.Role.DIALOG });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
params.parentActor.add_actor(this._group);
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
@ -54,15 +56,15 @@ const ModalDialog = new Lang.Class({
|
||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||
|
||||
this._actionKeys = {};
|
||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||
|
||||
this._backgroundBin = new St.Bin();
|
||||
this._group.add_actor(this._backgroundBin);
|
||||
|
||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
if (params.styleClass != null) {
|
||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
||||
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||
}
|
||||
|
||||
if (!this._shellReactive) {
|
||||
@ -75,29 +77,29 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||
stack.add_actor(this._eventBlocker);
|
||||
stack.add_actor(this._dialogLayout);
|
||||
stack.add_actor(this.dialogLayout);
|
||||
} else {
|
||||
this._backgroundBin.child = this._dialogLayout;
|
||||
this._backgroundBin.child = this.dialogLayout;
|
||||
}
|
||||
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this._dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
this.dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
visible: false,
|
||||
vertical: false });
|
||||
this._dialogLayout.add(this._buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
this.dialogLayout.add(this._buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
|
||||
global.focus_manager.add_group(this._dialogLayout);
|
||||
this._initialKeyFocus = this._dialogLayout;
|
||||
global.focus_manager.add_group(this.dialogLayout);
|
||||
this._initialKeyFocus = this.dialogLayout;
|
||||
this._initialKeyFocusDestroyId = 0;
|
||||
this._savedKeyFocus = null;
|
||||
},
|
||||
@ -106,6 +108,10 @@ const ModalDialog = new Lang.Class({
|
||||
this._group.destroy();
|
||||
},
|
||||
|
||||
setActionKey: function(key, action) {
|
||||
this._actionKeys[key] = action;
|
||||
},
|
||||
|
||||
setButtons: function(buttons) {
|
||||
let hadChildren = this._buttonLayout.get_children() > 0;
|
||||
|
||||
@ -119,11 +125,17 @@ const ModalDialog = new Lang.Class({
|
||||
let label = buttonInfo['label'];
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
let isDefault = buttonInfo['default'];
|
||||
|
||||
if (isDefault && !key)
|
||||
key = Clutter.KEY_Return;
|
||||
|
||||
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
if (isDefault)
|
||||
buttonInfo.button.add_style_pseudo_class('default');
|
||||
|
||||
let x_alignment;
|
||||
if (buttons.length == 1)
|
||||
@ -167,12 +179,16 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
||||
let symbol = keyPressEvent.get_key_symbol();
|
||||
_onKeyReleaseEvent: function(object, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
let action = this._actionKeys[symbol];
|
||||
|
||||
if (action)
|
||||
if (action) {
|
||||
action();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onGroupDestroy: function() {
|
||||
@ -180,14 +196,14 @@ const ModalDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_fadeOpen: function() {
|
||||
let monitor = Main.layoutManager.focusMonitor;
|
||||
let monitor = Main.layoutManager.currentMonitor;
|
||||
|
||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
||||
|
||||
this.state = State.OPENING;
|
||||
|
||||
this._dialogLayout.opacity = 255;
|
||||
this.dialogLayout.opacity = 255;
|
||||
if (this._lightbox)
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
@ -211,7 +227,7 @@ const ModalDialog = new Lang.Class({
|
||||
this._initialKeyFocus = actor;
|
||||
|
||||
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._initialKeyFocus = this._dialogLayout;
|
||||
this._initialKeyFocus = this.dialogLayout;
|
||||
this._initialKeyFocusDestroyId = 0;
|
||||
}));
|
||||
},
|
||||
@ -304,7 +320,7 @@ const ModalDialog = new Lang.Class({
|
||||
return;
|
||||
|
||||
this.popModal(timestamp);
|
||||
Tweener.addTween(this._dialogLayout,
|
||||
Tweener.addTween(this.dialogLayout,
|
||||
{ opacity: 0,
|
||||
time: FADE_OUT_DIALOG_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
|
@ -147,7 +147,7 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
|
||||
this._okButton = { label: _("Connect"),
|
||||
action: Lang.bind(this, this._onOk),
|
||||
key: Clutter.KEY_Return,
|
||||
default: true
|
||||
};
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
@ -165,11 +165,6 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
}
|
||||
|
||||
this._okButton.button.reactive = valid;
|
||||
this._okButton.button.can_focus = valid;
|
||||
if (valid)
|
||||
this._okButton.button.remove_style_pseudo_class('disabled');
|
||||
else
|
||||
this._okButton.button.add_style_pseudo_class('disabled');
|
||||
},
|
||||
|
||||
_onOk: function() {
|
||||
@ -683,7 +678,10 @@ const NetworkAgent = new Lang.Class({
|
||||
try {
|
||||
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
let path = GLib.build_filenamev([Config.LIBEXECDIR, binary]);
|
||||
let path = binary;
|
||||
if (!GLib.path_is_absolute(path)) {
|
||||
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
|
||||
}
|
||||
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
|
||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
@ -87,6 +88,21 @@ const rewriteRules = {
|
||||
]
|
||||
};
|
||||
|
||||
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'bluetooth-applet': 'bluetooth',
|
||||
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
|
||||
// when moved to control center
|
||||
'gnome-sound-applet': 'volume',
|
||||
'nm-applet': 'network',
|
||||
'gnome-power-manager': 'battery',
|
||||
'keyboard': 'keyboard',
|
||||
'a11y-keyboard': 'a11y',
|
||||
'kbd-scrolllock': 'keyboard',
|
||||
'kbd-numlock': 'keyboard',
|
||||
'kbd-capslock': 'keyboard',
|
||||
'ibus-ui-gtk': 'keyboard'
|
||||
};
|
||||
|
||||
const NotificationDaemon = new Lang.Class({
|
||||
Name: 'NotificationDaemon',
|
||||
|
||||
@ -99,18 +115,19 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._notifications = {};
|
||||
this._busProxy = new Bus();
|
||||
|
||||
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
this._trayManager = new Shell.TrayManager();
|
||||
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
|
||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon, hints, size) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
|
||||
_iconForNotificationData: function(icon, hints) {
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
@ -121,20 +138,18 @@ const NotificationDaemon = new Lang.Class({
|
||||
// a large image.
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return textureCache.load_uri_async(icon, size, size);
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/') {
|
||||
let uri = GLib.filename_to_uri(icon, null);
|
||||
return textureCache.load_uri_async(uri, size, size);
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
} else
|
||||
return new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
} else if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
|
||||
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
} else if (hints['image-path']) {
|
||||
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||
} else {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
@ -146,9 +161,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
stockIcon = 'gtk-dialog-error';
|
||||
break;
|
||||
}
|
||||
return new St.Icon({ icon_name: stockIcon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
}
|
||||
},
|
||||
|
||||
@ -221,12 +234,19 @@ const NotificationDaemon = new Lang.Class({
|
||||
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
|
||||
let id;
|
||||
|
||||
for (let hint in hints) {
|
||||
// unpack the variants
|
||||
hints[hint] = hints[hint].deep_unpack();
|
||||
}
|
||||
|
||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||
|
||||
// Filter out chat, presence, calls and invitation notifications from
|
||||
// Empathy, since we handle that information from telepathyClient.js
|
||||
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
||||
hints['category'] == 'x-empathy.im.room-invitation' ||
|
||||
hints['category'] == 'x-empathy.call.incoming' ||
|
||||
hints['category'] == 'x-empathy.call.incoming"' ||
|
||||
hints['category'] == 'x-empathy.transfer.incoming' ||
|
||||
hints['category'] == 'x-empathy.im.subscription-request' ||
|
||||
hints['category'] == 'presence.online' ||
|
||||
hints['category'] == 'presence.offline')) {
|
||||
@ -249,13 +269,6 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
for (let hint in hints) {
|
||||
// unpack the variants
|
||||
hints[hint] = hints[hint].deep_unpack();
|
||||
}
|
||||
|
||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||
|
||||
// Be compatible with the various hints for image data and image path
|
||||
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
|
||||
|
||||
@ -341,7 +354,10 @@ const NotificationDaemon = new Lang.Class({
|
||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||
ndata.actions, ndata.hints, ndata.notification];
|
||||
|
||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
let iconActor = new St.Icon({ gicon: gicon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: source.ICON_SIZE });
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
@ -421,8 +437,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
||||
source.processNotification(notification, sourceIconActor);
|
||||
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
||||
source.processNotification(notification, sourceGIcon);
|
||||
},
|
||||
|
||||
CloseNotification: function(id) {
|
||||
@ -483,7 +499,11 @@ const NotificationDaemon = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
|
||||
let wmClass = icon.wm_class.toLowerCase();
|
||||
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
|
||||
return;
|
||||
|
||||
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
|
||||
},
|
||||
|
||||
_onTrayIconRemoved: function(o, icon) {
|
||||
@ -534,11 +554,10 @@ const Source = new Lang.Class({
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
processNotification: function(notification, icon) {
|
||||
if (!this.app)
|
||||
this._setApp();
|
||||
if (!this.app && icon)
|
||||
this._setSummaryIcon(icon);
|
||||
processNotification: function(notification, gicon) {
|
||||
if (gicon)
|
||||
this._gicon = gicon;
|
||||
this._setSummaryIcon(this.createIcon(this.ICON_SIZE));
|
||||
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
if (notification.resident && this.app && tracker.focus_app == this.app)
|
||||
@ -578,11 +597,27 @@ const Source = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_getApp: function() {
|
||||
let app;
|
||||
|
||||
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||
if (app != null)
|
||||
return app;
|
||||
|
||||
if (this.trayIcon) {
|
||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
|
||||
if (app != null)
|
||||
return app;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_setApp: function() {
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||
this.app = this._getApp();
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
@ -590,10 +625,20 @@ const Source = new Lang.Class({
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this.trayIcon) {
|
||||
this.useNotificationIcon = false;
|
||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
||||
this._setSummaryIcon(this.createIcon(this.ICON_SIZE));
|
||||
}
|
||||
},
|
||||
|
||||
setTitle: function(title) {
|
||||
// Do nothing if .app is set, we don't want to override the
|
||||
// app name with whatever is provided through libnotify (usually
|
||||
// garbage)
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.parent(title);
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
this.destroyNonResidentNotifications();
|
||||
this.openApp();
|
||||
@ -622,5 +667,20 @@ const Source = new Lang.Class({
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
if (this.trayIcon) {
|
||||
return new Clutter.Clone({ width: size,
|
||||
height: size,
|
||||
source: this.trayIcon });
|
||||
} else if (this.app) {
|
||||
return this.app.create_icon_texture(size);
|
||||
} else if (this._gicon) {
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_size: size });
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -11,10 +11,8 @@ const Shell = imports.gi.Shell;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const ContactDisplay = imports.ui.contactDisplay;
|
||||
const Dash = imports.ui.dash;
|
||||
const DND = imports.ui.dnd;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Panel = imports.ui.panel;
|
||||
@ -77,6 +75,7 @@ const ShellInfo = new Lang.Class({
|
||||
let notification = null;
|
||||
if (this._source.notifications.length == 0) {
|
||||
notification = new MessageTray.Notification(this._source, text, null);
|
||||
notification.setShowWhenLocked(true);
|
||||
} else {
|
||||
notification = this._source.notifications[0];
|
||||
notification.update(text, null, { clear: true });
|
||||
@ -99,10 +98,8 @@ const ShellInfo = new Lang.Class({
|
||||
const Overview = new Lang.Class({
|
||||
Name: 'Overview',
|
||||
|
||||
_init : function(params) {
|
||||
params = Params.parse(params, { isDummy: false });
|
||||
|
||||
this.isDummy = params.isDummy;
|
||||
_init : function() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
|
||||
// We only have an overview in user sessions, so
|
||||
// create a dummy overview in other cases
|
||||
@ -210,7 +207,6 @@ const Overview = new Lang.Class({
|
||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
|
||||
|
||||
// Load remote search providers provided by applications
|
||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
|
||||
|
126
js/ui/panel.js
@ -4,6 +4,7 @@ const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -13,7 +14,6 @@ const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const DND = imports.ui.dnd;
|
||||
const Layout = imports.ui.layout;
|
||||
@ -31,33 +31,6 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
const SPINNER_ANIMATION_TIME = 0.2;
|
||||
|
||||
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
|
||||
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.XKBIndicator,
|
||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||
};
|
||||
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
|
||||
|
||||
try {
|
||||
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
|
||||
} catch(e) {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
const GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
|
||||
const GDM_STATUS_AREA_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.XKBIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
|
||||
};
|
||||
|
||||
// To make sure the panel corners blend nicely with the panel,
|
||||
// we draw background and borders the same way, e.g. drawing
|
||||
// them as filled shapes from the outside inwards instead of
|
||||
@ -373,6 +346,7 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._stop = true;
|
||||
this.actor.reactive = true;
|
||||
Tweener.addTween(this._spinner.actor,
|
||||
{ opacity: 0,
|
||||
time: SPINNER_ANIMATION_TIME,
|
||||
@ -387,6 +361,7 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
startAnimation: function() {
|
||||
this._stop = false;
|
||||
this.actor.reactive = false;
|
||||
this._spinner.actor.show();
|
||||
},
|
||||
|
||||
@ -492,6 +467,13 @@ const AppMenuButton = new Lang.Class({
|
||||
this._sync();
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
if (locked)
|
||||
this.hide();
|
||||
else
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let focusedApp = tracker.focus_app;
|
||||
@ -778,10 +760,11 @@ const PanelCorner = new Lang.Class({
|
||||
return null;
|
||||
|
||||
// Start at the back and work backward
|
||||
let index = children.length - 1;
|
||||
while (!children[index].visible && index >= 0)
|
||||
index--;
|
||||
|
||||
let index;
|
||||
for (index = children.length - 1; index >= 0; index--) {
|
||||
if (children[index].visible)
|
||||
break;
|
||||
}
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
@ -802,10 +785,11 @@ const PanelCorner = new Lang.Class({
|
||||
return null;
|
||||
|
||||
// Start at the front and work forward
|
||||
let index = 0;
|
||||
while (!children[index].visible && index < children.length)
|
||||
index++;
|
||||
|
||||
let index;
|
||||
for (index = 0; index < children.length; index++) {
|
||||
if (children[index].visible)
|
||||
break;
|
||||
}
|
||||
if (index == children.length)
|
||||
return null;
|
||||
|
||||
@ -933,6 +917,8 @@ const Panel = new Lang.Class({
|
||||
this.actor.remove_style_class_name('in-overview');
|
||||
}));
|
||||
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onLockStateChanged));
|
||||
|
||||
this._menus = new PopupMenu.PopupMenuManager(this);
|
||||
|
||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||
@ -961,7 +947,7 @@ const Panel = new Lang.Class({
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
|
||||
/* Button on the left side of the panel. */
|
||||
if (global.session_type == Shell.SessionType.USER) {
|
||||
if (Main.sessionMode.hasOverview) {
|
||||
this._activitiesButton = new ActivitiesButton();
|
||||
this._activities = this._activitiesButton.actor;
|
||||
this._leftBox.add(this._activities);
|
||||
@ -969,31 +955,18 @@ const Panel = new Lang.Class({
|
||||
// The activities button has a pretend menu, so as to integrate
|
||||
// more cleanly with the rest of the panel
|
||||
this._menus.addMenu(this._activitiesButton.menu);
|
||||
}
|
||||
|
||||
if (Main.sessionMode.hasAppMenu) {
|
||||
this._appMenu = new AppMenuButton(this._menus);
|
||||
this._leftBox.add(this._appMenu.actor);
|
||||
}
|
||||
|
||||
/* center */
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
|
||||
else
|
||||
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
|
||||
this._dateMenu = new DateMenu.DateMenuButton();
|
||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
||||
this._menus.addMenu(this._dateMenu.menu);
|
||||
|
||||
/* right */
|
||||
if (global.session_type == Shell.SessionType.GDM) {
|
||||
this._status_area_order = GDM_STATUS_AREA_ORDER;
|
||||
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION;
|
||||
} else {
|
||||
this._status_area_order = STANDARD_STATUS_AREA_ORDER;
|
||||
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
@ -1112,10 +1085,19 @@ const Panel = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
openAppMenu: function() {
|
||||
let menu = this._appMenu.menu;
|
||||
if (!this._appMenu.actor.reactive || menu.isOpen)
|
||||
return;
|
||||
|
||||
menu.open();
|
||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
startStatusArea: function() {
|
||||
for (let i = 0; i < this._status_area_order.length; i++) {
|
||||
let role = this._status_area_order[i];
|
||||
let constructor = this._status_area_shell_implementation[role];
|
||||
for (let i = 0; i < Main.sessionMode.statusArea.order.length; i++) {
|
||||
let role = Main.sessionMode.statusArea.order[i];
|
||||
let constructor = Main.sessionMode.statusArea.implementation[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
continue;
|
||||
@ -1153,35 +1135,27 @@ const Panel = new Lang.Class({
|
||||
if (!position)
|
||||
position = 0;
|
||||
this._insertStatusItem(indicator.actor, position);
|
||||
this._menus.addMenu(indicator.menu);
|
||||
if (indicator.menu)
|
||||
this._menus.addMenu(indicator.menu);
|
||||
|
||||
this._statusArea[role] = indicator;
|
||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||
this._statusArea[role] = null;
|
||||
delete this._statusArea[role];
|
||||
emitter.disconnect(destroyId);
|
||||
}));
|
||||
|
||||
return indicator;
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon, role) {
|
||||
if (this._status_area_shell_implementation[role]) {
|
||||
// This icon is legacy, and replaced by a Shell version
|
||||
// Hide it
|
||||
return;
|
||||
}
|
||||
_onLockStateChanged: function(shield, locked) {
|
||||
if (this._activitiesButton)
|
||||
this._activitiesButton.setLockedState(locked);
|
||||
if (this._appMenu)
|
||||
this._appMenu.setLockedState(locked);
|
||||
if (this._dateMenu)
|
||||
this._dateMenu.setLockedState(locked);
|
||||
|
||||
icon.height = PANEL_ICON_SIZE;
|
||||
let buttonBox = new PanelMenu.ButtonBox();
|
||||
let box = buttonBox.actor;
|
||||
box.add_actor(icon);
|
||||
|
||||
this._insertStatusItem(box, this._status_area_order.indexOf(role));
|
||||
},
|
||||
|
||||
_onTrayIconRemoved: function(o, icon) {
|
||||
let box = icon.get_parent();
|
||||
if (box && box._delegate instanceof PanelMenu.ButtonBox)
|
||||
box.destroy();
|
||||
for (let id in this._statusArea)
|
||||
this._statusArea[id].setLockedState(locked);
|
||||
},
|
||||
});
|
||||
|
@ -145,6 +145,13 @@ const Button = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
// default behaviour is to hide completely
|
||||
if (locked)
|
||||
this.menu.close();
|
||||
this.actor.visible = !locked;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (!this.menu)
|
||||
return;
|
||||
|
@ -189,7 +189,7 @@ const PlacesManager = new Lang.Class({
|
||||
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
||||
this._updateDevices();
|
||||
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']);
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']);
|
||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
this._bookmarkTimeoutId = 0;
|
||||
@ -365,12 +365,13 @@ const PlaceSearchProvider = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("PLACES & DEVICES"));
|
||||
this.placesManager = new PlacesManager();
|
||||
},
|
||||
|
||||
getResultMetas: function(resultIds) {
|
||||
getResultMetas: function(resultIds, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < resultIds.length; i++) {
|
||||
let placeInfo = Main.placesManager.lookupPlaceById(resultIds[i]);
|
||||
let placeInfo = this.placesManager.lookupPlaceById(resultIds[i]);
|
||||
if (!placeInfo)
|
||||
metas.push(null);
|
||||
else
|
||||
@ -381,24 +382,22 @@ const PlaceSearchProvider = new Lang.Class({
|
||||
}
|
||||
});
|
||||
}
|
||||
return metas;
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
let placeInfo = Main.placesManager.lookupPlaceById(id);
|
||||
let placeInfo = this.placesManager.lookupPlaceById(id);
|
||||
placeInfo.launch(params);
|
||||
},
|
||||
|
||||
_compareResultMeta: function (idA, idB) {
|
||||
let infoA = Main.placesManager.lookupPlaceById(idA);
|
||||
let infoB = Main.placesManager.lookupPlaceById(idB);
|
||||
let infoA = this.placesManager.lookupPlaceById(idA);
|
||||
let infoB = this.placesManager.lookupPlaceById(idB);
|
||||
return infoA.name.localeCompare(infoB.name);
|
||||
},
|
||||
|
||||
_searchPlaces: function(places, terms) {
|
||||
let multiplePrefixResults = [];
|
||||
let prefixResults = [];
|
||||
let multipleSubstringResults = [];
|
||||
let substringResults = [];
|
||||
|
||||
terms = terms.map(String.toLowerCase);
|
||||
@ -406,29 +405,26 @@ const PlaceSearchProvider = new Lang.Class({
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
let place = places[i];
|
||||
let mtype = place.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.MULTIPLE_PREFIX)
|
||||
multiplePrefixResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.PREFIX)
|
||||
if (mtype == Search.MatchType.PREFIX)
|
||||
prefixResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
|
||||
multipleSubstringResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringResults.push(place.id);
|
||||
}
|
||||
multiplePrefixResults.sort(this._compareResultMeta);
|
||||
prefixResults.sort(this._compareResultMeta);
|
||||
multipleSubstringResults.sort(this._compareResultMeta);
|
||||
substringResults.sort(this._compareResultMeta);
|
||||
return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults)));
|
||||
prefixResults.sort(Lang.bind(this, this._compareResultMeta));
|
||||
substringResults.sort(Lang.bind(this, this._compareResultMeta));
|
||||
|
||||
this.searchSystem.pushResults(this, prefixResults.concat(substringResults));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
let places = Main.placesManager.getAllPlaces();
|
||||
return this._searchPlaces(places, terms);
|
||||
let places = this.placesManager.getAllPlaces();
|
||||
this._searchPlaces(places, terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
|
||||
return this._searchPlaces(places, terms);
|
||||
let places = previousResults.map(Lang.bind(this, function(id) {
|
||||
return this.placesManager.lookupPlaceById(id);
|
||||
}));
|
||||
this._searchPlaces(places, terms);
|
||||
}
|
||||
});
|
||||
|
@ -135,17 +135,17 @@ const AuthenticationDialog = new Lang.Class({
|
||||
|
||||
this._onUserChanged();
|
||||
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
|
||||
messageBox.add(this._passwordBox);
|
||||
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||
this._passwordBox.add(this._passwordLabel);
|
||||
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: "",
|
||||
can_focus: true});
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||
this._passwordBox.add(this._passwordEntry,
|
||||
{expand: true });
|
||||
{ expand: true });
|
||||
this.setInitialKeyFocus(this._passwordEntry);
|
||||
this._passwordBox.hide();
|
||||
|
||||
@ -167,6 +167,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
*/
|
||||
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
|
||||
text: 'abc'});
|
||||
this._nullMessageLabel.add_style_class_name('hidden');
|
||||
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._nullMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._nullMessageLabel);
|
||||
@ -177,7 +178,8 @@ const AuthenticationDialog = new Lang.Class({
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
|
||||
this._doneEmitted = false;
|
||||
@ -268,7 +270,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
|
||||
_onSessionRequest: function(session, request, echo_on) {
|
||||
// Cheap localization trick
|
||||
if (request == 'Password:')
|
||||
if (request == 'Password:' || request == 'Password: ')
|
||||
this._passwordLabel.set_text(_("Password:"));
|
||||
else
|
||||
this._passwordLabel.set_text(request);
|
||||
|
@ -134,12 +134,7 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
this.sensitive = sensitive;
|
||||
this.actor.reactive = sensitive;
|
||||
this.actor.can_focus = sensitive;
|
||||
|
||||
if (sensitive)
|
||||
this.actor.remove_style_pseudo_class('insensitive');
|
||||
else
|
||||
this.actor.add_style_pseudo_class('insensitive');
|
||||
this.emit('sensitive-changed', sensitive);
|
||||
},
|
||||
|
||||
@ -546,6 +541,10 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
this._slider.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('notify::mapped', Lang.bind(this, function() {
|
||||
if (!this.actor.mapped)
|
||||
this._endDragging();
|
||||
}));
|
||||
|
||||
this._releaseId = this._motionId = 0;
|
||||
this._dragging = false;
|
||||
@ -769,12 +768,10 @@ const PopupSwitchMenuItem = new Lang.Class({
|
||||
this._statusLabel.text = text;
|
||||
this._statusBin.child = this._statusLabel;
|
||||
this.actor.reactive = false;
|
||||
this.actor.can_focus = false;
|
||||
this.actor.accessible_role = Atk.Role.MENU_ITEM;
|
||||
} else {
|
||||
this._statusBin.child = this._switch.actor;
|
||||
this.actor.reactive = true;
|
||||
this.actor.can_focus = true;
|
||||
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
|
||||
}
|
||||
this.checkAccessibleState();
|
||||
@ -871,6 +868,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
this._activeMenuItem = null;
|
||||
this._childMenus = [];
|
||||
this._settingsActions = { };
|
||||
},
|
||||
|
||||
addAction: function(title, callback) {
|
||||
@ -884,8 +882,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
},
|
||||
|
||||
addSettingsAction: function(title, desktopFile) {
|
||||
// Don't allow user settings to get edited unless we're in a user session
|
||||
if (global.session_type != Shell.SessionType.USER)
|
||||
if (!Main.sessionMode.allowSettings)
|
||||
return null;
|
||||
|
||||
let menuItem = this.addAction(title, function() {
|
||||
@ -899,11 +896,21 @@ const PopupMenuBase = new Lang.Class({
|
||||
Main.overview.hide();
|
||||
app.activate();
|
||||
});
|
||||
|
||||
this._settingsActions[desktopFile] = menuItem;
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
setSettingsVisibility: function(visible) {
|
||||
for (let id in this._settingsActions) {
|
||||
let item = this._settingsActions[id];
|
||||
item.actor.visible = visible;
|
||||
}
|
||||
},
|
||||
|
||||
isEmpty: function() {
|
||||
return this.box.get_children().length == 0;
|
||||
return this.box.get_n_children() == 0;
|
||||
},
|
||||
|
||||
isChildMenu: function(menu) {
|
||||
@ -939,7 +946,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
_connectSubMenuSignals: function(object, menu) {
|
||||
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
|
||||
this.emit('activate');
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
}));
|
||||
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
|
||||
if (this._activeMenuItem && this._activeMenuItem != submenuItem)
|
||||
@ -974,7 +981,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
}));
|
||||
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
||||
this.emit('activate', menuItem);
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
}));
|
||||
// the weird name is to avoid a conflict with some random property
|
||||
// the menuItem may have, called destroyId
|
||||
@ -1047,7 +1054,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
menuItem._closingId = this.connect('open-state-changed',
|
||||
function(self, open) {
|
||||
if (!open)
|
||||
menuItem.close(false);
|
||||
menuItem.close(BoxPointer.PopupAnimation.FADE);
|
||||
});
|
||||
menuItem.connect('destroy', Lang.bind(this, function() {
|
||||
menuItem.disconnect(menuItem._subMenuActivateId);
|
||||
@ -1064,7 +1071,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
this._connectItemSignals(menuItem);
|
||||
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
|
||||
if (!open)
|
||||
menuItem.menu.close(false);
|
||||
menuItem.menu.close(BoxPointer.PopupAnimation.FADE);
|
||||
});
|
||||
} else if (menuItem instanceof PopupSeparatorMenuItem) {
|
||||
this._connectItemSignals(menuItem);
|
||||
@ -1151,9 +1158,9 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
toggle: function() {
|
||||
if (this.isOpen)
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
else
|
||||
this.open(true);
|
||||
this.open(BoxPointer.PopupAnimation.FULL);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -1214,7 +1221,7 @@ const PopupMenu = new Lang.Class({
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape) {
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1416,7 +1423,7 @@ const PopupSubMenu = new Lang.Class({
|
||||
// Move focus back to parent menu if the user types Left.
|
||||
|
||||
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
this.sourceActor._delegate.setActive(true);
|
||||
return true;
|
||||
}
|
||||
@ -1498,7 +1505,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.KEY_Right) {
|
||||
this.menu.open(true);
|
||||
this.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
|
||||
@ -1510,7 +1517,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
this.menu.open(true);
|
||||
this.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
},
|
||||
|
||||
_onButtonReleaseEvent: function(actor) {
|
||||
@ -1537,7 +1544,7 @@ const PopupComboMenu = new Lang.Class({
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape) {
|
||||
this.close(true);
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1888,11 +1895,7 @@ const RemoteMenu = new Lang.Class({
|
||||
}));
|
||||
}
|
||||
|
||||
item.actor.reactive = item.actor.can_focus = action.enabled;
|
||||
if (action.enabled)
|
||||
item.actor.remove_style_pseudo_class('insensitive');
|
||||
else
|
||||
item.actor.add_style_pseudo_class('insensitive');
|
||||
item.actor.reactive = action.enabled;
|
||||
|
||||
destroyId = item.connect('destroy', Lang.bind(this, function() {
|
||||
item.disconnect(destroyId);
|
||||
@ -1917,7 +1920,7 @@ const RemoteMenu = new Lang.Class({
|
||||
while (k0 < currentItems.length && currentItems[k0]._ignored)
|
||||
k0++;
|
||||
// find the right menu item matching the model item
|
||||
for (j0 = 0; j0 < position; j0++, k0++) {
|
||||
for (j0 = 0; k0 < currentItems.length && j0 < position; j0++, k0++) {
|
||||
if (currentItems[k0]._ignored)
|
||||
k0++;
|
||||
}
|
||||
@ -1927,7 +1930,7 @@ const RemoteMenu = new Lang.Class({
|
||||
for (k = k0; k < currentItems.length; k++)
|
||||
currentItems[k].destroy();
|
||||
} else {
|
||||
for (j = j0, k = k0; j < j0 + removed; j++, k++) {
|
||||
for (j = j0, k = k0; k < currentItems.length && j < j0 + removed; j++, k++) {
|
||||
currentItems[k].destroy();
|
||||
|
||||
if (currentItems[k]._ignored)
|
||||
@ -1958,8 +1961,9 @@ const RemoteMenu = new Lang.Class({
|
||||
k++;
|
||||
}
|
||||
} else if (changeSignal) {
|
||||
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() {
|
||||
this.actionGroup.disconnect(signalId);
|
||||
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) {
|
||||
actionGroup.disconnect(signalId);
|
||||
if (this._actions[actionName]) return;
|
||||
|
||||
// force a full update
|
||||
this._modelChanged(model, 0, -1, model.get_n_items(), target);
|
||||
@ -2023,12 +2027,7 @@ const RemoteMenu = new Lang.Class({
|
||||
if (action.items.length) {
|
||||
for (let i = 0; i < action.items.length; i++) {
|
||||
let item = action.items[i];
|
||||
item.actor.reactive = item.actor.can_focus = action.enabled;
|
||||
|
||||
if (action.enabled)
|
||||
item.actor.remove_style_pseudo_class('insensitive');
|
||||
else
|
||||
item.actor.add_style_pseudo_class('insensitive');
|
||||
item.actor.reactive = action.enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2190,11 +2189,11 @@ const PopupMenuManager = new Lang.Class({
|
||||
let oldMenu = this._activeMenu;
|
||||
this._activeMenu = null;
|
||||
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
||||
this._menuStack[i].close(false);
|
||||
oldMenu.close(false);
|
||||
newMenu.open(false);
|
||||
this._menuStack[i].close(BoxPointer.PopupAnimation.FADE);
|
||||
oldMenu.close(BoxPointer.PopupAnimation.FADE);
|
||||
newMenu.open(BoxPointer.PopupAnimation.FADE);
|
||||
} else
|
||||
newMenu.open(true);
|
||||
newMenu.open(BoxPointer.PopupAnimation.FULL);
|
||||
},
|
||||
|
||||
_onMenuSourceEnter: function(menu) {
|
||||
@ -2309,6 +2308,6 @@ const PopupMenuManager = new Lang.Class({
|
||||
|
||||
_closeMenu: function() {
|
||||
if (this._activeMenu != null)
|
||||
this._activeMenu.close(true);
|
||||
this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
|
||||
}
|
||||
});
|
||||
|
@ -62,10 +62,25 @@ function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
|
||||
let remoteProvider, title;
|
||||
try {
|
||||
let group = KEY_FILE_GROUP;
|
||||
let icon = keyfile.get_string(group, 'Icon');
|
||||
let busName = keyfile.get_string(group, 'BusName');
|
||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||
title = keyfile.get_locale_string(group, 'Title', null);
|
||||
|
||||
let appInfo = null;
|
||||
try {
|
||||
let desktopId = keyfile.get_string(group, 'DesktopId');
|
||||
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
let icon;
|
||||
if (appInfo) {
|
||||
icon = appInfo.get_icon();
|
||||
title = appInfo.get_name();
|
||||
} else {
|
||||
let iconName = keyfile.get_string(group, 'Icon');
|
||||
icon = new Gio.ThemedIcon({ name: iconName });
|
||||
title = keyfile.get_locale_string(group, 'Title', null);
|
||||
}
|
||||
|
||||
remoteProvider = new RemoteSearchProvider(title,
|
||||
icon,
|
||||
@ -91,7 +106,6 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
dbusName, dbusPath);
|
||||
|
||||
this.parent(title.toUpperCase());
|
||||
this.async = true;
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
},
|
||||
|
||||
@ -120,7 +134,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this.searchSystem.pushResults(this, results[0]);
|
||||
},
|
||||
|
||||
getInitialResultSetAsync: function(terms) {
|
||||
getInitialResultSet: function(terms) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
@ -133,7 +147,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
getSubsearchResultSetAsync: function(previousResults, newTerms) {
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
@ -164,7 +178,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
callback(resultMetas);
|
||||
},
|
||||
|
||||
getResultMetasAsync: function(ids, callback) {
|
||||
getResultMetas: function(ids, callback) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
try {
|
||||
|
615
js/ui/screenShield.js
Normal file
@ -0,0 +1,615 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
|
||||
const CURTAIN_SLIDE_TIME = 0.8;
|
||||
// fraction of screen height the arrow must reach before completing
|
||||
// the slide up automatically
|
||||
const ARROW_DRAG_TRESHOLD = 0.1;
|
||||
|
||||
const SUMMARY_ICON_SIZE = 48;
|
||||
|
||||
// Lightbox fading times
|
||||
// STANDARD_FADE_TIME is used when the session goes idle, while
|
||||
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
|
||||
const STANDARD_FADE_TIME = 10;
|
||||
const SHORT_FADE_TIME = 0.8;
|
||||
|
||||
const Clock = new Lang.Class({
|
||||
Name: 'ScreenShieldClock',
|
||||
|
||||
CLOCK_FORMAT_KEY: 'clock-format',
|
||||
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
|
||||
vertical: true });
|
||||
|
||||
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
|
||||
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
|
||||
|
||||
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
|
||||
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
|
||||
|
||||
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
|
||||
this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
|
||||
|
||||
this._updateClock();
|
||||
},
|
||||
|
||||
_updateClock: function() {
|
||||
this._time.text = this._wallClock.clock;
|
||||
|
||||
let date = new Date();
|
||||
/* Translators: This is a time format for a date in
|
||||
long format */
|
||||
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
this._wallClock.run_dispose();
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationsBox = new Lang.Class({
|
||||
Name: 'NotificationsBox',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true,
|
||||
name: 'screenShieldNotifications',
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
|
||||
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
|
||||
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
scrollView.add_actor(this._persistentNotificationBox);
|
||||
|
||||
this.actor.add(this._residentNotificationBox, { x_fill: true });
|
||||
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.MIDDLE });
|
||||
|
||||
this._items = [];
|
||||
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
|
||||
this._summaryItemAdded(Main.messageTray, item);
|
||||
}));
|
||||
|
||||
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._summaryAddedId) {
|
||||
Main.messageTray.disconnect(this._summaryAddedId);
|
||||
this._summaryAddedId = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._items.length; i++)
|
||||
this._removeItem(this._items[i]);
|
||||
this._items = [];
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
if (this._residentNotificationBox.get_n_children() > 0) {
|
||||
this.actor.show();
|
||||
return;
|
||||
}
|
||||
|
||||
let children = this._persistentNotificationBox.get_children()
|
||||
this.actor.visible = children.some(function(a) { return a.visible; });
|
||||
},
|
||||
|
||||
_sourceIsResident: function(source) {
|
||||
return source.hasResidentNotification() && !source.isChat;
|
||||
},
|
||||
|
||||
_makeNotificationCountText: function(count, isChat) {
|
||||
if (isChat)
|
||||
return ngettext("%d new message", "%d new messages", count).format(count);
|
||||
else
|
||||
return ngettext("%d new notification", "%d new notifications", count).format(count);
|
||||
},
|
||||
|
||||
_makeNotificationSource: function(source) {
|
||||
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
|
||||
|
||||
let iconClone = source.createIcon(SUMMARY_ICON_SIZE);
|
||||
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
|
||||
sourceActor.setIcon(iconClone);
|
||||
box.add(sourceActor.actor, { y_fill: true });
|
||||
|
||||
let textBox = new St.BoxLayout({ vertical: true });
|
||||
box.add(textBox);
|
||||
|
||||
let label = new St.Label({ text: source.title,
|
||||
style_class: 'screen-shield-notification-label' });
|
||||
textBox.add(label);
|
||||
|
||||
let count = source.unseenCount;
|
||||
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
|
||||
style_class: 'screen-shield-notification-count-text' });
|
||||
textBox.add(countLabel);
|
||||
|
||||
box.visible = count != 0;
|
||||
return [box, countLabel];
|
||||
},
|
||||
|
||||
_summaryItemAdded: function(tray, item) {
|
||||
// Ignore transient sources
|
||||
if (item.source.isTransient)
|
||||
return;
|
||||
|
||||
let obj = {
|
||||
item: item,
|
||||
source: item.source,
|
||||
resident: this._sourceIsResident(item.source),
|
||||
contentUpdatedId: 0,
|
||||
sourceDestroyId: 0,
|
||||
sourceBox: null,
|
||||
countLabel: null,
|
||||
};
|
||||
|
||||
if (obj.resident) {
|
||||
item.prepareNotificationStackForShowing();
|
||||
this._residentNotificationBox.add(item.notificationStackView);
|
||||
} else {
|
||||
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
|
||||
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.MIDDLE });
|
||||
}
|
||||
|
||||
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
|
||||
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
|
||||
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
|
||||
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||
this._items.push(obj);
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_findSource: function(source) {
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].source == source)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
|
||||
_onItemContentUpdated: function(item) {
|
||||
let obj = this._items[this._findSource(item.source)];
|
||||
this._updateItem(obj);
|
||||
},
|
||||
|
||||
_onSourceChanged: function(source) {
|
||||
let obj = this._items[this._findSource(source)];
|
||||
this._updateItem(obj);
|
||||
},
|
||||
|
||||
_updateItem: function(obj) {
|
||||
let itemShouldBeResident = this._sourceIsResident(obj.source);
|
||||
|
||||
if (itemShouldBeResident && obj.resident) {
|
||||
// Nothing to do here, the actor is already updated
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj.resident && !itemShouldBeResident) {
|
||||
// make into a regular item
|
||||
this._residentNotificationBox.remove_actor(obj.item.notificationStackView);
|
||||
|
||||
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
|
||||
this._persistentNotificationBox.add(obj.sourceBox);
|
||||
} else if (itemShouldBeResident && !obj.resident) {
|
||||
// make into a resident item
|
||||
obj.sourceBox.destroy();
|
||||
obj.sourceBox = obj.countLabel = null;
|
||||
obj.resident = true;
|
||||
|
||||
obj.item.prepareNotificationStackForShowing();
|
||||
this._residentNotificationBox.add(obj.item.notificationStackView);
|
||||
} else {
|
||||
// just update the counter
|
||||
let count = obj.source.unseenCount;
|
||||
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
|
||||
obj.sourceBox.visible = count != 0;
|
||||
}
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source) {
|
||||
let idx = this._findSource(source);
|
||||
|
||||
this._removeItem(this._items[idx]);
|
||||
this._items.splice(idx, 1);
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_removeItem: function(obj) {
|
||||
if (obj.resident) {
|
||||
this._residentNotificationBox.remove_actor(obj.item.notificationStackView);
|
||||
obj.item.doneShowingNotificationStack();
|
||||
} else {
|
||||
obj.sourceBox.destroy();
|
||||
}
|
||||
|
||||
obj.item.disconnect(obj.contentUpdatedId);
|
||||
obj.source.disconnect(obj.sourceDestroyId);
|
||||
obj.source.disconnect(obj.sourceCountChangedId);
|
||||
},
|
||||
});
|
||||
|
||||
const LockDialogGroup = new Lang.Class({
|
||||
Name: 'LockDialogGroup',
|
||||
Extends: St.Widget,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(forWidth) {
|
||||
return [global.screen_height, global.screen_height];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(forHeight) {
|
||||
return [global.screen_width, global.screen_width];
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* To test screen shield, make sure to kill gnome-screensaver.
|
||||
*
|
||||
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
|
||||
* rather than through System Settings, you also need to set
|
||||
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
|
||||
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
|
||||
* This will ensure that the screen blanks at the right time when it fades out.
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
|
||||
*/
|
||||
const ScreenShield = new Lang.Class({
|
||||
Name: 'ScreenShield',
|
||||
|
||||
_init: function() {
|
||||
this.actor = Main.layoutManager.screenShieldGroup;
|
||||
|
||||
this._lockScreenGroup = new St.Widget({ x_expand: true,
|
||||
y_expand: true,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
name: 'lockScreenGroup',
|
||||
});
|
||||
this._lockScreenGroup.connect('key-release-event',
|
||||
Lang.bind(this, this._onLockScreenKeyRelease));
|
||||
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._lockScreenGroup.add_actor(this._background);
|
||||
|
||||
// FIXME: build the rest of the lock screen here
|
||||
|
||||
this._arrow = new St.DrawingArea({ style_class: 'arrow',
|
||||
reactive: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
// HACK: without these, ClutterBinLayout
|
||||
// ignores alignment properties on the actor
|
||||
x_expand: true,
|
||||
y_expand: true
|
||||
});
|
||||
this._arrow.connect('repaint', Lang.bind(this, this._drawArrow));
|
||||
this._lockScreenGroup.add_actor(this._arrow);
|
||||
|
||||
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS });
|
||||
action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
action.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this._lockScreenGroup.add_action(action);
|
||||
|
||||
this._lockDialogGroup = new LockDialogGroup({ name: 'lockDialogGroup' });
|
||||
this.actor.add_actor(this._lockDialogGroup);
|
||||
this.actor.add_actor(this._lockScreenGroup);
|
||||
|
||||
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
logError(error, 'Error while reading gnome-session presence');
|
||||
return;
|
||||
}
|
||||
|
||||
this._onStatusChanged(proxy.status);
|
||||
}));
|
||||
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
|
||||
this._onStatusChanged(status);
|
||||
}));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
|
||||
this._isModal = false;
|
||||
this._isLocked = false;
|
||||
this._hasLockScreen = false;
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ inhibitEvents: true,
|
||||
fadeInTime: STANDARD_FADE_TIME,
|
||||
fadeFactor: 1 });
|
||||
},
|
||||
|
||||
_onLockScreenKeyRelease: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this._showUnlockDialog(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_drawArrow: function() {
|
||||
let cr = this._arrow.get_context();
|
||||
let [w, h] = this._arrow.get_surface_size();
|
||||
let node = this._arrow.get_theme_node();
|
||||
|
||||
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
||||
|
||||
cr.moveTo(0, h);
|
||||
cr.lineTo(w/2, 0);
|
||||
cr.lineTo(w, h);
|
||||
cr.fill();
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
},
|
||||
|
||||
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
|
||||
if (this._lockScreenGroup.y < -(ARROW_DRAG_TRESHOLD * global.stage.height)) {
|
||||
// Complete motion automatically
|
||||
this._showUnlockDialog(true);
|
||||
} else {
|
||||
// restore the lock screen to its original place
|
||||
// try to use the same speed as the normal animation
|
||||
let h = global.stage.height;
|
||||
let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: 0,
|
||||
time: time,
|
||||
transition: 'linear',
|
||||
onComplete: function() {
|
||||
this.fixed_position_set = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onStatusChanged: function(status) {
|
||||
if (status == GnomeSession.PresenceStatus.IDLE) {
|
||||
if (this._dialog) {
|
||||
this._dialog.cancel();
|
||||
if (!this._keepDialog) {
|
||||
this._dialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor);
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
if (!this._isLocked)
|
||||
this._lightbox.show();
|
||||
} else {
|
||||
let lightboxWasShown = this._lightbox.shown;
|
||||
this._lightbox.hide();
|
||||
|
||||
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
|
||||
if (shouldLock || this._isLocked) {
|
||||
this.lock(false);
|
||||
} else if (this._isModal) {
|
||||
this.unlock();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showDialog: function() {
|
||||
this.lock(true);
|
||||
this._showUnlockDialog(false);
|
||||
},
|
||||
|
||||
_showUnlockDialog: function(animate) {
|
||||
if (animate) {
|
||||
// Tween the lock screen out of screen
|
||||
// try to use the same speed regardless of original position
|
||||
let h = global.stage.height;
|
||||
let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: -h,
|
||||
time: time,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._hideLockScreen),
|
||||
});
|
||||
} else {
|
||||
this._hideLockScreen();
|
||||
}
|
||||
|
||||
if (!this._dialog) {
|
||||
[this._dialog, this._keepDialog] = Main.sessionMode.createUnlockDialog(this._lockDialogGroup);
|
||||
if (!this._dialog) {
|
||||
// This session mode has no locking capabilities
|
||||
this.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
this._dialog.connect('loaded', Lang.bind(this, function() {
|
||||
if (!this._dialog.open()) {
|
||||
log('Could not open login dialog: failed to acquire grab');
|
||||
this.unlock();
|
||||
}
|
||||
}));
|
||||
|
||||
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
|
||||
}
|
||||
|
||||
if (this._keepDialog) {
|
||||
// Notify the other components that even though we are showing the
|
||||
// screenshield, we're not in a locked state
|
||||
// (this happens for the gdm greeter)
|
||||
|
||||
this._isLocked = false;
|
||||
this.emit('lock-status-changed', false);
|
||||
}
|
||||
},
|
||||
|
||||
_onUnlockFailed: function() {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
|
||||
this._resetLockScreen(false);
|
||||
},
|
||||
|
||||
_onUnlockSucceded: function() {
|
||||
this.unlock();
|
||||
},
|
||||
|
||||
_hideLockScreen: function() {
|
||||
this._arrow.hide();
|
||||
this._lockScreenGroup.hide();
|
||||
},
|
||||
|
||||
_resetLockScreen: function(animate) {
|
||||
this._lockScreenGroup.show();
|
||||
this._arrow.show();
|
||||
|
||||
if (animate) {
|
||||
this._lockScreenGroup.y = -global.screen_height;
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: 0,
|
||||
time: SHORT_FADE_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: function() {
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this.emit('lock-screen-shown');
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
this._lockDialogGroup.opacity = 0;
|
||||
Tweener.removeTweens(this._lockDialogGroup);
|
||||
Tweener.addTween(this._lockDialogGroup,
|
||||
{ opacity: 255,
|
||||
time: SHORT_FADE_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
} else {
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this._lockDialogGroup.opacity = 255;
|
||||
this.emit('lock-screen-shown');
|
||||
}
|
||||
|
||||
this._lockScreenGroup.grab_key_focus();
|
||||
},
|
||||
|
||||
// Some of the actors in the lock screen are heavy in
|
||||
// resources, so we only create them when needed
|
||||
_prepareLockScreen: function() {
|
||||
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
vertical: true });
|
||||
this._clock = new Clock();
|
||||
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
this._lockScreenGroup.add_actor(this._lockScreenContentsBox);
|
||||
|
||||
if (this._settings.get_boolean('show-notifications')) {
|
||||
this._notificationsBox = new NotificationsBox();
|
||||
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
}
|
||||
|
||||
this._hasLockScreen = true;
|
||||
},
|
||||
|
||||
_clearLockScreen: function() {
|
||||
this._clock.destroy();
|
||||
this._clock = null;
|
||||
|
||||
if (this._notificationsBox) {
|
||||
this._notificationsBox.destroy();
|
||||
this._notificationsBox = null;
|
||||
}
|
||||
|
||||
this._lockScreenContentsBox.destroy();
|
||||
|
||||
this._hasLockScreen = false;
|
||||
},
|
||||
|
||||
get locked() {
|
||||
return this._isLocked;
|
||||
},
|
||||
|
||||
unlock: function() {
|
||||
if (this._hasLockScreen)
|
||||
this._clearLockScreen();
|
||||
|
||||
if (this._keepDialog) {
|
||||
// The dialog must be kept alive,
|
||||
// so immediately go back to it
|
||||
// This will also reset _isLocked
|
||||
this._showUnlockDialog(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._dialog) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
|
||||
this._lightbox.hide();
|
||||
|
||||
Main.popModal(this.actor);
|
||||
this.actor.hide();
|
||||
|
||||
this._isModal = false;
|
||||
this._isLocked = false;
|
||||
|
||||
this.emit('lock-status-changed', false);
|
||||
},
|
||||
|
||||
lock: function(animate) {
|
||||
if (!this._hasLockScreen)
|
||||
this._prepareLockScreen();
|
||||
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor);
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
this._isLocked = true;
|
||||
this.actor.show();
|
||||
this._resetLockScreen(animate);
|
||||
|
||||
this.emit('lock-status-changed', true);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ScreenShield.prototype);
|
108
js/ui/search.js
@ -18,9 +18,7 @@ const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
|
||||
const MatchType = {
|
||||
NONE: 0,
|
||||
SUBSTRING: 1,
|
||||
MULTIPLE_SUBSTRING: 2,
|
||||
PREFIX: 3,
|
||||
MULTIPLE_PREFIX: 4
|
||||
PREFIX: 2
|
||||
};
|
||||
|
||||
const SearchResultDisplay = new Lang.Class({
|
||||
@ -53,7 +51,7 @@ const SearchResultDisplay = new Lang.Class({
|
||||
* Remove all results from this display.
|
||||
*/
|
||||
clear: function() {
|
||||
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
||||
this.actor.destroy_all_children();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -72,11 +70,8 @@ const SearchResultDisplay = new Lang.Class({
|
||||
* Subclass this object to add a new result type
|
||||
* to the search system, then call registerProvider()
|
||||
* in SearchSystem with an instance.
|
||||
* By default, search is synchronous and uses the
|
||||
* Search is asynchronous and uses the
|
||||
* getInitialResultSet()/getSubsearchResultSet() methods.
|
||||
* For asynchronous search, set the async property to true
|
||||
* and implement getInitialResultSetAsync()/getSubsearchResultSetAsync()
|
||||
* instead.
|
||||
*/
|
||||
const SearchProvider = new Lang.Class({
|
||||
Name: 'SearchProvider',
|
||||
@ -84,7 +79,6 @@ const SearchProvider = new Lang.Class({
|
||||
_init: function(title) {
|
||||
this.title = title;
|
||||
this.searchSystem = null;
|
||||
this.async = false;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -95,7 +89,7 @@ const SearchProvider = new Lang.Class({
|
||||
* therefore a single term of length one or two), or when
|
||||
* a new term is added.
|
||||
*
|
||||
* Should return an array of result identifier strings representing
|
||||
* Should "return" an array of result identifier strings representing
|
||||
* items which match the given search terms. This
|
||||
* is expected to be a substring match on the metadata for a given
|
||||
* item. Ordering of returned results is up to the discretion of the provider,
|
||||
@ -105,6 +99,9 @@ const SearchProvider = new Lang.Class({
|
||||
* description) before single matches
|
||||
* * Put items which match on a prefix before non-prefix substring matches
|
||||
*
|
||||
* We say "return" above, but in order to make the query asynchronous, use
|
||||
* this.searchSystem.pushResults();. The return value should be ignored.
|
||||
*
|
||||
* This function should be fast; do not perform unindexed full-text searches
|
||||
* or network queries.
|
||||
*/
|
||||
@ -112,18 +109,6 @@ const SearchProvider = new Lang.Class({
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getInitialResultSetAsync:
|
||||
* @terms: Array of search terms, treated as logical AND
|
||||
*
|
||||
* Like getInitialResultSet(), but the method should return immediately
|
||||
* without a return value - use SearchSystem.pushResults() when the
|
||||
* corresponding results are ready.
|
||||
*/
|
||||
getInitialResultSetAsync: function(terms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getSubsearchResultSet:
|
||||
* @previousResults: Array of item identifiers
|
||||
@ -136,62 +121,26 @@ const SearchProvider = new Lang.Class({
|
||||
*
|
||||
* This allows search providers to only search through the previous
|
||||
* result set, rather than possibly performing a full re-query.
|
||||
*
|
||||
* Similar to getInitialResultSet, the return value for this will
|
||||
* be ignored; use this.searchSystem.pushResults();.
|
||||
*/
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getSubsearchResultSetAsync:
|
||||
* @previousResults: Array of item identifiers
|
||||
* @newTerms: Updated search terms
|
||||
*
|
||||
* Like getSubsearchResultSet(), but the method should return immediately
|
||||
* without a return value - use SearchSystem.pushResults() when the
|
||||
* corresponding results are ready.
|
||||
*/
|
||||
getSubsearchResultSetAsync: function(previousResults, newTerms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getResultMetas:
|
||||
* @ids: Result identifier strings
|
||||
*
|
||||
* Return an array of objects with 'id', 'name', (both strings) and
|
||||
* Call callback with array of objects with 'id', 'name', (both strings) and
|
||||
* 'createIcon' (function(size) returning a Clutter.Texture) properties
|
||||
* with the same number of members as @ids
|
||||
*/
|
||||
getResultMetas: function(ids) {
|
||||
getResultMetas: function(ids, callback) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getResultMetasAsync:
|
||||
* @ids: Result identifier strings
|
||||
* @callback: callback to pass the results to when ready
|
||||
*
|
||||
* Like getResultMetas(), but the method should return immediately
|
||||
* without a return value - pass the results to the provided @callback
|
||||
* when ready.
|
||||
*/
|
||||
getResultMetasAsync: function(ids, callback) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultContainer:
|
||||
*
|
||||
* Search providers may optionally override this to render their
|
||||
* results in a custom fashion. The default implementation
|
||||
* will create a vertical list.
|
||||
*
|
||||
* Returns: An instance of SearchResultDisplay.
|
||||
*/
|
||||
createResultContainerActor: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultActor:
|
||||
* @resultMeta: Object with result metadata
|
||||
@ -379,42 +328,33 @@ const SearchSystem = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
let previousResultsArr = this._previousResults;
|
||||
|
||||
let results = [];
|
||||
this._previousTerms = terms;
|
||||
this._previousResults = results;
|
||||
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let [provider, previousResults] = this._previousResults[i];
|
||||
let [provider, previousResults] = previousResultsArr[i];
|
||||
try {
|
||||
if (provider.async) {
|
||||
provider.getSubsearchResultSetAsync(previousResults, terms);
|
||||
results.push([provider, []]);
|
||||
} else {
|
||||
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
||||
results.push([provider, providerResults]);
|
||||
}
|
||||
results.push([provider, []]);
|
||||
provider.getSubsearchResultSet(previousResults, terms);
|
||||
} catch (error) {
|
||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
try {
|
||||
if (provider.async) {
|
||||
provider.getInitialResultSetAsync(terms);
|
||||
results.push([provider, []]);
|
||||
} else {
|
||||
let providerResults = provider.getInitialResultSet(terms);
|
||||
results.push([provider, providerResults]);
|
||||
}
|
||||
results.push([provider, []]);
|
||||
provider.getInitialResultSet(terms);
|
||||
} catch (error) {
|
||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._previousTerms = terms;
|
||||
this._previousResults = results;
|
||||
this.emit('search-completed', results);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
||||
|
@ -5,6 +5,7 @@ const Lang = imports.lang;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
@ -33,12 +34,13 @@ const SearchResult = new Lang.Class({
|
||||
content = new St.Bin({ style_class: 'search-result-content',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true });
|
||||
track_hover: true,
|
||||
accessible_role: Atk.Role.PUSH_BUTTON });
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
content.set_child(icon.actor);
|
||||
this._dragActorSource = icon.icon;
|
||||
this.actor.label_actor = icon.label;
|
||||
content.label_actor = icon.label;
|
||||
} else {
|
||||
if (content._delegate && content._delegate.getDragActorSource)
|
||||
this._dragActorSource = content._delegate.getDragActorSource();
|
||||
@ -119,13 +121,7 @@ const GridSearchResults = new Lang.Class({
|
||||
if (results.length == 0)
|
||||
return;
|
||||
|
||||
if (provider.async) {
|
||||
provider.getResultMetasAsync(results,
|
||||
Lang.bind(this, this.renderResults));
|
||||
} else {
|
||||
let metas = provider.getResultMetas(results);
|
||||
this.renderResults(metas);
|
||||
}
|
||||
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
|
||||
}));
|
||||
}));
|
||||
this._notDisplayedResult = [];
|
||||
@ -135,7 +131,7 @@ const GridSearchResults = new Lang.Class({
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
|
||||
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
|
||||
- alreadyVisible;
|
||||
|
||||
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
|
||||
@ -179,8 +175,7 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
_init: function(searchSystem, openSearchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
|
||||
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||
this._openSearchSystem = openSearchSystem;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
@ -214,10 +209,8 @@ const SearchResults = new Lang.Class({
|
||||
this._content.add(this._statusText);
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerMeta = [];
|
||||
this._providerMetaResults = {};
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderMeta(this._providers[i]);
|
||||
this._providerMetaResults[this.providers[i].title] = [];
|
||||
}
|
||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||
this.actor.add(this._searchProvidersBox);
|
||||
@ -282,16 +275,12 @@ const SearchResults = new Lang.Class({
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
providerBox.add(resultDisplayBin, { expand: true });
|
||||
let resultDisplay = provider.createResultContainerActor();
|
||||
if (resultDisplay == null) {
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
let resultDisplay = new GridSearchResults(provider);
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
this._providerMeta.push({ provider: provider,
|
||||
actor: providerBox,
|
||||
resultDisplay: resultDisplay,
|
||||
hasPendingResults: false });
|
||||
resultDisplay: resultDisplay });
|
||||
this._content.add(providerBox);
|
||||
},
|
||||
|
||||
@ -307,7 +296,6 @@ const SearchResults = new Lang.Class({
|
||||
},
|
||||
|
||||
_clearDisplay: function() {
|
||||
this._visibleResultsCount = 0;
|
||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
meta.resultDisplay.clear();
|
||||
@ -335,6 +323,8 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
doSearch: function (searchString) {
|
||||
this._searchSystem.updateSearch(searchString);
|
||||
let terms = this._searchSystem.getTerms();
|
||||
this._openSearchSystem.setSearchTerms(terms);
|
||||
},
|
||||
|
||||
_metaForProvider: function(provider) {
|
||||
@ -346,8 +336,6 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (meta.hasPendingResults)
|
||||
return;
|
||||
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
@ -372,71 +360,57 @@ const SearchResults = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateCurrentResults: function(searchSystem, results) {
|
||||
let terms = searchSystem.getTerms();
|
||||
let [provider, providerResults] = results;
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.hasPendingResults = false;
|
||||
this._updateProviderResults(provider, providerResults, terms);
|
||||
},
|
||||
_updateStatusText: function () {
|
||||
let haveResults = false;
|
||||
|
||||
_updateProviderResults: function(provider, providerResults, terms) {
|
||||
let meta = this._metaForProvider(provider);
|
||||
if (providerResults.length == 0) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.resultDisplay.setResults([], []);
|
||||
} else {
|
||||
this._providerMetaResults[provider.title] = providerResults;
|
||||
meta.resultDisplay.setResults(providerResults, terms);
|
||||
let results = meta.resultDisplay.getResultsForDisplay();
|
||||
|
||||
if (provider.async) {
|
||||
provider.getResultMetasAsync(results, Lang.bind(this,
|
||||
function(metas) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.actor.show();
|
||||
this._content.hide();
|
||||
meta.resultDisplay.renderResults(metas);
|
||||
this._maybeSetInitialSelection();
|
||||
this._content.show();
|
||||
}));
|
||||
} else {
|
||||
let metas = provider.getResultMetas(results);
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(metas);
|
||||
for (let i = 0; i < this._providerMeta.length; ++i)
|
||||
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
|
||||
haveResults = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateResults: function(searchSystem, results) {
|
||||
if (results.length == 0) {
|
||||
if (!haveResults) {
|
||||
this._statusText.set_text(_("No matching results."));
|
||||
this._statusText.show();
|
||||
} else {
|
||||
this._statusText.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateResults: function(searchSystem, results) {
|
||||
let terms = searchSystem.getTerms();
|
||||
this._openSearchSystem.setSearchTerms(terms);
|
||||
let [provider, providerResults] = results;
|
||||
let meta = this._metaForProvider(provider);
|
||||
|
||||
// To avoid CSS transitions causing flickering when the first search
|
||||
// result stays the same, we hide the content while filling in the
|
||||
// results.
|
||||
this._content.hide();
|
||||
if (providerResults.length == 0) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.resultDisplay.setResults([], []);
|
||||
this._maybeSetInitialSelection();
|
||||
this._updateStatusText();
|
||||
} else {
|
||||
meta.resultDisplay.setResults(providerResults, terms);
|
||||
let results = meta.resultDisplay.getResultsForDisplay();
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let [provider, providerResults] = results[i];
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.hasPendingResults = provider.async;
|
||||
if (!meta.hasPendingResults)
|
||||
this._updateProviderResults(provider, providerResults, terms);
|
||||
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.actor.show();
|
||||
|
||||
// Hiding drops the key focus if we have it
|
||||
let focus = global.stage.get_key_focus();
|
||||
// To avoid CSS transitions causing flickering when
|
||||
// the first search result stays the same, we hide the
|
||||
// content while filling in the results.
|
||||
this._content.hide();
|
||||
|
||||
meta.resultDisplay.renderResults(metas);
|
||||
this._maybeSetInitialSelection();
|
||||
this._updateStatusText();
|
||||
|
||||
this._content.show();
|
||||
if (this._content.contains(focus))
|
||||
global.stage.set_key_focus(focus);
|
||||
}));
|
||||
}
|
||||
|
||||
this._maybeSetInitialSelection();
|
||||
this._content.show();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
activateDefault: function() {
|
||||
|
135
js/ui/sessionMode.js
Normal file
@ -0,0 +1,135 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
|
||||
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||
};
|
||||
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] =
|
||||
imports.ui.status.bluetooth.Indicator;
|
||||
|
||||
try {
|
||||
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] =
|
||||
imports.ui.status.network.NMApplet;
|
||||
} catch(e) {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
|
||||
const DEFAULT_MODE = 'user';
|
||||
|
||||
const _modes = {
|
||||
'gdm': { hasOverview: false,
|
||||
hasAppMenu: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: true,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
createSession: Main.createGDMSession,
|
||||
createUnlockDialog: Main.createGDMLoginDialog,
|
||||
extraStylesheet: null,
|
||||
statusArea: {
|
||||
order: [
|
||||
'a11y', 'display', 'keyboard',
|
||||
'volume', 'battery', 'powerMenu'
|
||||
],
|
||||
implementation: {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'initial-setup': { hasOverview: false,
|
||||
hasAppMenu: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
createSession: Main.createInitialSetupSession,
|
||||
extraStylesheet: null,
|
||||
statusArea: {
|
||||
order: [
|
||||
'a11y', 'keyboard', 'volume'
|
||||
],
|
||||
implementation: {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'keyboard': imports.ui.status.keyboard.XKBIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'user': { hasOverview: true,
|
||||
hasAppMenu: true,
|
||||
showCalendarEvents: true,
|
||||
allowSettings: true,
|
||||
allowExtensions: true,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: true,
|
||||
hasWorkspaces: true,
|
||||
createSession: Main.createUserSession,
|
||||
createUnlockDialog: Main.createSessionUnlockDialog,
|
||||
extraStylesheet: null,
|
||||
statusArea: {
|
||||
order: [
|
||||
'input-method', 'a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'userMenu'
|
||||
],
|
||||
implementation: STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function listModes() {
|
||||
let modes = Object.getOwnPropertyNames(_modes);
|
||||
for (let i = 0; i < modes.length; i++)
|
||||
print(modes[i]);
|
||||
}
|
||||
|
||||
const SessionMode = new Lang.Class({
|
||||
Name: 'SessionMode',
|
||||
|
||||
_init: function() {
|
||||
let params = _modes[global.session_mode];
|
||||
|
||||
params = Params.parse(params, _modes[DEFAULT_MODE]);
|
||||
|
||||
this._createSession = params.createSession;
|
||||
delete params.createSession;
|
||||
this._createUnlockDialog = params.createUnlockDialog;
|
||||
delete params.createUnlockDialog;
|
||||
|
||||
Lang.copyProperties(params, this);
|
||||
},
|
||||
|
||||
createSession: function() {
|
||||
if (this._createSession)
|
||||
this._createSession();
|
||||
},
|
||||
|
||||
createUnlockDialog: function() {
|
||||
if (this._createUnlockDialog)
|
||||
return this._createUnlockDialog.apply(this, arguments);
|
||||
else
|
||||
return null;
|
||||
},
|
||||
});
|
@ -7,6 +7,7 @@ const Shell = imports.gi.Shell;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Flashspot = imports.ui.flashspot;
|
||||
const Main = imports.ui.main;
|
||||
@ -17,17 +18,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="b" direction="out" name="success" />
|
||||
<arg type="s" direction="out" name="result" />
|
||||
</method>
|
||||
<method name="ListExtensions">
|
||||
<arg type="a{sa{sv}}" direction="out" name="extensions" />
|
||||
</method>
|
||||
<method name="GetExtensionInfo">
|
||||
<arg type="s" direction="in" name="extension" />
|
||||
<arg type="a{sv}" direction="out" name="info" />
|
||||
</method>
|
||||
<method name="GetExtensionErrors">
|
||||
<arg type="s" direction="in" name="extension" />
|
||||
<arg type="as" direction="out" name="errors" />
|
||||
</method>
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
@ -56,30 +46,21 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
<method name="EnableExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<method name="DisableExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<method name="InstallRemoteExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="s" direction="in" name="version"/>
|
||||
</method>
|
||||
<method name="UninstallExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="LaunchExtensionPrefs">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
<property name="ApiVersion" type="i" access="read" />
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
<arg type="s" name="error"/>
|
||||
</interface>;
|
||||
|
||||
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="u" />
|
||||
</method>
|
||||
<signal name="ActiveChanged">
|
||||
<arg name="new_value" type="b" />
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
@ -89,8 +70,8 @@ const GnomeShell = new Lang.Class({
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||
ExtensionSystem.connect('extension-state-changed',
|
||||
Lang.bind(this, this._extensionStateChanged));
|
||||
|
||||
this._extensionsSerivce = new GnomeShellExtensions();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -202,6 +183,67 @@ const GnomeShell = new Lang.Class({
|
||||
flashspot.fire();
|
||||
},
|
||||
|
||||
get OverviewActive() {
|
||||
return Main.overview.visible;
|
||||
},
|
||||
|
||||
set OverviewActive(visible) {
|
||||
if (visible)
|
||||
Main.overview.show();
|
||||
else
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
ShellVersion: Config.PACKAGE_VERSION
|
||||
});
|
||||
|
||||
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
|
||||
<method name="ListExtensions">
|
||||
<arg type="a{sa{sv}}" direction="out" name="extensions" />
|
||||
</method>
|
||||
<method name="GetExtensionInfo">
|
||||
<arg type="s" direction="in" name="extension" />
|
||||
<arg type="a{sv}" direction="out" name="info" />
|
||||
</method>
|
||||
<method name="GetExtensionErrors">
|
||||
<arg type="s" direction="in" name="extension" />
|
||||
<arg type="as" direction="out" name="errors" />
|
||||
</method>
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
<arg type="s" name="error"/>
|
||||
</signal>
|
||||
<method name="InstallRemoteExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="s" direction="out" name="result"/>
|
||||
</method>
|
||||
<method name="UninstallExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="LaunchExtensionPrefs">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<method name="ReloadExtension">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<method name="CheckForUpdates">
|
||||
</method>
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
</interface>;
|
||||
|
||||
const GnomeShellExtensions = new Lang.Class({
|
||||
Name: 'GnomeShellExtensionsDBus',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||
ExtensionSystem.connect('extension-state-changed',
|
||||
Lang.bind(this, this._extensionStateChanged));
|
||||
},
|
||||
|
||||
|
||||
ListExtensions: function() {
|
||||
let out = {};
|
||||
for (let uuid in ExtensionUtils.extensions) {
|
||||
@ -260,26 +302,12 @@ const GnomeShell = new Lang.Class({
|
||||
return extension.errors;
|
||||
},
|
||||
|
||||
EnableExtension: function(uuid) {
|
||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1)
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
},
|
||||
|
||||
DisableExtension: function(uuid) {
|
||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
while (enabledExtensions.indexOf(uuid) != -1)
|
||||
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
},
|
||||
|
||||
InstallRemoteExtension: function(uuid, version_tag) {
|
||||
ExtensionSystem.installExtensionFromUUID(uuid, version_tag);
|
||||
InstallRemoteExtensionAsync: function([uuid], invocation) {
|
||||
return ExtensionDownloader.installExtension(uuid, invocation);
|
||||
},
|
||||
|
||||
UninstallExtension: function(uuid) {
|
||||
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
|
||||
return ExtensionDownloader.uninstallExtension(uuid);
|
||||
},
|
||||
|
||||
LaunchExtensionPrefs: function(uuid) {
|
||||
@ -289,19 +317,15 @@ const GnomeShell = new Lang.Class({
|
||||
['extension:///' + uuid], -1, null);
|
||||
},
|
||||
|
||||
get OverviewActive() {
|
||||
return Main.overview.visible;
|
||||
ReloadExtension: function(uuid) {
|
||||
ExtensionSystem.unloadExtension(uuid);
|
||||
ExtensionSystem.loadExtension(uuid);
|
||||
},
|
||||
|
||||
set OverviewActive(visible) {
|
||||
if (visible)
|
||||
Main.overview.show();
|
||||
else
|
||||
Main.overview.hide();
|
||||
CheckForUpdates: function() {
|
||||
ExtensionDownloader.checkForUpdates();
|
||||
},
|
||||
|
||||
ApiVersion: ExtensionSystem.API_VERSION,
|
||||
|
||||
ShellVersion: Config.PACKAGE_VERSION,
|
||||
|
||||
_extensionStateChanged: function(_, newState) {
|
||||
@ -309,3 +333,33 @@ const GnomeShell = new Lang.Class({
|
||||
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
|
||||
}
|
||||
});
|
||||
|
||||
const ScreenSaverDBus = new Lang.Class({
|
||||
Name: 'ScreenSaverDBus',
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, function(shield, locked) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [locked]));
|
||||
}));
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
|
||||
},
|
||||
|
||||
Lock: function() {
|
||||
Main.screenShield.lock(true);
|
||||
},
|
||||
|
||||
SetActive: function(active) {
|
||||
if (active)
|
||||
Main.screenShield.lock(true);
|
||||
else
|
||||
Main.screenShield.unlock();
|
||||
},
|
||||
|
||||
GetActive: function() {
|
||||
return Main.screenShield.locked;
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const _EntryMenu = new Lang.Class({
|
||||
const EntryMenu = new Lang.Class({
|
||||
Name: 'ShellEntryMenu',
|
||||
Extends: PopupMenu.PopupMenu,
|
||||
|
||||
@ -34,18 +34,37 @@ const _EntryMenu = new Lang.Class({
|
||||
this._pasteItem = item;
|
||||
|
||||
this._passwordItem = null;
|
||||
if (params.isPassword) {
|
||||
item = new PopupMenu.PopupMenuItem('');
|
||||
item.connect('activate', Lang.bind(this,
|
||||
this._onPasswordActivated));
|
||||
this.addMenuItem(item);
|
||||
this._passwordItem = item;
|
||||
}
|
||||
if (params.isPassword)
|
||||
this._makePasswordItem();
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.hide();
|
||||
},
|
||||
|
||||
_makePasswordItem: function() {
|
||||
let item = new PopupMenu.PopupMenuItem('');
|
||||
item.connect('activate', Lang.bind(this,
|
||||
this._onPasswordActivated));
|
||||
this.addMenuItem(item);
|
||||
this._passwordItem = item;
|
||||
},
|
||||
|
||||
get isPassword() {
|
||||
return this._passwordItem != null;
|
||||
},
|
||||
|
||||
set isPassword(v) {
|
||||
if (v == this.isPassword)
|
||||
return;
|
||||
|
||||
if (v)
|
||||
this._makePasswordItem();
|
||||
else {
|
||||
this._passwordItem.destroy();
|
||||
this._passwordItem = null;
|
||||
}
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._updatePasteItem();
|
||||
this._updateCopyItem();
|
||||
@ -104,50 +123,50 @@ const _EntryMenu = new Lang.Class({
|
||||
function _setMenuAlignment(entry, stageX) {
|
||||
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
|
||||
if (success)
|
||||
entry._menu.setSourceAlignment(entryX / entry.width);
|
||||
entry.menu.setSourceAlignment(entryX / entry.width);
|
||||
};
|
||||
|
||||
function _onClicked(action, actor) {
|
||||
let entry = actor._menu ? actor : actor.get_parent();
|
||||
let entry = actor.menu ? actor : actor.get_parent();
|
||||
|
||||
if (entry._menu.isOpen) {
|
||||
entry._menu.close();
|
||||
if (entry.menu.isOpen) {
|
||||
entry.menu.close();
|
||||
} else if (action.get_button() == 3) {
|
||||
let [stageX, stageY] = action.get_coords();
|
||||
_setMenuAlignment(entry, stageX);
|
||||
entry._menu.open();
|
||||
entry.menu.open();
|
||||
}
|
||||
};
|
||||
|
||||
function _onLongPress(action, actor, state) {
|
||||
let entry = actor._menu ? actor : actor.get_parent();
|
||||
let entry = actor.menu ? actor : actor.get_parent();
|
||||
|
||||
if (state == Clutter.LongPressState.QUERY)
|
||||
return action.get_button() == 1 && !entry._menu.isOpen;
|
||||
return action.get_button() == 1 && !entry.menu.isOpen;
|
||||
|
||||
if (state == Clutter.LongPressState.ACTIVATE) {
|
||||
let [stageX, stageY] = action.get_coords();
|
||||
_setMenuAlignment(entry, stageX);
|
||||
entry._menu.open();
|
||||
entry.menu.open();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function _onPopup(actor) {
|
||||
let entry = actor._menu ? actor : actor.get_parent();
|
||||
let entry = actor.menu ? actor : actor.get_parent();
|
||||
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
||||
if (success)
|
||||
entry._menu.setSourceAlignment(textX / entry.width);
|
||||
entry._menu.open();
|
||||
entry.menu.setSourceAlignment(textX / entry.width);
|
||||
entry.menu.open();
|
||||
};
|
||||
|
||||
function addContextMenu(entry, params) {
|
||||
if (entry._menu)
|
||||
if (entry.menu)
|
||||
return;
|
||||
|
||||
entry._menu = new _EntryMenu(entry, params);
|
||||
entry.menu = new EntryMenu(entry, params);
|
||||
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
||||
entry._menuManager.addMenu(entry._menu);
|
||||
entry._menuManager.addMenu(entry.menu);
|
||||
|
||||
let clickAction;
|
||||
|
||||
|
@ -1,17 +1,21 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Params = imports.misc.params;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
|
||||
const LIST_ITEM_ICON_SIZE = 48;
|
||||
|
||||
@ -48,6 +52,11 @@ function _setLabelsForMessage(dialog, message) {
|
||||
_setLabelText(dialog.descriptionLabel, labels[1]);
|
||||
}
|
||||
|
||||
function _createIcon(gicon) {
|
||||
return new St.Icon({ gicon: gicon,
|
||||
style_class: 'shell-mount-operation-icon' })
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
|
||||
const ListItem = new Lang.Class({
|
||||
@ -91,11 +100,11 @@ const ShellMountOperation = new Lang.Class({
|
||||
Name: 'ShellMountOperation',
|
||||
|
||||
_init: function(source, params) {
|
||||
params = Params.parse(params, { reaskPassword: false });
|
||||
|
||||
this._reaskPassword = params.reaskPassword;
|
||||
params = Params.parse(params, { existingDialog: null });
|
||||
|
||||
this._dialog = null;
|
||||
this._dialogId = 0;
|
||||
this._existingDialog = params.existingDialog;
|
||||
this._processesDialog = null;
|
||||
|
||||
this.mountOp = new Shell.MountOperation();
|
||||
@ -107,99 +116,177 @@ const ShellMountOperation = new Lang.Class({
|
||||
this.mountOp.connect('show-processes-2',
|
||||
Lang.bind(this, this._onShowProcesses2));
|
||||
this.mountOp.connect('aborted',
|
||||
Lang.bind(this, this._onAborted));
|
||||
Lang.bind(this, this.close));
|
||||
this.mountOp.connect('show-unmount-progress',
|
||||
Lang.bind(this, this._onShowUnmountProgress));
|
||||
|
||||
this._icon = new St.Icon({ gicon: source.get_icon(),
|
||||
style_class: 'shell-mount-operation-icon' });
|
||||
this._gicon = source.get_icon();
|
||||
},
|
||||
|
||||
_closeExistingDialog: function() {
|
||||
if (!this._existingDialog)
|
||||
return;
|
||||
|
||||
this._existingDialog.close();
|
||||
this._existingDialog = null;
|
||||
},
|
||||
|
||||
_onAskQuestion: function(op, message, choices) {
|
||||
this._dialog = new ShellMountQuestionDialog(this._icon);
|
||||
this._closeExistingDialog();
|
||||
this._dialog = new ShellMountQuestionDialog(this._gicon);
|
||||
|
||||
this._dialog.connect('response',
|
||||
Lang.bind(this, function(object, choice) {
|
||||
this.mountOp.set_choice(choice);
|
||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||
this._dialogId = this._dialog.connect('response', Lang.bind(this,
|
||||
function(object, choice) {
|
||||
this.mountOp.set_choice(choice);
|
||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||
|
||||
this._dialog.close(global.get_current_time());
|
||||
this._dialog = null;
|
||||
}));
|
||||
this.close();
|
||||
}));
|
||||
|
||||
this._dialog.update(message, choices);
|
||||
this._dialog.open(global.get_current_time());
|
||||
this._dialog.open();
|
||||
},
|
||||
|
||||
_onAskPassword: function(op, message) {
|
||||
this._notificationShowing = true;
|
||||
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
|
||||
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
|
||||
if (this._existingDialog) {
|
||||
this._dialog = this._existingDialog;
|
||||
this._dialog.reaskPassword();
|
||||
} else {
|
||||
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
|
||||
}
|
||||
|
||||
this._source.connect('password-ready',
|
||||
Lang.bind(this, function(source, password) {
|
||||
this.mountOp.set_password(password);
|
||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||
this._dialogId = this._dialog.connect('response', Lang.bind(this,
|
||||
function(object, choice, password, remember) {
|
||||
if (choice == -1) {
|
||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||
} else {
|
||||
if (remember)
|
||||
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
|
||||
else
|
||||
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
|
||||
|
||||
this._notificationShowing = false;
|
||||
this._source.destroy();
|
||||
}));
|
||||
|
||||
this._source.connect('destroy',
|
||||
Lang.bind(this, function() {
|
||||
if (!this._notificationShowing)
|
||||
return;
|
||||
|
||||
this._notificationShowing = false;
|
||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||
}));
|
||||
this.mountOp.set_password(password);
|
||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||
}
|
||||
}));
|
||||
this._dialog.open();
|
||||
},
|
||||
|
||||
_onAborted: function(op) {
|
||||
if (!this._dialog)
|
||||
return;
|
||||
close: function(op) {
|
||||
this._closeExistingDialog();
|
||||
this._processesDialog = null;
|
||||
|
||||
this._dialog.close(global.get_current_time());
|
||||
this._dialog = null;
|
||||
if (this._dialog) {
|
||||
this._dialog.close();
|
||||
this._dialog = null;
|
||||
}
|
||||
|
||||
if (this._notifier) {
|
||||
this._notifier.done();
|
||||
this._notifier = null;
|
||||
}
|
||||
},
|
||||
|
||||
_onShowProcesses2: function(op) {
|
||||
this._closeExistingDialog();
|
||||
|
||||
let processes = op.get_show_processes_pids();
|
||||
let choices = op.get_show_processes_choices();
|
||||
let message = op.get_show_processes_message();
|
||||
|
||||
if (!this._processesDialog) {
|
||||
this._processesDialog = new ShellProcessesDialog(this._icon);
|
||||
this._processesDialog = new ShellProcessesDialog(this._gicon);
|
||||
this._dialog = this._processesDialog;
|
||||
|
||||
this._processesDialog.connect('response',
|
||||
Lang.bind(this, function(object, choice) {
|
||||
if (choice == -1) {
|
||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||
} else {
|
||||
this.mountOp.set_choice(choice);
|
||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||
}
|
||||
this._dialogId = this._processesDialog.connect('response', Lang.bind(this,
|
||||
function(object, choice) {
|
||||
if (choice == -1) {
|
||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||
} else {
|
||||
this.mountOp.set_choice(choice);
|
||||
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||
}
|
||||
|
||||
this._processesDialog.close(global.get_current_time());
|
||||
this._dialog = null;
|
||||
}));
|
||||
this._processesDialog.open(global.get_current_time());
|
||||
this.close();
|
||||
}));
|
||||
this._processesDialog.open();
|
||||
}
|
||||
|
||||
this._processesDialog.update(message, processes, choices);
|
||||
},
|
||||
|
||||
_onShowUnmountProgress: function(op, message, timeLeft, bytesLeft) {
|
||||
if (!this._notifier)
|
||||
this._notifier = new ShellUnmountNotifier();
|
||||
|
||||
if (bytesLeft == 0)
|
||||
this._notifier.done(message);
|
||||
else
|
||||
this._notifier.show(message);
|
||||
},
|
||||
|
||||
borrowDialog: function() {
|
||||
if (this._dialogId != 0) {
|
||||
this._dialog.disconnect(this._dialogId);
|
||||
this._dialogId = 0;
|
||||
}
|
||||
|
||||
return this._dialog;
|
||||
}
|
||||
});
|
||||
|
||||
const ShellUnmountNotifier = new Lang.Class({
|
||||
Name: 'ShellUnmountNotifier',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent('', 'media-removable', St.IconType.FULLCOLOR);
|
||||
|
||||
this._notification = null;
|
||||
Main.messageTray.add(this);
|
||||
},
|
||||
|
||||
show: function(message) {
|
||||
let [header, text] = message.split('\n', 2);
|
||||
|
||||
if (!this._notification) {
|
||||
this._notification = new MessageTray.Notification(this, header, text);
|
||||
this._notification.setTransient(true);
|
||||
this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||
} else {
|
||||
this._notification.update(header, text);
|
||||
}
|
||||
|
||||
this.notify(this._notification);
|
||||
},
|
||||
|
||||
done: function(message) {
|
||||
if (this._notification) {
|
||||
this._notification.destroy();
|
||||
this._notification = null;
|
||||
}
|
||||
|
||||
if (message) {
|
||||
let notification = new MessageTray.Notification(this, message, null);
|
||||
notification.setTransient(true);
|
||||
|
||||
this.notify(notification);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ShellMountQuestionDialog = new Lang.Class({
|
||||
Name: 'ShellMountQuestionDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(icon) {
|
||||
_init: function(gicon) {
|
||||
this.parent({ styleClass: 'mount-question-dialog' });
|
||||
|
||||
let mainContentLayout = new St.BoxLayout();
|
||||
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
||||
y_fill: false });
|
||||
|
||||
this._iconBin = new St.Bin({ child: icon });
|
||||
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
|
||||
mainContentLayout.add(this._iconBin,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
@ -234,62 +321,108 @@ const ShellMountQuestionDialog = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
|
||||
|
||||
const ShellMountPasswordSource = new Lang.Class({
|
||||
Name: 'ShellMountPasswordSource',
|
||||
Extends: MessageTray.Source,
|
||||
const ShellMountPasswordDialog = new Lang.Class({
|
||||
Name: 'ShellMountPasswordDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(message, icon, reaskPassword) {
|
||||
_init: function(message, gicon, flags) {
|
||||
let strings = message.split('\n');
|
||||
this.parent(strings[0]);
|
||||
this.parent({ styleClass: 'prompt-dialog' });
|
||||
|
||||
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox);
|
||||
|
||||
// add ourselves as a source, and popup the notification
|
||||
Main.messageTray.add(this);
|
||||
this.notify(this._notification);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
|
||||
let icon = _createIcon(gicon);
|
||||
mainContentBox.add(icon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
const ShellMountPasswordNotification = new Lang.Class({
|
||||
Name: 'ShellMountPasswordNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(this._messageBox,
|
||||
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
|
||||
|
||||
_init: function(source, strings, icon, reaskPassword) {
|
||||
this.parent(source, strings[0], null, { customContent: true, icon: icon });
|
||||
|
||||
// set the notification to transient and urgent, so that it
|
||||
// expands out
|
||||
this.setTransient(true);
|
||||
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
|
||||
this._messageBox.add(subject,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
if (strings[0])
|
||||
subject.set_text(strings[0]);
|
||||
|
||||
let description = new St.Label({ style_class: 'prompt-dialog-description' });
|
||||
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
description.clutter_text.line_wrap = true;
|
||||
this._messageBox.add(description,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
if (strings[1])
|
||||
this.addBody(strings[1]);
|
||||
description.set_text(strings[1]);
|
||||
|
||||
if (reaskPassword) {
|
||||
let label = new St.Label({ style_class: 'mount-password-reask',
|
||||
text: _("Wrong password, please try again") });
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
||||
this._messageBox.add(this._passwordBox);
|
||||
|
||||
this.addActor(label);
|
||||
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
|
||||
text: _("Passphrase") }));
|
||||
this._passwordBox.add(this._passwordLabel);
|
||||
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: "",
|
||||
can_focus: true});
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
this._passwordBox.add(this._passwordEntry, {expand: true });
|
||||
this.setInitialKeyFocus(this._passwordEntry);
|
||||
|
||||
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
|
||||
text: _("Sorry, that didn\'t work. Please try again.") });
|
||||
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._errorMessageLabel.clutter_text.line_wrap = true;
|
||||
this._errorMessageLabel.hide();
|
||||
this._messageBox.add(this._errorMessageLabel);
|
||||
|
||||
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
|
||||
this._rememberChoice = new CheckBox.CheckBox();
|
||||
this._rememberChoice.getLabelActor().text = _("Remember Passphrase");
|
||||
this._rememberChoice.actor.checked = true;
|
||||
this._messageBox.add(this._rememberChoice.actor);
|
||||
} else {
|
||||
this._rememberChoice = null;
|
||||
}
|
||||
|
||||
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
|
||||
can_focus: true });
|
||||
this.setActionArea(this._responseEntry);
|
||||
let buttons = [{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Unlock"),
|
||||
action: Lang.bind(this, this._onUnlockButton),
|
||||
default: true
|
||||
}];
|
||||
|
||||
this._responseEntry.clutter_text.connect('activate',
|
||||
Lang.bind(this, this._onEntryActivated));
|
||||
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
|
||||
this._responseEntry.grab_key_focus();
|
||||
this.setButtons(buttons);
|
||||
},
|
||||
|
||||
_onEntryActivated: function() {
|
||||
let text = this._responseEntry.get_text();
|
||||
if (text == '')
|
||||
return;
|
||||
reaskPassword: function() {
|
||||
this._passwordEntry.set_text('');
|
||||
this._errorMessageLabel.show();
|
||||
},
|
||||
|
||||
this.source.emit('password-ready', text);
|
||||
_onCancelButton: function() {
|
||||
this.emit('response', -1, '', false);
|
||||
},
|
||||
|
||||
_onUnlockButton: function() {
|
||||
this._onEntryActivate();
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
this.emit('response', 1,
|
||||
this._passwordEntry.get_text(),
|
||||
this._rememberChoice &&
|
||||
this._rememberChoice.actor.checked);
|
||||
}
|
||||
});
|
||||
|
||||
@ -297,14 +430,14 @@ const ShellProcessesDialog = new Lang.Class({
|
||||
Name: 'ShellProcessesDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(icon) {
|
||||
_init: function(gicon) {
|
||||
this.parent({ styleClass: 'show-processes-dialog' });
|
||||
|
||||
let mainContentLayout = new St.BoxLayout();
|
||||
this.contentLayout.add(mainContentLayout, { x_fill: true,
|
||||
y_fill: false });
|
||||
|
||||
this._iconBin = new St.Bin({ child: icon });
|
||||
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
|
||||
mainContentLayout.add(this._iconBin,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
@ -338,21 +471,17 @@ const ShellProcessesDialog = new Lang.Class({
|
||||
scrollView.hide();
|
||||
|
||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(this._applicationList,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
scrollView.add_actor(this._applicationList);
|
||||
|
||||
this._applicationList.connect('actor-added',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 1)
|
||||
if (this._applicationList.get_n_children() == 1)
|
||||
scrollView.show();
|
||||
}));
|
||||
|
||||
this._applicationList.connect('actor-removed',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 0)
|
||||
if (this._applicationList.get_n_children() == 0)
|
||||
scrollView.hide();
|
||||
}));
|
||||
},
|
||||
@ -386,3 +515,253 @@ 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 ShellMountOperationType = {
|
||||
NONE: 0,
|
||||
ASK_PASSWORD: 1,
|
||||
ASK_QUESTION: 2,
|
||||
SHOW_PROCESSES: 3
|
||||
};
|
||||
|
||||
const GnomeShellMountOpHandler = new Lang.Class({
|
||||
Name: 'GnomeShellMountOpHandler',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
|
||||
Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
|
||||
this._dialog = null;
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
this._ensureEmptyRequest();
|
||||
},
|
||||
|
||||
_ensureEmptyRequest: function() {
|
||||
this._currentId = null;
|
||||
this._currentInvocation = null;
|
||||
this._currentType = ShellMountOperationType.NONE;
|
||||
},
|
||||
|
||||
_clearCurrentRequest: function(response, details) {
|
||||
if (this._currentInvocation) {
|
||||
this._currentInvocation.return_value(
|
||||
GLib.Variant.new('(ua{sv})', [response, details]));
|
||||
}
|
||||
|
||||
this._ensureEmptyRequest();
|
||||
},
|
||||
|
||||
_setCurrentRequest: function(invocation, id, type) {
|
||||
let oldId = this._currentId;
|
||||
let oldType = this._currentType;
|
||||
let requestId = id + '@' + invocation.get_sender();
|
||||
|
||||
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
|
||||
|
||||
this._currentInvocation = invocation;
|
||||
this._currentId = requestId;
|
||||
this._currentType = type;
|
||||
|
||||
if (this._dialog && (oldId == requestId) && (oldType == type))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_closeDialog: function() {
|
||||
if (this._dialog) {
|
||||
this._dialog.close();
|
||||
this._dialog = null;
|
||||
}
|
||||
},
|
||||
|
||||
_createGIcon: function(iconName) {
|
||||
let realIconName = iconName ? iconName : 'drive-harddisk';
|
||||
return new Gio.ThemedIcon({ name: realIconName,
|
||||
use_default_fallbacks: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* AskPassword:
|
||||
* @id: an opaque ID identifying the object for which the operation is requested
|
||||
* The ID must be unique in the context of the calling process.
|
||||
* @message: the message to display
|
||||
* @icon_name: the name of an icon to display
|
||||
* @default_user: the default username for display
|
||||
* @default_domain: the default domain for display
|
||||
* @flags: a set of GAskPasswordFlags
|
||||
* @response: a GMountOperationResult
|
||||
* @response_details: a dictionary containing the response details as
|
||||
* entered by the user. The dictionary MAY contain the following properties:
|
||||
* - "password" -> (s): a password to be used to complete the mount operation
|
||||
* - "password_save" -> (u): a GPasswordSave
|
||||
*
|
||||
* The dialog will stay visible until clients call the Close() method, or
|
||||
* another dialog becomes visible.
|
||||
* Calling AskPassword again for the same id will have the effect to clear
|
||||
* the existing dialog and update it with a message indicating the previous
|
||||
* attempt went wrong.
|
||||
*/
|
||||
AskPasswordAsync: function(params, invocation) {
|
||||
let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
|
||||
|
||||
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
|
||||
this._dialog.reaskPassword();
|
||||
return;
|
||||
}
|
||||
|
||||
this._closeDialog();
|
||||
|
||||
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
|
||||
this._dialog.connect('response', Lang.bind(this,
|
||||
function(object, choice, password, remember) {
|
||||
let details = {};
|
||||
let response;
|
||||
|
||||
if (choice == -1) {
|
||||
response = Gio.MountOperationResult.ABORTED;
|
||||
} else {
|
||||
response = Gio.MountOperationResult.HANDLED;
|
||||
|
||||
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
|
||||
details['password_save'] = GLib.Variant.new('u', passSave);
|
||||
details['password'] = GLib.Variant.new('s', password);
|
||||
}
|
||||
|
||||
this._clearCurrentRequest(response, details);
|
||||
}));
|
||||
this._dialog.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* AskQuestion:
|
||||
* @id: an opaque ID identifying the object for which the operation is requested
|
||||
* The ID must be unique in the context of the calling process.
|
||||
* @message: the message to display
|
||||
* @icon_name: the name of an icon to display
|
||||
* @choices: an array of choice strings
|
||||
* GetResponse:
|
||||
* @response: a GMountOperationResult
|
||||
* @response_details: a dictionary containing the response details as
|
||||
* entered by the user. The dictionary MAY contain the following properties:
|
||||
* - "choice" -> (i): the chosen answer among the array of strings passed in
|
||||
*
|
||||
* The dialog will stay visible until clients call the Close() method, or
|
||||
* another dialog becomes visible.
|
||||
* Calling AskQuestion again for the same id will have the effect to clear
|
||||
* update the dialog with the new question.
|
||||
*/
|
||||
AskQuestionAsync: function(params, invocation) {
|
||||
let [id, message, iconName, choices] = params;
|
||||
|
||||
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
|
||||
this._dialog.update(message, choices);
|
||||
return;
|
||||
}
|
||||
|
||||
this._closeDialog();
|
||||
|
||||
this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
|
||||
this._dialog.connect('response', Lang.bind(this,
|
||||
function(object, choice) {
|
||||
this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
|
||||
{ choice: GLib.Variant.new('i', choice) });
|
||||
}));
|
||||
|
||||
this._dialog.update(message, choices);
|
||||
this._dialog.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* ShowProcesses:
|
||||
* @id: an opaque ID identifying the object for which the operation is requested
|
||||
* The ID must be unique in the context of the calling process.
|
||||
* @message: the message to display
|
||||
* @icon_name: the name of an icon to display
|
||||
* @application_pids: the PIDs of the applications to display
|
||||
* @choices: an array of choice strings
|
||||
* @response: a GMountOperationResult
|
||||
* @response_details: a dictionary containing the response details as
|
||||
* entered by the user. The dictionary MAY contain the following properties:
|
||||
* - "choice" -> (i): the chosen answer among the array of strings passed in
|
||||
*
|
||||
* The dialog will stay visible until clients call the Close() method, or
|
||||
* another dialog becomes visible.
|
||||
* Calling ShowProcesses again for the same id will have the effect to clear
|
||||
* the existing dialog and update it with the new message and the new list
|
||||
* of processes.
|
||||
*/
|
||||
ShowProcessesAsync: function(params, invocation) {
|
||||
let [id, message, iconName, applicationPids, choices] = params;
|
||||
|
||||
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
|
||||
this._dialog.update(message, applicationPids, choices);
|
||||
return;
|
||||
}
|
||||
|
||||
this._closeDialog();
|
||||
|
||||
this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
|
||||
this._dialog.connect('response', Lang.bind(this,
|
||||
function(object, choice) {
|
||||
let response;
|
||||
let details = {};
|
||||
|
||||
if (choice == -1) {
|
||||
response = Gio.MountOperationResult.ABORTED;
|
||||
} else {
|
||||
response = Gio.MountOperationResult.HANDLED;
|
||||
details['choice'] = GLib.Variant.new('i', choice);
|
||||
}
|
||||
|
||||
this._clearCurrentRequest(response, details);
|
||||
}));
|
||||
|
||||
this._dialog.update(message, applicationPids, choices);
|
||||
this._dialog.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Close:
|
||||
*
|
||||
* Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
|
||||
* If no dialog is open, does nothing.
|
||||
*/
|
||||
Close: function(params, invocation) {
|
||||
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
|
||||
this._closeDialog();
|
||||
}
|
||||
});
|
||||
|
@ -1,18 +1,10 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||
@ -56,9 +48,9 @@ const ATIndicator = new Lang.Class({
|
||||
let textZoom = this._buildFontItem();
|
||||
this.menu.addMenuItem(textZoom);
|
||||
|
||||
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
|
||||
// 'screen-reader-enabled');
|
||||
// this.menu.addMenuItem(screenReader);
|
||||
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
|
||||
'screen-reader-enabled');
|
||||
this.menu.addMenuItem(screenReader);
|
||||
|
||||
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
|
||||
'screen-keyboard-enabled');
|
||||
@ -83,6 +75,10 @@ const ATIndicator = new Lang.Class({
|
||||
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this.menu.setSettingsVisibility(!locked);
|
||||
},
|
||||
|
||||
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
||||
let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value);
|
||||
if (!writable)
|
||||
|
@ -1,15 +1,11 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GnomeBluetooth = imports.gi.GnomeBluetooth;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
@ -36,11 +32,11 @@ const Indicator = new Lang.Class({
|
||||
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
||||
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
|
||||
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
|
||||
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
|
||||
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
|
||||
this._applet.killswitch_state = this._killswitch.state ?
|
||||
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
|
||||
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
|
||||
GnomeBluetooth.KillswitchState.UNBLOCKED:
|
||||
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||
} else
|
||||
this._killswitch.setToggleState(false);
|
||||
}));
|
||||
@ -92,12 +88,17 @@ const Indicator = new Lang.Class({
|
||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this._isLocked = locked;
|
||||
this._updateKillswitch();
|
||||
},
|
||||
|
||||
_updateKillswitch: function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
|
||||
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
|
||||
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
|
||||
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
|
||||
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
|
||||
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
|
||||
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
|
||||
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
|
||||
|
||||
this._killswitch.setToggleState(on);
|
||||
if (can_toggle)
|
||||
@ -106,10 +107,7 @@ const Indicator = new Lang.Class({
|
||||
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
||||
this._killswitch.setStatus(_("hardware disabled"));
|
||||
|
||||
if (has_adapter)
|
||||
this.actor.show();
|
||||
else
|
||||
this.actor.hide();
|
||||
this.actor.visible = !this._isLocked && has_adapter;
|
||||
|
||||
if (on) {
|
||||
this._discoverable.actor.show();
|
||||
@ -308,7 +306,7 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new Source();
|
||||
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active', St.IconType.SYMBOLIC);
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
@ -333,35 +331,6 @@ const Indicator = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const Source = new Lang.Class({
|
||||
Name: 'BluetoothSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Bluetooth"));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
|
||||
if (this.notification == notification) {
|
||||
// the destroyed notification is the last for this source
|
||||
this.notification.disconnect(this._private_destroyId);
|
||||
this.destroy();
|
||||
}
|
||||
}));
|
||||
|
||||
this.parent(notification);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'bluetooth-active',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
});
|
||||
|
||||
const AuthNotification = new Lang.Class({
|
||||
Name: 'AuthNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
@ -412,7 +381,7 @@ const ConfirmNotification = new Lang.Class({
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
|
||||
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
|
||||
|
||||
this.addButton('matches', _("Matches"));
|
||||
this.addButton('does-not-match', _("Does not match"));
|
||||
@ -448,7 +417,8 @@ const PinNotification = new Lang.Class({
|
||||
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Return) {
|
||||
this.emit('action-invoked', 'ok');
|
||||
if (this._canActivateOkButton())
|
||||
this.emit('action-invoked', 'ok');
|
||||
return true;
|
||||
} else if (key == Clutter.KEY_Escape) {
|
||||
this.emit('action-invoked', 'cancel');
|
||||
@ -461,6 +431,12 @@ const PinNotification = new Lang.Class({
|
||||
this.addButton('ok', _("OK"));
|
||||
this.addButton('cancel', _("Cancel"));
|
||||
|
||||
this.setButtonSensitive('ok', this._canActivateOkButton());
|
||||
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
|
||||
function() {
|
||||
this.setButtonSensitive('ok', this._canActivateOkButton());
|
||||
}));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'ok') {
|
||||
if (this._numeric) {
|
||||
@ -483,6 +459,11 @@ const PinNotification = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_canActivateOkButton: function() {
|
||||
// PINs have a fixed length of 6
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
this.parent(lockTray);
|
||||
global.stage.set_key_focus(this._entry);
|
||||
|
@ -1,47 +1,149 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gkbd = imports.gi.Gkbd;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
try {
|
||||
var IBus = imports.gi.IBus;
|
||||
if (!('new_async' in IBus.Bus))
|
||||
throw "IBus version is too old";
|
||||
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
|
||||
} catch (e) {
|
||||
var IBus = null;
|
||||
log(e);
|
||||
}
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
|
||||
const KEY_CURRENT_INPUT_SOURCE = 'current';
|
||||
const KEY_INPUT_SOURCES = 'sources';
|
||||
|
||||
const INPUT_SOURCE_TYPE_XKB = 'xkb';
|
||||
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
|
||||
|
||||
const IBusManager = new Lang.Class({
|
||||
Name: 'IBusManager',
|
||||
|
||||
_init: function(readyCallback) {
|
||||
if (!IBus)
|
||||
return;
|
||||
|
||||
IBus.init();
|
||||
|
||||
this._readyCallback = readyCallback;
|
||||
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
|
||||
|
||||
this._ibus = null;
|
||||
this._panelService = null;
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
|
||||
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
Lang.bind(this, this._onNameAppeared),
|
||||
Lang.bind(this, this._clear));
|
||||
},
|
||||
|
||||
_clear: function() {
|
||||
if (this._panelService)
|
||||
this._panelService.destroy();
|
||||
if (this._ibus)
|
||||
this._ibus.destroy();
|
||||
|
||||
this._ibus = null;
|
||||
this._panelService = null;
|
||||
this._candidatePopup.setPanelService(null);
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
},
|
||||
|
||||
_onNameAppeared: function() {
|
||||
this._ibus = IBus.Bus.new_async();
|
||||
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
|
||||
},
|
||||
|
||||
_onConnected: function() {
|
||||
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
|
||||
this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
||||
IBus.BusNameFlag.REPLACE_EXISTING,
|
||||
-1, null,
|
||||
Lang.bind(this, this._initPanelService));
|
||||
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
|
||||
},
|
||||
|
||||
_initEngines: function(ibus, result) {
|
||||
let enginesList = this._ibus.list_engines_async_finish(result);
|
||||
if (enginesList) {
|
||||
for (let i = 0; i < enginesList.length; ++i) {
|
||||
let name = enginesList[i].get_name();
|
||||
this._engines[name] = enginesList[i];
|
||||
}
|
||||
} else {
|
||||
this._clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateReadiness();
|
||||
},
|
||||
|
||||
_initPanelService: function(ibus, result) {
|
||||
let success = this._ibus.request_name_async_finish(result);
|
||||
if (success) {
|
||||
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
||||
object_path: IBus.PATH_PANEL });
|
||||
this._candidatePopup.setPanelService(this._panelService);
|
||||
} else {
|
||||
this._clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateReadiness();
|
||||
},
|
||||
|
||||
_updateReadiness: function() {
|
||||
this._ready = (Object.keys(this._engines).length > 0 &&
|
||||
this._panelService != null);
|
||||
|
||||
if (this._ready && this._readyCallback)
|
||||
this._readyCallback();
|
||||
},
|
||||
|
||||
getEngineDesc: function(id) {
|
||||
if (!IBus || !this._ready)
|
||||
return null;
|
||||
|
||||
return this._engines[id];
|
||||
}
|
||||
});
|
||||
|
||||
const LayoutMenuItem = new Lang.Class({
|
||||
Name: 'LayoutMenuItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function(config, id, indicator, long_name) {
|
||||
_init: function(displayName, shortName) {
|
||||
this.parent();
|
||||
|
||||
this._config = config;
|
||||
this._id = id;
|
||||
this.label = new St.Label({ text: long_name });
|
||||
this.indicator = indicator;
|
||||
this.label = new St.Label({ text: displayName });
|
||||
this.indicator = new St.Label({ text: shortName });
|
||||
this.addActor(this.label);
|
||||
this.addActor(this.indicator);
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
this.parent(event);
|
||||
|
||||
this._config.lock_group(this._id);
|
||||
}
|
||||
});
|
||||
|
||||
const XKBIndicator = new Lang.Class({
|
||||
Name: 'XKBIndicator',
|
||||
const InputSourceIndicator = new Lang.Class({
|
||||
Name: 'InputSourceIndicator',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0);
|
||||
this.parent(0.0, _("Keyboard"));
|
||||
|
||||
this._container = new Shell.GenericContainer();
|
||||
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
||||
@ -50,122 +152,171 @@ const XKBIndicator = new Lang.Class({
|
||||
this.actor.add_actor(this._container);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
|
||||
this._container.add_actor(this._iconActor);
|
||||
this._labelActors = [ ];
|
||||
this._layoutItems = [ ];
|
||||
this._labelActors = {};
|
||||
this._layoutItems = {};
|
||||
|
||||
this._showFlags = false;
|
||||
this._config = Gkbd.Configuration.get();
|
||||
this._config.connect('changed', Lang.bind(this, this._syncConfig));
|
||||
this._config.connect('group-changed', Lang.bind(this, this._syncGroup));
|
||||
this._config.start_listen();
|
||||
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
|
||||
|
||||
this._syncConfig();
|
||||
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
this._xkbInfo = new GnomeDesktop.XkbInfo();
|
||||
|
||||
if (global.session_type == Shell.SessionType.USER) {
|
||||
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
|
||||
|
||||
this._inputSourcesChanged();
|
||||
|
||||
// re-using "allowSettings" for the keyboard layout is a bit shady,
|
||||
// but at least for now it is used as "allow popping up windows
|
||||
// from shell menus"; we can always add a separate sessionMode
|
||||
// option if need arises.
|
||||
if (Main.sessionMode.allowSettings) {
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
|
||||
Main.overview.hide();
|
||||
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
|
||||
}));
|
||||
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
||||
}
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
},
|
||||
|
||||
_adjustGroupNames: function(names) {
|
||||
// Disambiguate duplicate names with a subscript
|
||||
// This is O(N^2) to avoid sorting names
|
||||
// but N <= 4 so who cares?
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
let name = names[i];
|
||||
let cnt = 0;
|
||||
for (let j = i + 1; j < names.length; j++) {
|
||||
if (names[j] == name) {
|
||||
cnt++;
|
||||
// U+2081 SUBSCRIPT ONE
|
||||
names[j] = name + String.fromCharCode(0x2081 + cnt);
|
||||
}
|
||||
}
|
||||
if (cnt != 0)
|
||||
names[i] = name + '\u2081';
|
||||
setLockedState: function(locked) {
|
||||
if (Main.sessionMode.allowSettings) {
|
||||
this._showLayoutItem.actor.visible = !locked;
|
||||
}
|
||||
|
||||
return names;
|
||||
this.menu.setSettingsVisibility(!locked);
|
||||
},
|
||||
|
||||
_syncConfig: function() {
|
||||
this._showFlags = this._config.if_flags_shown();
|
||||
if (this._showFlags) {
|
||||
this._container.set_skip_paint(this._iconActor, false);
|
||||
_currentInputSourceChanged: function() {
|
||||
let nVisibleSources = Object.keys(this._layoutItems).length;
|
||||
if (nVisibleSources < 2)
|
||||
return;
|
||||
|
||||
let nSources = this._settings.get_value(KEY_INPUT_SOURCES).n_children();
|
||||
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
if (newCurrentSourceIndex >= nSources)
|
||||
return;
|
||||
|
||||
if (!this._layoutItems[newCurrentSourceIndex]) {
|
||||
// This source index is invalid as we weren't able to
|
||||
// build a menu item for it, so we hide ourselves since we
|
||||
// can't fix it here. *shrug*
|
||||
this.menu.close();
|
||||
this.actor.hide();
|
||||
return;
|
||||
} else {
|
||||
this._container.set_skip_paint(this._iconActor, true);
|
||||
this.actor.show();
|
||||
}
|
||||
|
||||
let groups = this._config.get_group_names();
|
||||
if (groups.length > 1) {
|
||||
if (this._layoutItems[this._currentSourceIndex]) {
|
||||
this._layoutItems[this._currentSourceIndex].setShowDot(false);
|
||||
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
|
||||
}
|
||||
|
||||
this._layoutItems[newCurrentSourceIndex].setShowDot(true);
|
||||
this._container.set_skip_paint(this._labelActors[newCurrentSourceIndex], false);
|
||||
|
||||
this._currentSourceIndex = newCurrentSourceIndex;
|
||||
},
|
||||
|
||||
_inputSourcesChanged: function() {
|
||||
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||
let nSources = sources.n_children();
|
||||
|
||||
for (let i in this._layoutItems)
|
||||
this._layoutItems[i].destroy();
|
||||
|
||||
for (let i in this._labelActors)
|
||||
this._labelActors[i].destroy();
|
||||
|
||||
this._layoutItems = {};
|
||||
this._labelActors = {};
|
||||
|
||||
let infos = [];
|
||||
let infosByShortName = {};
|
||||
|
||||
for (let i = 0; i < nSources; i++) {
|
||||
let info = { exists: false };
|
||||
let [type, id] = sources.get_child_value(i).deep_unpack();
|
||||
|
||||
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||
[info.exists, info.displayName, info.shortName, , ] =
|
||||
this._xkbInfo.get_layout_info(id);
|
||||
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||
if (engineDesc) {
|
||||
info.exists = true;
|
||||
info.displayName = engineDesc.get_longname();
|
||||
info.shortName = engineDesc.get_symbol();
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.exists)
|
||||
continue;
|
||||
|
||||
info.sourceIndex = i;
|
||||
|
||||
if (!(info.shortName in infosByShortName))
|
||||
infosByShortName[info.shortName] = [];
|
||||
infosByShortName[info.shortName].push(info);
|
||||
infos.push(info);
|
||||
}
|
||||
|
||||
if (infos.length > 1) {
|
||||
this.actor.show();
|
||||
} else {
|
||||
this.menu.close();
|
||||
this.actor.hide();
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._layoutItems.length; i++)
|
||||
this._layoutItems[i].destroy();
|
||||
for (let i = 0; i < infos.length; i++) {
|
||||
let info = infos[i];
|
||||
if (infosByShortName[info.shortName].length > 1) {
|
||||
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
|
||||
info.shortName += String.fromCharCode(0x2080 + sub);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
this._labelActors[i].destroy();
|
||||
|
||||
let short_names = this._adjustGroupNames(this._config.get_short_group_names());
|
||||
|
||||
this._selectedLayout = null;
|
||||
this._layoutItems = [ ];
|
||||
this._selectedLabel = null;
|
||||
this._labelActors = [ ];
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let icon_name = this._config.get_group_name(i);
|
||||
let actor;
|
||||
if (this._showFlags)
|
||||
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
|
||||
else
|
||||
actor = new St.Label({ text: short_names[i] });
|
||||
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
|
||||
item._short_group_name = short_names[i];
|
||||
item._icon_name = icon_name;
|
||||
this._layoutItems.push(item);
|
||||
let item = new LayoutMenuItem(info.displayName, info.shortName);
|
||||
this._layoutItems[info.sourceIndex] = item;
|
||||
this.menu.addMenuItem(item, i);
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
||||
GLib.Variant.new_uint32(info.sourceIndex));
|
||||
}));
|
||||
|
||||
let shortLabel = new St.Label({ text: short_names[i] });
|
||||
this._labelActors.push(shortLabel);
|
||||
let shortLabel = new St.Label({ text: info.shortName });
|
||||
this._labelActors[info.sourceIndex] = shortLabel;
|
||||
this._container.add_actor(shortLabel);
|
||||
this._container.set_skip_paint(shortLabel, true);
|
||||
}
|
||||
|
||||
this._syncGroup();
|
||||
this._currentInputSourceChanged();
|
||||
},
|
||||
|
||||
_syncGroup: function() {
|
||||
let selected = this._config.get_current_group();
|
||||
_showLayout: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._selectedLayout) {
|
||||
this._selectedLayout.setShowDot(false);
|
||||
this._selectedLayout = null;
|
||||
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
let [type, id] = sources.get_child_value(current).deep_unpack();
|
||||
let xkbLayout = '';
|
||||
let xkbVariant = '';
|
||||
|
||||
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
|
||||
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||
if (engineDesc) {
|
||||
xkbLayout = engineDesc.get_layout();
|
||||
xkbVariant = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (this._selectedLabel) {
|
||||
this._container.set_skip_paint(this._selectedLabel, true);
|
||||
this._selectedLabel = null;
|
||||
}
|
||||
if (!xkbLayout || xkbLayout.length == 0)
|
||||
return;
|
||||
|
||||
let item = this._layoutItems[selected];
|
||||
item.setShowDot(true);
|
||||
let description = xkbLayout;
|
||||
if (xkbVariant.length > 0)
|
||||
description = description + '\t' + xkbVariant;
|
||||
|
||||
this._iconActor.icon_name = item._icon_name;
|
||||
this._selectedLabel = this._labelActors[selected];
|
||||
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
|
||||
|
||||
this._selectedLayout = item;
|
||||
Util.spawn(['gkbd-keyboard-display', '-l', description]);
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
||||
@ -173,15 +324,11 @@ const XKBIndicator = new Lang.Class({
|
||||
// for the height of all children, but we ignore the results
|
||||
// for those we don't actually display.
|
||||
let max_min_width = 0, max_natural_width = 0;
|
||||
if (this._showFlags)
|
||||
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++) {
|
||||
for (let i in this._labelActors) {
|
||||
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
|
||||
if (!this._showFlags) {
|
||||
max_min_width = Math.max(max_min_width, min_width);
|
||||
max_natural_width = Math.max(max_natural_width, natural_width);
|
||||
}
|
||||
max_min_width = Math.max(max_min_width, min_width);
|
||||
max_natural_width = Math.max(max_natural_width, natural_width);
|
||||
}
|
||||
|
||||
alloc.min_size = max_min_width;
|
||||
@ -190,15 +337,11 @@ const XKBIndicator = new Lang.Class({
|
||||
|
||||
_containerGetPreferredHeight: function(container, for_width, alloc) {
|
||||
let max_min_height = 0, max_natural_height = 0;
|
||||
if (this._showFlags)
|
||||
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++) {
|
||||
|
||||
for (let i in this._labelActors) {
|
||||
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
|
||||
if (!this._showFlags) {
|
||||
max_min_height = Math.max(max_min_height, min_height);
|
||||
max_natural_height = Math.max(max_natural_height, natural_height);
|
||||
}
|
||||
max_min_height = Math.max(max_min_height, min_height);
|
||||
max_natural_height = Math.max(max_natural_height, natural_height);
|
||||
}
|
||||
|
||||
alloc.min_size = max_min_height;
|
||||
@ -212,8 +355,7 @@ const XKBIndicator = new Lang.Class({
|
||||
box.y2 -= box.y1;
|
||||
box.y1 = 0;
|
||||
|
||||
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
for (let i in this._labelActors)
|
||||
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
}
|
||||
});
|
||||
|
@ -1,12 +1,9 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
const ByteArray = imports.byteArray;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
@ -101,11 +98,10 @@ const NMNetworkMenuItem = new Lang.Class({
|
||||
Name: 'NMNetworkMenuItem',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function(accessPoints, title, params) {
|
||||
_init: function(bestAP, title, params) {
|
||||
this.parent(params);
|
||||
|
||||
accessPoints = sortAccessPoints(accessPoints);
|
||||
this.bestAP = accessPoints[0];
|
||||
this.bestAP = bestAP;
|
||||
|
||||
if (!title) {
|
||||
let ssid = this.bestAP.get_ssid();
|
||||
@ -127,24 +123,10 @@ const NMNetworkMenuItem = new Lang.Class({
|
||||
this.bestAP._secType != NMAccessPointSecurity.NONE)
|
||||
this._secureIcon.icon_name = 'network-wireless-encrypted';
|
||||
this._icons.add_actor(this._secureIcon);
|
||||
|
||||
this._accessPoints = [ ];
|
||||
for (let i = 0; i < accessPoints.length; i++) {
|
||||
let ap = accessPoints[i];
|
||||
// need a wrapper object here, because the access points can be shared
|
||||
// between many NMNetworkMenuItems
|
||||
let apObj = {
|
||||
ap: ap,
|
||||
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
|
||||
};
|
||||
this._accessPoints.push(apObj);
|
||||
}
|
||||
},
|
||||
|
||||
_updated: function(ap) {
|
||||
if (ap.strength > this.bestAP.strength)
|
||||
this.bestAP = ap;
|
||||
|
||||
updateBestAP: function(ap) {
|
||||
this.bestAP = ap;
|
||||
this._signalIcon.icon_name = this._getIcon();
|
||||
},
|
||||
|
||||
@ -153,36 +135,6 @@ const NMNetworkMenuItem = new Lang.Class({
|
||||
return 'network-workgroup';
|
||||
else
|
||||
return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength);
|
||||
},
|
||||
|
||||
updateAccessPoints: function(accessPoints) {
|
||||
for (let i = 0; i < this._accessPoints.length; i++) {
|
||||
let apObj = this._accessPoints[i];
|
||||
apObj.ap.disconnect(apObj.updateId);
|
||||
apObj.updateId = 0;
|
||||
}
|
||||
|
||||
accessPoints = sortAccessPoints(accessPoints);
|
||||
this.bestAP = accessPoints[0];
|
||||
this._accessPoints = [ ];
|
||||
for (let i = 0; i < accessPoints; i++) {
|
||||
let ap = accessPoints[i];
|
||||
let apObj = {
|
||||
ap: ap,
|
||||
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
|
||||
};
|
||||
this._accessPoints.push(apObj);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
for (let i = 0; i < this._accessPoints.length; i++) {
|
||||
let apObj = this._accessPoints[i];
|
||||
apObj.ap.disconnect(apObj.updateId);
|
||||
apObj.updateId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
|
||||
@ -297,16 +249,17 @@ const NMDevice = new Lang.Class({
|
||||
this._client = client;
|
||||
this._connections = [ ];
|
||||
for (let i = 0; i < connections.length; i++) {
|
||||
if (!connections[i]._uuid)
|
||||
if (!connections[i].get_uuid())
|
||||
continue;
|
||||
if (!this.connectionValid(connections[i]))
|
||||
continue;
|
||||
// record the connection
|
||||
let obj = {
|
||||
connection: connections[i],
|
||||
name: connections[i]._name,
|
||||
uuid: connections[i]._uuid,
|
||||
name: connections[i].get_id(),
|
||||
uuid: connections[i].get_uuid(),
|
||||
timestamp: connections[i]._timestamp,
|
||||
item: null,
|
||||
};
|
||||
this._connections.push(obj);
|
||||
}
|
||||
@ -401,48 +354,46 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
checkConnection: function(connection) {
|
||||
let pos = this._findConnection(connection._uuid);
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
let exists = pos != -1;
|
||||
let valid = this.connectionValid(connection);
|
||||
let similar = false;
|
||||
if (exists) {
|
||||
let existing = this._connections[pos];
|
||||
|
||||
if (exists && !valid)
|
||||
this.removeConnection(connection);
|
||||
else if (!exists && valid)
|
||||
this.addConnection(connection);
|
||||
else if (exists && valid) {
|
||||
// propagate changes and update the UI
|
||||
|
||||
if (this._connections[pos].timestamp != connection._timestamp) {
|
||||
this._connections[pos].timestamp = connection._timestamp;
|
||||
this._connections.sort(this._connectionSortFunction);
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
// Check if connection changed name or id
|
||||
similar = existing.name == connection.get_id() &&
|
||||
existing.timestamp == connection._timestamp;
|
||||
}
|
||||
|
||||
if (exists && valid && similar) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (exists)
|
||||
this.removeConnection(connection);
|
||||
if (valid)
|
||||
this.addConnection(connection);
|
||||
},
|
||||
|
||||
addConnection: function(connection) {
|
||||
// record the connection
|
||||
let obj = {
|
||||
connection: connection,
|
||||
name: connection._name,
|
||||
uuid: connection._uuid,
|
||||
name: connection.get_id(),
|
||||
uuid: connection.get_uuid(),
|
||||
timestamp: connection._timestamp,
|
||||
item: null,
|
||||
};
|
||||
this._connections.push(obj);
|
||||
this._connections.sort(this._connectionSortFunction);
|
||||
Util.insertSorted(this._connections, obj, this._connectionSortFunction);
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
removeConnection: function(connection) {
|
||||
if (!connection._uuid) {
|
||||
log('Cannot remove a connection without an UUID');
|
||||
return;
|
||||
}
|
||||
let pos = this._findConnection(connection._uuid);
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
if (pos == -1) {
|
||||
// this connection was never added, nothing to do here
|
||||
return;
|
||||
@ -614,7 +565,7 @@ const NMDevice = new Lang.Class({
|
||||
let title;
|
||||
let active = this._activeConnection._connection;
|
||||
if (active) {
|
||||
title = active._name;
|
||||
title = active.get_id();
|
||||
} else {
|
||||
/* TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
and we cannot access its settings (including the name) */
|
||||
@ -707,18 +658,15 @@ const NMDeviceWired = new Lang.Class({
|
||||
// the device
|
||||
// we can do it here because addConnection and removeConnection
|
||||
// both call _createSection at some point
|
||||
if (this._connections.length <= 1)
|
||||
this.section.actor.hide();
|
||||
else
|
||||
this.section.actor.show();
|
||||
this.section.actor.visible = this._connections.length > 1;
|
||||
},
|
||||
|
||||
_createAutomaticConnection: function() {
|
||||
let connection = new NetworkManager.Connection();
|
||||
connection._uuid = NetworkManager.utils_uuid_generate();
|
||||
let uuid = NetworkManager.utils_uuid_generate();
|
||||
connection.add_setting(new NetworkManager.SettingWired());
|
||||
connection.add_setting(new NetworkManager.SettingConnection({
|
||||
uuid: connection._uuid,
|
||||
uuid: uuid,
|
||||
id: this._autoConnectionName,
|
||||
type: NetworkManager.SETTING_WIRED_SETTING_NAME,
|
||||
autoconnect: true
|
||||
@ -862,10 +810,10 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
|
||||
_createAutomaticConnection: function() {
|
||||
let connection = new NetworkManager.Connection;
|
||||
connection._uuid = NetworkManager.utils_uuid_generate();
|
||||
let uuid = NetworkManager.utils_uuid_generate();
|
||||
connection.add_setting(new NetworkManager.SettingBluetooth);
|
||||
connection.add_setting(new NetworkManager.SettingConnection({
|
||||
uuid: connection._uuid,
|
||||
uuid: uuid,
|
||||
id: this._autoConnectionName,
|
||||
type: NetworkManager.SETTING_BLUETOOTH_SETTING_NAME,
|
||||
autoconnect: false
|
||||
@ -900,12 +848,12 @@ const NMDeviceVPN = new Lang.Class({
|
||||
Name: 'NMDeviceVPN',
|
||||
Extends: NMDevice,
|
||||
|
||||
_init: function(client) {
|
||||
_init: function(client, device, connections) {
|
||||
// Disable autoconnections
|
||||
this._autoConnectionName = null;
|
||||
this.category = NMConnectionCategory.VPN;
|
||||
|
||||
this.parent(client, null, [ ]);
|
||||
this.parent(client, null, connections);
|
||||
},
|
||||
|
||||
connectionValid: function(connection) {
|
||||
@ -917,13 +865,24 @@ const NMDeviceVPN = new Lang.Class({
|
||||
},
|
||||
|
||||
get connected() {
|
||||
return !!this._activeConnection;
|
||||
if (!this._activeConnection)
|
||||
return false;
|
||||
|
||||
return this._activeConnection.vpn_state == NetworkManager.VPNConnectionState.ACTIVATED;
|
||||
},
|
||||
|
||||
setActiveConnection: function(activeConnection) {
|
||||
if (this._stateChangeId)
|
||||
this._activeConnection.disconnect(this._stateChangeId);
|
||||
this._stateChangeId = 0;
|
||||
|
||||
this.parent(activeConnection);
|
||||
|
||||
this.emit('active-connection-changed');
|
||||
if (this._activeConnection)
|
||||
this._stateChangeId = this._activeConnection.connect('vpn-state-changed',
|
||||
Lang.bind(this, this._connectionStateChanged));
|
||||
|
||||
this.emit('state-changed');
|
||||
},
|
||||
|
||||
_shouldShowConnectionList: function() {
|
||||
@ -936,7 +895,39 @@ const NMDeviceVPN = new Lang.Class({
|
||||
},
|
||||
|
||||
getStatusLabel: function() {
|
||||
return null;
|
||||
if (!this._activeConnection) // Same as DISCONNECTED
|
||||
return null;
|
||||
|
||||
switch(this._activeConnection.vpn_state) {
|
||||
case NetworkManager.VPNConnectionState.DISCONNECTED:
|
||||
case NetworkManager.VPNConnectionState.ACTIVATED:
|
||||
return null;
|
||||
case NetworkManager.VPNConnectionState.PREPARE:
|
||||
case NetworkManager.VPNConnectionState.CONNECT:
|
||||
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
|
||||
return _("connecting...");
|
||||
case NetworkManager.VPNConnectionState.NEED_AUTH:
|
||||
/* Translators: this is for network connections that require some kind of key or password */
|
||||
return _("authentication required");
|
||||
case NetworkManager.VPNConnectionState.FAILED:
|
||||
return _("connection failed");
|
||||
default:
|
||||
log('VPN connection state invalid, is %d'.format(this.device.state));
|
||||
return 'invalid';
|
||||
}
|
||||
},
|
||||
|
||||
_connectionStateChanged: function(connection, newstate, reason) {
|
||||
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
|
||||
// FIXME: if we ever want to show something based on reason,
|
||||
// we need to convert from NetworkManager.VPNConnectionStateReason
|
||||
// to NetworkManager.DeviceStateReason
|
||||
this.emit('activation-failed', reason);
|
||||
}
|
||||
|
||||
// Differently from real NMDevices, there is no need to queue
|
||||
// an update of the menu section, contents wouldn't change anyway
|
||||
this.emit('state-changed');
|
||||
}
|
||||
});
|
||||
|
||||
@ -987,6 +978,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
obj.ssidText = ssidToLabel(obj.ssid);
|
||||
this._networks.push(obj);
|
||||
}
|
||||
ap._updateId = ap.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
|
||||
|
||||
// Check if some connection is valid for this AP
|
||||
for (let j = 0; j < validConnections.length; j++) {
|
||||
@ -998,6 +990,10 @@ const NMDeviceWireless = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
// Sort APs within each network by strength
|
||||
for (let i = 0; i < this._networks.length; i++)
|
||||
sortAccessPoints(this._networks[i].accessPoints);
|
||||
|
||||
if (this.device.active_access_point) {
|
||||
let networkPos = this._findNetwork(this.device.active_access_point);
|
||||
|
||||
@ -1038,13 +1034,8 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
|
||||
setEnabled: function(enabled) {
|
||||
if (enabled) {
|
||||
this.statusItem.actor.show();
|
||||
this.section.actor.show();
|
||||
} else {
|
||||
this.statusItem.actor.hide();
|
||||
this.section.actor.hide();
|
||||
}
|
||||
this.statusItem.actor.visible = enabled;
|
||||
this.section.actor.visible = enabled;
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
@ -1085,7 +1076,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
// the user toggles the switch and has more than one wireless device)
|
||||
if (this._networks.length > 0) {
|
||||
let connection = this._createAutomaticConnection(this._networks[0]);
|
||||
let accessPoints = sortAccessPoints(this._networks[0].accessPoints);
|
||||
let accessPoints = this._networks[0].accessPoints;
|
||||
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null);
|
||||
}
|
||||
},
|
||||
@ -1155,6 +1146,13 @@ const NMDeviceWireless = new Lang.Class({
|
||||
else if (!oneHasConnection && twoHasConnection)
|
||||
return 1;
|
||||
|
||||
let oneStrength = one.accessPoints[0].strength;
|
||||
let twoStrength = two.accessPoints[0].strength;
|
||||
|
||||
// place stronger connections first
|
||||
if (oneStrength != twoStrength)
|
||||
return oneStrength < twoStrength ? 1 : -1;
|
||||
|
||||
let oneHasSecurity = one.security != NMAccessPointSecurity.NONE;
|
||||
let twoHasSecurity = two.security != NMAccessPointSecurity.NONE;
|
||||
|
||||
@ -1204,6 +1202,28 @@ const NMDeviceWireless = new Lang.Class({
|
||||
return -1;
|
||||
},
|
||||
|
||||
_onApStrengthChanged: function(ap) {
|
||||
let res = this._findExistingNetwork(ap);
|
||||
if (res == null) {
|
||||
// Uhm... stale signal?
|
||||
return;
|
||||
}
|
||||
|
||||
let network = this._networks[res.network];
|
||||
network.accessPoints.splice(res.ap, 1);
|
||||
Util.insertSorted(network.accessPoints, ap, function(one, two) {
|
||||
return two.strength - one.strength;
|
||||
});
|
||||
|
||||
this._networks.splice(res.network, 1);
|
||||
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
|
||||
|
||||
if (newPos != res.network) {
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_accessPointAdded: function(device, accessPoint) {
|
||||
if (accessPoint.get_ssid() == null) {
|
||||
// This access point is not visible yet
|
||||
@ -1223,9 +1243,11 @@ const NMDeviceWireless = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
apObj.accessPoints.push(accessPoint);
|
||||
Util.insertSorted(apObj.accessPoints, accessPoint, function(one, two) {
|
||||
return two.strength - one.strength;
|
||||
});
|
||||
if (apObj.item)
|
||||
apObj.item.updateAccessPoints(apObj.accessPoints);
|
||||
apObj.item.updateBestAP(apObj.accessPoints[0]);
|
||||
} else {
|
||||
apObj = { ssid: accessPoint.get_ssid(),
|
||||
mode: accessPoint.mode,
|
||||
@ -1236,6 +1258,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
};
|
||||
apObj.ssidText = ssidToLabel(apObj.ssid);
|
||||
}
|
||||
accessPoint._updateId = accessPoint.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
|
||||
|
||||
// check if this enables new connections for this group
|
||||
for (let i = 0; i < this._connections.length; i++) {
|
||||
@ -1243,23 +1266,26 @@ const NMDeviceWireless = new Lang.Class({
|
||||
if (accessPoint.connection_valid(connection) &&
|
||||
apObj.connections.indexOf(connection) == -1) {
|
||||
apObj.connections.push(connection);
|
||||
|
||||
// this potentially changes the order
|
||||
needsupdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == -1 || needsupdate) {
|
||||
if (pos != -1)
|
||||
this._networks.splice(pos, 1);
|
||||
pos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
|
||||
if (pos != -1)
|
||||
this._networks.splice(pos, 1);
|
||||
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
|
||||
|
||||
// Queue an update of the UI if we changed the order
|
||||
if (newPos != pos) {
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_accessPointRemoved: function(device, accessPoint) {
|
||||
if (accessPoint._updateId) {
|
||||
accessPoint.disconnect(accessPoint._updateId);
|
||||
accessPoint._updateId = 0;
|
||||
}
|
||||
|
||||
let res = this._findExistingNetwork(accessPoint);
|
||||
|
||||
if (res == null) {
|
||||
@ -1301,17 +1327,30 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this._overflowItem = null;
|
||||
}
|
||||
}
|
||||
this._networks.splice(res.network, 1);
|
||||
|
||||
} else if (apObj.item)
|
||||
apObj.item.updateAccessPoints(apObj.accessPoints);
|
||||
this._networks.splice(res.network, 1);
|
||||
} else {
|
||||
let okPrev = true, okNext = true;
|
||||
|
||||
if (res.network > 0)
|
||||
okPrev = this._networkSortFunction(this._networks[res.network - 1], apObj) >= 0;
|
||||
if (res.network < this._networks.length-1)
|
||||
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
|
||||
|
||||
if (!okPrev || !okNext) {
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
} else if (apObj.item) {
|
||||
apObj.item.updateBestAP(apObj.accessPoints[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_createAPItem: function(connection, accessPointObj, useConnectionName) {
|
||||
let item = new NMNetworkMenuItem(accessPointObj.accessPoints, useConnectionName ? connection._name : undefined);
|
||||
let item = new NMNetworkMenuItem(accessPointObj.accessPoints[0], useConnectionName ? connection.get_id() : undefined);
|
||||
item._connection = connection;
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
let accessPoints = sortAccessPoints(accessPointObj.accessPoints);
|
||||
let accessPoints = accessPointObj.accessPoints;
|
||||
for (let i = 0; i < accessPoints.length; i++) {
|
||||
if (accessPoints[i].connection_valid(connection)) {
|
||||
this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null);
|
||||
@ -1331,9 +1370,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
|
||||
removeConnection: function(connection) {
|
||||
if (!connection._uuid)
|
||||
return;
|
||||
let pos = this._findConnection(connection._uuid);
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
if (pos == -1) {
|
||||
// removing connection that was never added
|
||||
return;
|
||||
@ -1347,7 +1384,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
let apObj = this._networks[i];
|
||||
let connections = apObj.connections;
|
||||
for (let k = 0; k < connections.length; k++) {
|
||||
if (connections[k]._uuid == connection._uuid) {
|
||||
if (connections[k].get_uuid() == connection.get_uuid()) {
|
||||
// remove the connection from the access point group
|
||||
connections.splice(k);
|
||||
forceupdate = forceupdate || connections.length == 0;
|
||||
@ -1363,7 +1400,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
forceupdate = true;
|
||||
} else {
|
||||
for (let j = 0; j < items.length; j++) {
|
||||
if (items[j]._connection._uuid == connection._uuid) {
|
||||
if (items[j]._connection.get_uuid() == connection.get_uuid()) {
|
||||
items[j].destroy();
|
||||
break;
|
||||
}
|
||||
@ -1390,8 +1427,8 @@ const NMDeviceWireless = new Lang.Class({
|
||||
// record the connection
|
||||
let obj = {
|
||||
connection: connection,
|
||||
name: connection._name,
|
||||
uuid: connection._uuid,
|
||||
name: connection.get_id(),
|
||||
uuid: connection.get_uuid(),
|
||||
};
|
||||
this._connections.push(obj);
|
||||
|
||||
@ -1420,27 +1457,19 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
|
||||
_createActiveConnectionItem: function() {
|
||||
let icon, title;
|
||||
if (this._activeConnection && this._activeConnection._connection) {
|
||||
let connection = this._activeConnection._connection;
|
||||
if (this._activeNetwork)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
|
||||
{ reactive: false });
|
||||
else
|
||||
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
|
||||
'network-wireless-connected',
|
||||
{ reactive: false });
|
||||
} else {
|
||||
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
|
||||
let menuItem;
|
||||
if (this._activeNetwork)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
|
||||
{ reactive: false });
|
||||
else
|
||||
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
|
||||
'network-wireless-connected',
|
||||
{ reactive: false });
|
||||
}
|
||||
let title;
|
||||
if (this._activeConnection && this._activeConnection._connection)
|
||||
title = this._activeConnection._connection.get_id();
|
||||
else
|
||||
title = _("Connected (private)");
|
||||
|
||||
if (this._activeNetwork)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem(this.device.active_access_point, undefined,
|
||||
{ reactive: false });
|
||||
else
|
||||
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(title,
|
||||
'network-wireless-connected',
|
||||
{ reactive: false });
|
||||
this._activeConnectionItem.setShowDot(true);
|
||||
},
|
||||
|
||||
@ -1480,9 +1509,9 @@ const NMDeviceWireless = new Lang.Class({
|
||||
apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true));
|
||||
}
|
||||
} else {
|
||||
apObj.item = new NMNetworkMenuItem(apObj.accessPoints);
|
||||
apObj.item = new NMNetworkMenuItem(apObj.accessPoints[0]);
|
||||
apObj.item.connect('activate', Lang.bind(this, function() {
|
||||
let accessPoints = sortAccessPoints(apObj.accessPoints);
|
||||
let accessPoints = apObj.accessPoints;
|
||||
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|
||||
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
|
||||
// 802.1x-enabled APs require further configuration, so they're
|
||||
@ -1535,10 +1564,25 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
const NMApplet = new Lang.Class({
|
||||
Name: 'NMApplet',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent('network-error', _("Network"));
|
||||
this.parent(0.0, _('Network'));
|
||||
|
||||
this._box = new St.BoxLayout({ name: 'networkMenu' });
|
||||
this.actor.add_actor (this._box);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
this._primaryIcon = new St.Icon({ icon_name: 'network-offline',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'system-status-icon' });
|
||||
this._box.add_actor(this._primaryIcon);
|
||||
|
||||
this._secondaryIcon = new St.Icon({ icon_name: 'network-vpn',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'system-status-icon',
|
||||
visible: false });
|
||||
this._box.add_actor(this._secondaryIcon);
|
||||
|
||||
this._client = NMClient.Client.new();
|
||||
|
||||
@ -1552,6 +1596,16 @@ const NMApplet = new Lang.Class({
|
||||
this.menu.addMenuItem(this._statusSection);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._activeConnections = [ ];
|
||||
this._connections = [ ];
|
||||
|
||||
this._mainConnection = null;
|
||||
this._vpnConnection = null;
|
||||
this._activeAccessPointUpdateId = 0;
|
||||
this._activeAccessPoint = null;
|
||||
this._mobileUpdateId = 0;
|
||||
this._mobileUpdateDevice = null;
|
||||
|
||||
this._devices = { };
|
||||
|
||||
this._devices.wired = {
|
||||
@ -1587,13 +1641,9 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
this._devices.vpn = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
device: new NMDeviceVPN(this._client),
|
||||
device: this._makeWrapperDevice(NMDeviceVPN, null),
|
||||
item: new NMWiredSectionTitleMenuItem(_("VPN Connections"))
|
||||
};
|
||||
this._devices.vpn.device.connect('active-connection-changed', Lang.bind(this, function() {
|
||||
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
|
||||
}));
|
||||
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
|
||||
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
|
||||
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
|
||||
this._devices.vpn.section.actor.hide();
|
||||
@ -1601,15 +1651,6 @@ const NMApplet = new Lang.Class({
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
|
||||
this._activeConnections = [ ];
|
||||
this._connections = [ ];
|
||||
|
||||
this._mainConnection = null;
|
||||
this._activeAccessPointUpdateId = 0;
|
||||
this._activeAccessPoint = null;
|
||||
this._mobileUpdateId = 0;
|
||||
this._mobileUpdateDevice = null;
|
||||
|
||||
// Device types
|
||||
this._dtypes = { };
|
||||
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
|
||||
@ -1650,9 +1691,25 @@ const NMApplet = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
setIcon: function(iconName) {
|
||||
this._primaryIcon.icon_name = iconName;
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
// FIXME: more design discussion is needed before we can
|
||||
// expose part of this menu
|
||||
|
||||
if (locked)
|
||||
this.menu.close();
|
||||
this.actor.reactive = !locked;
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new NMMessageTraySource();
|
||||
this._source = new MessageTray.Source(_("Network Manager"),
|
||||
'network-transmit-receive',
|
||||
St.IconType.SYMBOLIC);
|
||||
|
||||
this._source.connect('destroy', Lang.bind(this, function() {
|
||||
this._source = null;
|
||||
}));
|
||||
@ -1673,6 +1730,18 @@ const NMApplet = new Lang.Class({
|
||||
},
|
||||
|
||||
_syncSectionTitle: function(category) {
|
||||
if (category == NMConnectionCategory.VPN) {
|
||||
// Special case VPN: it's only one device (and a fake one
|
||||
// actually), and we don't show it if empty
|
||||
let device = this._devices.vpn.device;
|
||||
let section = this._devices.vpn.section;
|
||||
let item = this._devices.vpn.item;
|
||||
|
||||
section.actor.visible = !device.empty;
|
||||
item.updateForDevice(device);
|
||||
return;
|
||||
}
|
||||
|
||||
let devices = this._devices[category].devices;
|
||||
let item = this._devices[category].item;
|
||||
let section = this._devices[category].section;
|
||||
@ -1723,6 +1792,29 @@ const NMApplet = new Lang.Class({
|
||||
this._source.notify(device._notification);
|
||||
},
|
||||
|
||||
_makeWrapperDevice: function(wrapperClass, device) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, 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',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
}));
|
||||
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
|
||||
this._syncSectionTitle(dev.category);
|
||||
}));
|
||||
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
|
||||
wrapper.disconnect(wrapper._activationFailedId);
|
||||
wrapper.disconnect(wrapper._deviceStateChangedId);
|
||||
wrapper.disconnect(wrapper._destroyId);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
_deviceAdded: function(client, device) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
@ -1730,24 +1822,8 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
let wrapperClass = this._dtypes[device.get_device_type()];
|
||||
if (wrapperClass) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
let wrapper = this._makeWrapperDevice(wrapperClass, device);
|
||||
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, 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',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
}));
|
||||
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
|
||||
this._syncSectionTitle(dev.category);
|
||||
}));
|
||||
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
|
||||
wrapper.disconnect(wrapper._activationFailedId);
|
||||
wrapper.disconnect(wrapper._deviceStateChangedId);
|
||||
wrapper.disconnect(wrapper._destroyId);
|
||||
});
|
||||
let section = this._devices[wrapper.category].section;
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
|
||||
@ -1801,6 +1877,8 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
this._activeConnections = newActiveConnections;
|
||||
this._mainConnection = null;
|
||||
this._vpnConnection = null;
|
||||
|
||||
let activating = null;
|
||||
let default_ip4 = null;
|
||||
let default_ip6 = null;
|
||||
@ -1834,17 +1912,17 @@ const NMApplet = new Lang.Class({
|
||||
default_ip4 = a;
|
||||
if (a.default6)
|
||||
default_ip6 = a;
|
||||
|
||||
if (a._type == 'vpn')
|
||||
active_vpn = a;
|
||||
|
||||
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
activating = a;
|
||||
|
||||
if (!a._primaryDevice) {
|
||||
if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) {
|
||||
// find a good device to be considered primary
|
||||
a._primaryDevice = null;
|
||||
let devices = a.get_devices();
|
||||
let devices = a.get_devices() || [];
|
||||
for (let j = 0; j < devices.length; j++) {
|
||||
let d = devices[j];
|
||||
if (d._delegate) {
|
||||
@ -1866,7 +1944,8 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
this._mainConnection = activating || active_vpn || default_ip4 || default_ip6 || this._activeConnections[0] || null;
|
||||
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
|
||||
this._vpnConnection = active_vpn;
|
||||
},
|
||||
|
||||
_notifyActivated: function(activeConnection) {
|
||||
@ -1883,7 +1962,7 @@ const NMApplet = new Lang.Class({
|
||||
let connections = this._settings.list_connections();
|
||||
for (let i = 0; i < connections.length; i++) {
|
||||
let connection = connections[i];
|
||||
if (connection._uuid) {
|
||||
if (connection._updatedId) {
|
||||
// connection was already seen (for example because NetworkManager was restarted)
|
||||
continue;
|
||||
}
|
||||
@ -1896,7 +1975,7 @@ const NMApplet = new Lang.Class({
|
||||
},
|
||||
|
||||
_newConnection: function(settings, connection) {
|
||||
if (connection._uuid) {
|
||||
if (connection._updatedId) {
|
||||
// connection was already seen
|
||||
return;
|
||||
}
|
||||
@ -1919,35 +1998,31 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
if (section == NMConnectionCategory.VPN) {
|
||||
this._devices.vpn.device.removeConnection(connection);
|
||||
if (this._devices.vpn.device.empty)
|
||||
this._devices.vpn.section.actor.hide();
|
||||
this._syncSectionTitle(section);
|
||||
} else if (section != NMConnectionCategory.INVALID) {
|
||||
let devices = this._devices[section].devices;
|
||||
for (let i = 0; i < devices.length; i++)
|
||||
devices[i].removeConnection(connection);
|
||||
}
|
||||
|
||||
connection._uuid = null;
|
||||
connection.disconnect(connection._removedId);
|
||||
connection.disconnect(connection._updatedId);
|
||||
connection._removedId = connection._updatedId = 0;
|
||||
},
|
||||
|
||||
_updateConnection: function(connection) {
|
||||
let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME);
|
||||
connection._type = connectionSettings.type;
|
||||
|
||||
connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID;
|
||||
connection._name = connectionSettings.id;
|
||||
connection._uuid = connectionSettings.uuid;
|
||||
connection._timestamp = connectionSettings.timestamp;
|
||||
|
||||
let section = connection._section;
|
||||
|
||||
if (connection._section == NMConnectionCategory.INVALID)
|
||||
if (section == NMConnectionCategory.INVALID)
|
||||
return;
|
||||
if (section == NMConnectionCategory.VPN) {
|
||||
this._devices.vpn.device.checkConnection(connection);
|
||||
this._devices.vpn.section.actor.show();
|
||||
this._syncSectionTitle(section);
|
||||
} else {
|
||||
let devices = this._devices[section].devices;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
@ -1970,12 +2045,10 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
this._statusSection.actor.hide();
|
||||
|
||||
this._syncSectionTitle('wired');
|
||||
this._syncSectionTitle('wireless');
|
||||
this._syncSectionTitle('wwan');
|
||||
|
||||
if (!this._devices.vpn.device.empty)
|
||||
this._devices.vpn.section.actor.show();
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRED);
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
|
||||
this._syncSectionTitle(NMConnectionCategory.WWAN);
|
||||
this._syncSectionTitle(NMConnectionCategory.VPN);
|
||||
},
|
||||
|
||||
_syncNMState: function() {
|
||||
@ -2018,9 +2091,6 @@ const NMApplet = new Lang.Class({
|
||||
case NMConnectionCategory.WIRED:
|
||||
this.setIcon('network-wired-acquiring');
|
||||
break;
|
||||
case NMConnectionCategory.VPN:
|
||||
this.setIcon('network-vpn-acquiring');
|
||||
break;
|
||||
default:
|
||||
// fallback to a generic connected icon
|
||||
// (it could be a private connection of some other user)
|
||||
@ -2083,9 +2153,6 @@ const NMApplet = new Lang.Class({
|
||||
this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality));
|
||||
hasMobileIcon = true;
|
||||
break;
|
||||
case NMConnectionCategory.VPN:
|
||||
this.setIcon('network-vpn');
|
||||
break;
|
||||
default:
|
||||
// fallback to a generic connected icon
|
||||
// (it could be a private connection of some other user)
|
||||
@ -2094,6 +2161,25 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
// update VPN indicator
|
||||
if (this._vpnConnection) {
|
||||
let vpnIconName = 'network-vpn';
|
||||
if (this._vpnConnection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
vpnIconName = 'network-vpn-acquiring';
|
||||
|
||||
// only show a separate icon when we're using a wireless/3g connection
|
||||
if (mc._section == NMConnectionCategory.WIRELESS ||
|
||||
mc._section == NMConnectionCategory.WWAN) {
|
||||
this._secondaryIcon.icon_name = vpnIconName;
|
||||
this._secondaryIcon.visible = true;
|
||||
} else {
|
||||
this.setIcon(vpnIconName);
|
||||
this._secondaryIcon.visible = false;
|
||||
}
|
||||
} else {
|
||||
this._secondaryIcon.visible = false;
|
||||
}
|
||||
|
||||
// cleanup stale signal connections
|
||||
|
||||
if (!hasApIcon && this._activeAccessPointUpdateId) {
|
||||
@ -2108,18 +2194,3 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const NMMessageTraySource = new Lang.Class({
|
||||
Name: 'NMMessageTraySource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Network Manager"));
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'network-transmit-receive',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE
|
||||
});
|
||||
this._setSummaryIcon(icon);
|
||||
}
|
||||
});
|
||||
|
@ -2,14 +2,10 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
@ -80,6 +76,12 @@ const Indicator = new Lang.Class({
|
||||
this._devicesChanged();
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
if (locked)
|
||||
this.menu.close();
|
||||
this.actor.reactive = !locked;
|
||||
},
|
||||
|
||||
_readPrimaryDevice: function() {
|
||||
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
|
||||
if (error) {
|
||||
@ -212,7 +214,7 @@ const DeviceItem = new Lang.Class({
|
||||
case UPDeviceType.COMPUTER:
|
||||
return _("Computer");
|
||||
default:
|
||||
return _("Unknown");
|
||||
return C_("device", "Unknown");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -2,16 +2,11 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Gvc = imports.gi.Gvc;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
|
||||
|
||||
@ -62,6 +57,10 @@ const Indicator = new Lang.Class({
|
||||
this._control.open();
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this.menu.setSettingsVisibility(!locked);
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let currentVolume = this._output.volume;
|
||||
@ -149,13 +148,9 @@ const Indicator = new Lang.Class({
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showInput) {
|
||||
this._inputTitle.actor.show();
|
||||
this._inputSlider.actor.show();
|
||||
} else {
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
|
||||
this._inputTitle.actor.visible = showInput;
|
||||
this._inputSlider.actor.visible = showInput;
|
||||
},
|
||||
|
||||
_volumeToIcon: function(volume) {
|
||||
|
@ -1,63 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'bluetooth-applet': 'bluetooth',
|
||||
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
|
||||
// when moved to control center
|
||||
'gnome-sound-applet': 'volume',
|
||||
'nm-applet': 'network',
|
||||
'gnome-power-manager': 'battery',
|
||||
'keyboard': 'keyboard',
|
||||
'a11y-keyboard': 'a11y',
|
||||
'kbd-scrolllock': 'keyboard',
|
||||
'kbd-numlock': 'keyboard',
|
||||
'kbd-capslock': 'keyboard',
|
||||
'ibus-ui-gtk': 'input-method'
|
||||
};
|
||||
|
||||
const StatusIconDispatcher = new Lang.Class({
|
||||
Name: 'StatusIconDispatcher',
|
||||
|
||||
_init: function() {
|
||||
this._traymanager = new Shell.TrayManager();
|
||||
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._traymanager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
// Yet-another-Ubuntu-workaround - we have to kill their
|
||||
// app-indicators, so that applications fall back to normal
|
||||
// status icons
|
||||
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
|
||||
Util.killall('indicator-application-service');
|
||||
},
|
||||
|
||||
start: function(themeWidget) {
|
||||
this._traymanager.manage_stage(global.stage, themeWidget);
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let wmClass = (icon.wm_class || 'unknown').toLowerCase();
|
||||
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
|
||||
if (role)
|
||||
this.emit('status-icon-added', icon, role);
|
||||
else
|
||||
this.emit('message-icon-added', icon);
|
||||
},
|
||||
|
||||
_onTrayIconRemoved: function(o, icon) {
|
||||
let wmClass = (icon.wm_class || 'unknown').toLowerCase();
|
||||
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
|
||||
if (role)
|
||||
this.emit('status-icon-removed', icon);
|
||||
else
|
||||
this.emit('message-icon-removed', icon);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(StatusIconDispatcher.prototype);
|
@ -132,6 +132,9 @@ const Client = new Lang.Class({
|
||||
let channel = channels[i];
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
if (Shell.is_channel_invalidated(channel))
|
||||
continue;
|
||||
|
||||
/* Only observe contact text channels */
|
||||
if ((!(channel instanceof Tp.TextChannel)) ||
|
||||
targetHandleType != Tp.HandleType.CONTACT)
|
||||
@ -181,6 +184,9 @@ const Client = new Lang.Class({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Shell.is_channel_invalidated(channel))
|
||||
continue;
|
||||
|
||||
// 'notify' will be true when coming from an actual HandleChannels
|
||||
// call, and not when from a successful Claim call. The point is
|
||||
// we don't want to notify for a channel we just claimed which
|
||||
@ -231,12 +237,19 @@ const Client = new Lang.Class({
|
||||
let channel = channels[0];
|
||||
let chanType = channel.get_channel_type();
|
||||
|
||||
if (Shell.is_channel_invalidated(channel)) {
|
||||
Shell.decline_dispatch_op(context, 'Channel is invalidated');
|
||||
return;
|
||||
}
|
||||
|
||||
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
|
||||
this._approveTextChannel(account, conn, channel, dispatchOp, context);
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
|
||||
this._approveCall(account, conn, channel, dispatchOp, context);
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
||||
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
||||
else
|
||||
Shell.decline_dispatch_op(context, 'Unsupported channel type');
|
||||
},
|
||||
|
||||
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
||||
@ -365,8 +378,9 @@ const Client = new Lang.Class({
|
||||
|
||||
_ensureSubscriptionSource: function() {
|
||||
if (this._subscriptionSource == null) {
|
||||
this._subscriptionSource = new MultiNotificationSource(
|
||||
_("Subscription request"), 'gtk-dialog-question');
|
||||
this._subscriptionSource = new MessageTray.Source(_("Subscription request"),
|
||||
'gtk-dialog-question',
|
||||
St.IconType.FULLCOLOR);
|
||||
Main.messageTray.add(this._subscriptionSource);
|
||||
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._subscriptionSource = null;
|
||||
@ -401,8 +415,9 @@ const Client = new Lang.Class({
|
||||
|
||||
_ensureAccountSource: function() {
|
||||
if (this._accountSource == null) {
|
||||
this._accountSource = new MultiNotificationSource(
|
||||
_("Connection error"), 'gtk-dialog-error');
|
||||
this._accountSource = new MessageTray.Source(_("Connection error"),
|
||||
'gtk-dialog-error',
|
||||
St.IconType.FULLCOLOR);
|
||||
Main.messageTray.add(this._accountSource);
|
||||
this._accountSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._accountSource = null;
|
||||
@ -418,14 +433,13 @@ const ChatSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(account, conn, channel, contact, client) {
|
||||
this.parent(contact.get_alias());
|
||||
|
||||
this.isChat = true;
|
||||
|
||||
this._account = account;
|
||||
this._contact = contact;
|
||||
this._client = client;
|
||||
|
||||
this.parent(contact.get_alias());
|
||||
|
||||
this.isChat = true;
|
||||
this._pendingMessages = [];
|
||||
|
||||
this._conn = conn;
|
||||
@ -446,8 +460,6 @@ const ChatSource = new Lang.Class({
|
||||
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
|
||||
this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
|
||||
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
|
||||
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
|
||||
@ -470,9 +482,9 @@ const ChatSource = new Lang.Class({
|
||||
this._notification.appendAliasChange(oldAlias, newAlias);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
createIcon: function(size) {
|
||||
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||
this._iconBox._size = this.ICON_SIZE;
|
||||
this._iconBox._size = size;
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let file = this._contact.get_avatar_file();
|
||||
|
||||
@ -488,9 +500,40 @@ const ChatSource = new Lang.Class({
|
||||
return this._iconBox;
|
||||
},
|
||||
|
||||
createSecondaryIcon: function() {
|
||||
let iconBox = new St.Bin();
|
||||
iconBox.child = new St.Icon({ style_class: 'secondary-icon',
|
||||
icon_type: St.IconType.FULLCOLOR });
|
||||
let presenceType = this._contact.get_presence_type();
|
||||
|
||||
switch (presenceType) {
|
||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||
iconBox.child.icon_name = 'user-available';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.BUSY:
|
||||
iconBox.child.icon_name = 'user-busy';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.OFFLINE:
|
||||
iconBox.child.icon_name = 'user-offline';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.HIDDEN:
|
||||
iconBox.child.icon_name = 'user-invisible';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.AWAY:
|
||||
iconBox.child.icon_name = 'user-away';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||
iconBox.child.icon_name = 'user-idle';
|
||||
break;
|
||||
default:
|
||||
iconBox.child.icon_name = 'user-offline';
|
||||
}
|
||||
return iconBox;
|
||||
},
|
||||
|
||||
_updateAvatarIcon: function() {
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
|
||||
this._setSummaryIcon(this.createIcon(this.ICON_SIZE));
|
||||
this._notification.update(this._notification.title, null, { customContent: true });
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
@ -510,10 +553,10 @@ const ChatSource = new Lang.Class({
|
||||
_getLogMessages: function() {
|
||||
let logManager = Tpl.LogManager.dup_singleton();
|
||||
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
|
||||
Shell.get_contact_events(logManager,
|
||||
this._account, entity,
|
||||
SCROLLBACK_HISTORY_LINES,
|
||||
Lang.bind(this, this._displayPendingMessages));
|
||||
|
||||
logManager.get_filtered_events_async(this._account, entity,
|
||||
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
|
||||
null, Lang.bind(this, this._displayPendingMessages));
|
||||
},
|
||||
|
||||
_displayPendingMessages: function(logManager, result) {
|
||||
@ -535,7 +578,7 @@ const ChatSource = new Lang.Class({
|
||||
this._pendingMessages.push(message);
|
||||
}
|
||||
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
|
||||
let showTimestamp = false;
|
||||
|
||||
@ -581,8 +624,17 @@ const ChatSource = new Lang.Class({
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_updateCount: function() {
|
||||
this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0);
|
||||
/* All messages are new messages for Telepathy sources */
|
||||
get count() {
|
||||
return this._pendingMessages.length;
|
||||
},
|
||||
|
||||
get unseenCount() {
|
||||
return this.count;
|
||||
},
|
||||
|
||||
get countVisible() {
|
||||
return this.count > 0;
|
||||
},
|
||||
|
||||
_messageReceived: function(channel, message) {
|
||||
@ -590,7 +642,7 @@ const ChatSource = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._pendingMessages.push(message);
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
|
||||
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
||||
this._notification.appendMessage(message);
|
||||
@ -652,38 +704,14 @@ const ChatSource = new Lang.Class({
|
||||
},
|
||||
|
||||
_presenceChanged: function (contact, presence, status, message) {
|
||||
let msg, shouldNotify, title;
|
||||
|
||||
if (this._presence == presence)
|
||||
return;
|
||||
let msg, title;
|
||||
|
||||
title = GLib.markup_escape_text(this.title, -1);
|
||||
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE) {
|
||||
msg = _("%s is online.").format(title);
|
||||
shouldNotify = (this._presence == Tp.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Tp.ConnectionPresenceType.OFFLINE) {
|
||||
presence = Tp.ConnectionPresenceType.OFFLINE;
|
||||
msg = _("%s is offline.").format(title);
|
||||
shouldNotify = (this._presence != Tp.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Tp.ConnectionPresenceType.AWAY ||
|
||||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
|
||||
msg = _("%s is away.").format(title);
|
||||
shouldNotify = false;
|
||||
} else if (presence == Tp.ConnectionPresenceType.BUSY) {
|
||||
msg = _("%s is busy.").format(title);
|
||||
shouldNotify = false;
|
||||
} else
|
||||
return;
|
||||
|
||||
this._presence = presence;
|
||||
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() });
|
||||
|
||||
if (message)
|
||||
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
||||
|
||||
this._notification.appendPresence(msg, shouldNotify);
|
||||
if (shouldNotify)
|
||||
this.notify();
|
||||
},
|
||||
|
||||
_pendingRemoved: function(channel, message) {
|
||||
@ -691,7 +719,7 @@ const ChatSource = new Lang.Class({
|
||||
|
||||
if (idx >= 0) {
|
||||
this._pendingMessages.splice(idx, 1);
|
||||
this._updateCount();
|
||||
this.countUpdated();
|
||||
}
|
||||
else
|
||||
throw new Error('Message not in our pending list: ' + message);
|
||||
@ -710,7 +738,7 @@ const ChatNotification = new Lang.Class({
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
this.parent(source, source.title, null, { customContent: true });
|
||||
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() });
|
||||
this.setResident(true);
|
||||
|
||||
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
||||
@ -803,7 +831,7 @@ const ChatNotification = new Lang.Class({
|
||||
let groups = this._contentArea.get_children();
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let group = groups[i];
|
||||
if (group.get_children().length == 0)
|
||||
if (group.get_n_children() == 0)
|
||||
group.destroy();
|
||||
}
|
||||
},
|
||||
@ -853,6 +881,8 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
this._lastGroupActor.add(body, props.childProps);
|
||||
|
||||
this.updated();
|
||||
|
||||
let timestamp = props.timestamp;
|
||||
this._history.unshift({ actor: body, time: timestamp,
|
||||
realMessage: group != 'meta' });
|
||||
@ -918,19 +948,6 @@ const ChatNotification = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
appendPresence: function(text, asTitle) {
|
||||
if (asTitle)
|
||||
this.update(text, null, { customContent: true, titleMarkup: true });
|
||||
else
|
||||
this.update(this.source.title, null, { customContent: true });
|
||||
|
||||
let label = this._append({ body: text,
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'] });
|
||||
|
||||
this._filterMessages();
|
||||
},
|
||||
|
||||
appendAliasChange: function(oldAlias, newAlias) {
|
||||
oldAlias = GLib.markup_escape_text(oldAlias, -1);
|
||||
newAlias = GLib.markup_escape_text(newAlias, -1);
|
||||
@ -1000,10 +1017,9 @@ const ApproverSource = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(dispatchOp, text, gicon) {
|
||||
this.parent(text);
|
||||
|
||||
this._gicon = gicon;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this.parent(text);
|
||||
|
||||
this._dispatchOp = dispatchOp;
|
||||
|
||||
@ -1024,10 +1040,9 @@ const ApproverSource = new Lang.Class({
|
||||
this.parent();
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
createIcon: function(size) {
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
icon_size: size });
|
||||
}
|
||||
});
|
||||
|
||||
@ -1149,40 +1164,6 @@ const FileTransferNotification = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
// A notification source that can embed multiple notifications
|
||||
const MultiNotificationSource = new Lang.Class({
|
||||
Name: 'MultiNotificationSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(title, icon) {
|
||||
this.parent(title);
|
||||
|
||||
this._icon = icon;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this._nbNotifications = 0;
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
this.parent(notification);
|
||||
|
||||
this._nbNotifications += 1;
|
||||
|
||||
// Display the source while there is at least one notification
|
||||
notification.connect('destroy', Lang.bind(this, function () {
|
||||
this._nbNotifications -= 1;
|
||||
|
||||
if (this._nbNotifications == 0)
|
||||
this.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string(this._icon),
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
});
|
||||
|
||||
// Subscription request
|
||||
const SubscriptionRequestNotification = new Lang.Class({
|
||||
Name: 'SubscriptionRequestNotification',
|
||||
|
227
js/ui/unlockDialog.js
Normal file
@ -0,0 +1,227 @@
|
||||
// -*- 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 Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
|
||||
// A widget showing the user avatar and name
|
||||
const UserWidget = new Lang.Class({
|
||||
Name: 'UserWidget',
|
||||
|
||||
_init: function(user) {
|
||||
this._user = user;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
|
||||
vertical: false });
|
||||
|
||||
this._avatar = new UserMenu.UserAvatarWidget(user);
|
||||
this.actor.add(this._avatar.actor,
|
||||
{ x_fill: true, y_fill: true });
|
||||
|
||||
this._label = new St.Label({ style_class: 'login-dialog-username' });
|
||||
this.actor.add(this._label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateUser));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._updateUser));
|
||||
if (this._user.is_loaded)
|
||||
this._updateUser();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._userLoadedId != 0) {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._userLoadedId = 0;
|
||||
}
|
||||
|
||||
if (this._userChangedId != 0) {
|
||||
this._user.disconnect(this._userChangedId);
|
||||
this._userChangedId = 0;
|
||||
}
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_updateUser: function() {
|
||||
if (this._user.is_loaded)
|
||||
this._label.text = this._user.get_real_name();
|
||||
else
|
||||
this._label.text = '';
|
||||
|
||||
this._avatar.update();
|
||||
}
|
||||
});
|
||||
|
||||
const UnlockDialog = new Lang.Class({
|
||||
Name: 'UnlockDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
parentActor: parentActor
|
||||
});
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._userName = GLib.get_user_name();
|
||||
this._user = this._userManager.get_user(this._userName);
|
||||
|
||||
this._greeterClient = new Gdm.Client();
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
|
||||
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
|
||||
this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt));
|
||||
this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt));
|
||||
|
||||
this._userWidget = new UserWidget(this._user);
|
||||
this.contentLayout.add_actor(this._userWidget.actor);
|
||||
|
||||
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||
vertical: true });
|
||||
|
||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||
this._promptLayout.add(this._promptLabel,
|
||||
{ x_align: St.Align.START });
|
||||
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
ShellEntry.addContextMenu(this._promptEntry);
|
||||
this.setInitialKeyFocus(this._promptEntry);
|
||||
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
|
||||
|
||||
this._promptLayout.add(this._promptEntry,
|
||||
{ expand: true,
|
||||
x_fill: true });
|
||||
|
||||
this.contentLayout.add_actor(this._promptLayout);
|
||||
|
||||
// Translators: this message is shown below the password entry field
|
||||
// to indicate the user can swipe their finger instead
|
||||
this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"),
|
||||
style_class: 'login-dialog-prompt-fingerprint-message' });
|
||||
this._promptFingerprintMessage.hide();
|
||||
this.contentLayout.add_actor(this._promptFingerprintMessage);
|
||||
|
||||
this._okButton = { label: _("Unlock"),
|
||||
action: Lang.bind(this, this._doUnlock),
|
||||
default: true };
|
||||
this.setButtons([this._okButton]);
|
||||
this.setActionKey(Clutter.KEY_Escape, Lang.bind(this, this._escape));
|
||||
|
||||
this._updateOkButton(false);
|
||||
this._reset();
|
||||
|
||||
let otherUserLabel = new St.Label({ text: _("Login as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
can_focus: true,
|
||||
child: otherUserLabel,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
this.emit('loaded');
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
_updateOkButton: function(sensitive) {
|
||||
this._okButton.button.reactive = sensitive;
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._userVerifier.begin(this._userName, new Batch.Hold());
|
||||
},
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
this._promptLabel.text = question;
|
||||
|
||||
this._promptEntry.text = '';
|
||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
||||
this._promptEntry.menu.isPassword = passwordChar != '';
|
||||
|
||||
this._currentQuery = serviceName;
|
||||
this._updateOkButton(true);
|
||||
},
|
||||
|
||||
_showFingerprintPrompt: function() {
|
||||
GdmUtil.fadeInActor(this._promptFingerprintMessage);
|
||||
},
|
||||
|
||||
_hideFingerprintPrompt: function() {
|
||||
GdmUtil.fadeOutActor(this._promptFingerprintMessage);
|
||||
},
|
||||
|
||||
_doUnlock: function() {
|
||||
if (!this._currentQuery)
|
||||
return;
|
||||
|
||||
let query = this._currentQuery;
|
||||
this._currentQuery = null;
|
||||
|
||||
this._updateOkButton(false);
|
||||
|
||||
this._userVerifier.answerQuery(query, this._promptEntry.text);
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this._userVerifier.clear();
|
||||
this.emit('unlocked');
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._userVerifier.cancel();
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
_escape: function() {
|
||||
this._onVerificationFailed();
|
||||
},
|
||||
|
||||
_otherUserClicked: function(button, event) {
|
||||
this._userManager.goto_login_session();
|
||||
|
||||
this._userVerifier.cancel();
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._userVerifier.clear();
|
||||
this.parent();
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._userVerifier.cancel(null);
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
});
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
@ -15,7 +16,6 @@ const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
@ -40,6 +40,34 @@ const IMStatus = {
|
||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
// Copyright (C) 2008,2009 Red Hat, Inc.
|
||||
|
||||
const UserAvatarWidget = new Lang.Class({
|
||||
Name: 'UserAvatarWidget',
|
||||
|
||||
_init: function(user) {
|
||||
this._user = user;
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'status-chooser-user-icon',
|
||||
track_hover: true,
|
||||
reactive: true });
|
||||
},
|
||||
|
||||
update: function() {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
iconFile = null;
|
||||
|
||||
if (iconFile) {
|
||||
let file = Gio.File.new_for_path(iconFile);
|
||||
this.actor.child = null;
|
||||
this.actor.style = 'background-image: url("%s");'.format(iconFile);
|
||||
} else {
|
||||
this.actor.style = null;
|
||||
this.actor.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: DIALOG_ICON_SIZE });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const IMStatusItem = new Lang.Class({
|
||||
Name: 'IMStatusItem',
|
||||
@ -107,7 +135,11 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
this.parent({ reactive: false,
|
||||
style_class: 'status-chooser' });
|
||||
|
||||
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
|
||||
this._avatar = new UserAvatarWidget(this._user);
|
||||
this._iconBin = new St.Button({ child: this._avatar.actor });
|
||||
this.addActor(this._iconBin);
|
||||
|
||||
this._iconBin.connect('clicked', Lang.bind(this,
|
||||
@ -132,7 +164,7 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
item = new IMStatusItem(_("Busy"), 'user-busy');
|
||||
this._combo.addMenuItem(item, IMStatus.BUSY);
|
||||
|
||||
item = new IMStatusItem(_("Hidden"), 'user-invisible');
|
||||
item = new IMStatusItem(_("Invisible"), 'user-invisible');
|
||||
this._combo.addMenuItem(item, IMStatus.HIDDEN);
|
||||
|
||||
item = new IMStatusItem(_("Away"), 'user-away');
|
||||
@ -185,10 +217,6 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
}
|
||||
}));
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
Lang.bind(this,
|
||||
this._updateUser));
|
||||
@ -226,44 +254,12 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateUser: function() {
|
||||
let iconFile = null;
|
||||
if (this._user.is_loaded) {
|
||||
if (this._user.is_loaded)
|
||||
this._name.label.set_text(this._user.get_real_name());
|
||||
iconFile = this._user.get_icon_file();
|
||||
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
iconFile = null;
|
||||
} else {
|
||||
this._name.label.set_text("");
|
||||
}
|
||||
|
||||
if (iconFile)
|
||||
this._setIconFromFile(iconFile);
|
||||
else
|
||||
this._setIconFromName('avatar-default');
|
||||
},
|
||||
this._name.label.set_text("");
|
||||
|
||||
_setIconFromFile: function(iconFile) {
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
||||
'background-size: contain;');
|
||||
this._iconBin.child = null;
|
||||
},
|
||||
|
||||
_setIconFromName: function(iconName) {
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
if (iconName != null) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
||||
iconName,
|
||||
St.IconType.SYMBOLIC,
|
||||
DIALOG_ICON_SIZE);
|
||||
|
||||
this._iconBin.child = icon;
|
||||
this._iconBin.show();
|
||||
} else {
|
||||
this._iconBin.child = null;
|
||||
this._iconBin.hide();
|
||||
}
|
||||
this._avatar.update();
|
||||
},
|
||||
|
||||
_statusForPresence: function(presence) {
|
||||
@ -454,7 +450,6 @@ const UserMenuButton = new Lang.Class({
|
||||
this._accountMgr = Tp.AccountManager.dup();
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
@ -473,13 +468,20 @@ const UserMenuButton = new Lang.Class({
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending',
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
this._accountMgr.connect('most-available-presence-changed',
|
||||
Lang.bind(this, this._updatePresenceIcon));
|
||||
this._accountMgr.connect('account-enabled',
|
||||
Lang.bind(this, this._onAccountEnabled));
|
||||
this._accountMgr.connect('account-removed',
|
||||
Lang.bind(this, this._onAccountRemoved));
|
||||
this._accountMgr.prepare_async(null, Lang.bind(this,
|
||||
function(mgr) {
|
||||
let [presence, s, msg] = mgr.get_most_available_presence();
|
||||
this._updatePresenceIcon(mgr, presence, s, msg);
|
||||
this._setupAccounts();
|
||||
}));
|
||||
|
||||
this._name = new St.Label();
|
||||
@ -497,13 +499,13 @@ const UserMenuButton = new Lang.Class({
|
||||
}));
|
||||
|
||||
this._userManager.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('notify::has-multiple-users',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('user-added',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._userManager.connect('user-removed',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
Lang.bind(this, this._updateMultiUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
@ -515,6 +517,10 @@ const UserMenuButton = new Lang.Class({
|
||||
this._updateLogout();
|
||||
this._updateLockScreen();
|
||||
|
||||
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
|
||||
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
|
||||
|
||||
// Whether shutdown is available or not depends on both lockdown
|
||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||
// notify, so we update the menu item each time the menu opens or
|
||||
@ -530,6 +536,12 @@ const UserMenuButton = new Lang.Class({
|
||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
if (locked)
|
||||
this.menu.close();
|
||||
this.actor.reactive = !locked;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._user.disconnect(this._userChangedId);
|
||||
@ -542,37 +554,45 @@ const UserMenuButton = new Lang.Class({
|
||||
this._name.set_text("");
|
||||
},
|
||||
|
||||
_updateMultiUser: function() {
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
},
|
||||
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
if (allowSwitch &&
|
||||
this._userManager.can_switch() &&
|
||||
this._userManager.has_multiple_users)
|
||||
this._loginScreenItem.actor.show();
|
||||
else
|
||||
this._loginScreenItem.actor.hide();
|
||||
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
|
||||
let multiSession = Gdm.get_session_ids().length > 1;
|
||||
|
||||
this._loginScreenItem.label.set_text(multiUser ? _("Switch User")
|
||||
: _("Switch Session"));
|
||||
this._loginScreenItem.actor.visible = allowSwitch && (multiUser || multiSession);
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
if (allowLogout)
|
||||
this._logoutItem.actor.show();
|
||||
else
|
||||
this._logoutItem.actor.hide();
|
||||
let multiUser = this._userManager.has_multiple_users;
|
||||
let multiSession = Gdm.get_session_ids().length > 1;
|
||||
|
||||
this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession);
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
if (allowLockScreen)
|
||||
this._lockScreenItem.actor.show();
|
||||
else
|
||||
this._lockScreenItem.actor.hide();
|
||||
this._lockScreenItem.actor.visible = allowLockScreen;
|
||||
},
|
||||
|
||||
_updateInstallUpdates: function() {
|
||||
let haveUpdates = this._updatesFile.query_exists(null);
|
||||
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._session.CanShutdownRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._haveShutdown = result;
|
||||
this._haveShutdown = result[0];
|
||||
this._updateInstallUpdates();
|
||||
this._updateSuspendOrPowerOff();
|
||||
}
|
||||
}));
|
||||
@ -584,19 +604,16 @@ const UserMenuButton = new Lang.Class({
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
if (!this._haveShutdown && !this._haveSuspend)
|
||||
this._suspendOrPowerOffItem.actor.hide();
|
||||
else
|
||||
this._suspendOrPowerOffItem.actor.show();
|
||||
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
|
||||
|
||||
// If we can't suspend show Power Off... instead
|
||||
// If we can't power off show Suspend instead
|
||||
// and disable the alt key
|
||||
if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
||||
} else if (!this._haveShutdown) {
|
||||
if (!this._haveShutdown) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
|
||||
} else if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
|
||||
} else {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
|
||||
}
|
||||
},
|
||||
|
||||
@ -620,11 +637,54 @@ const UserMenuButton = new Lang.Class({
|
||||
this._iconBox.child = this._offlineIcon;
|
||||
},
|
||||
|
||||
_setupAccounts: function() {
|
||||
let accounts = this._accountMgr.get_valid_accounts();
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
|
||||
Lang.bind(this, this._updateChangingPresence));
|
||||
}
|
||||
this._updateChangingPresence();
|
||||
},
|
||||
|
||||
_onAccountEnabled: function(accountMgr, account) {
|
||||
if (!account._changingId)
|
||||
account._changingId = account.connect('notify::connection-status',
|
||||
Lang.bind(this, this._updateChangingPresence));
|
||||
this._updateChangingPresence();
|
||||
},
|
||||
|
||||
_onAccountRemoved: function(accountMgr, account) {
|
||||
if (account._changingId) {
|
||||
account.disconnect(account._changingId);
|
||||
account._changingId = 0;
|
||||
}
|
||||
this._updateChangingPresence();
|
||||
},
|
||||
|
||||
_updateChangingPresence: function() {
|
||||
let accounts = this._accountMgr.get_valid_accounts();
|
||||
let changing = false;
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
|
||||
changing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changing) {
|
||||
this._iconBox.child = this._pendingIcon;
|
||||
} else {
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
|
||||
}
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new IMStatusChooserItem();
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
if (Main.sessionMode.allowSettings)
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._statusChooser = item;
|
||||
|
||||
@ -636,41 +696,44 @@ const UserMenuButton = new Lang.Class({
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
|
||||
item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
if (Main.sessionMode.allowSettings) {
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
}
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
|
||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._lockScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._loginScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Lock"));
|
||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._lockScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
||||
_("Power Off..."));
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
|
||||
_("Suspend"));
|
||||
this.menu.addMenuItem(item);
|
||||
this._suspendOrPowerOffItem = item;
|
||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||
this._suspendOrPowerOffItem = item;
|
||||
this._updateSuspendOrPowerOff();
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
|
||||
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._installUpdatesItem = item;
|
||||
},
|
||||
|
||||
_updatePresenceStatus: function(item, event) {
|
||||
@ -698,12 +761,6 @@ const UserMenuButton = new Lang.Class({
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_onOnlineAccountsActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
|
||||
app.activate(-1);
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
|
||||
@ -712,16 +769,13 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._screenSaverProxy.LockRemote();
|
||||
Main.screenShield.lock(true);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
// Ensure we only move to GDM after the screensaver has activated; in some
|
||||
// OS configurations, the X server may block event processing on VT switch
|
||||
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||
this._userManager.goto_login_session();
|
||||
}));
|
||||
Main.screenShield.lock(false);
|
||||
this._userManager.goto_login_session();
|
||||
},
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
@ -729,17 +783,27 @@ const UserMenuButton = new Lang.Class({
|
||||
this._session.LogoutRemote(0);
|
||||
},
|
||||
|
||||
_onInstallUpdatesActivate: function() {
|
||||
Main.overview.hide();
|
||||
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
|
||||
|
||||
this._session.RebootRemote();
|
||||
},
|
||||
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._haveSuspend &&
|
||||
if (this._haveShutdown &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
// Ensure we only suspend after locking the screen
|
||||
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
|
||||
this._session.ShutdownRemote();
|
||||
} else {
|
||||
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||
Main.screenShield.disconnect(tmpId);
|
||||
|
||||
this._upClient.suspend_sync(null);
|
||||
}));
|
||||
} else {
|
||||
this._session.ShutdownRemote();
|
||||
|
||||
Main.screenShield.lock(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -168,34 +168,35 @@ const WandaSearchProvider = new Lang.Class({
|
||||
this.parent(_("Your favorite Easter Egg"));
|
||||
},
|
||||
|
||||
getResultMetas: function(fish) {
|
||||
return [{ '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) {
|
||||
// for DND only (maybe could be improved)
|
||||
// DON'T use St.Icon here, it crashes the shell
|
||||
// (dnd.js code assumes it can query the actor size
|
||||
// without parenting it, while StWidget accesses
|
||||
// StThemeNode in get_preferred_width/height, which
|
||||
// triggers an assertion failure)
|
||||
return St.TextureCache.get_default().load_icon_name(null,
|
||||
'face-smile',
|
||||
St.IconType.FULLCOLOR,
|
||||
iconSize);
|
||||
}
|
||||
}];
|
||||
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) {
|
||||
// for DND only (maybe could be improved)
|
||||
// DON'T use St.Icon here, it crashes the shell
|
||||
// (dnd.js code assumes it can query the actor size
|
||||
// without parenting it, while StWidget accesses
|
||||
// StThemeNode in get_preferred_width/height, which
|
||||
// triggers an assertion failure)
|
||||
return St.TextureCache.get_default().load_icon_name(null,
|
||||
'face-smile',
|
||||
St.IconType.FULLCOLOR,
|
||||
iconSize);
|
||||
}
|
||||
}]);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
if (terms.join(' ') == MAGIC_FISH_KEY) {
|
||||
return [ FISH_NAME ];
|
||||
this.searchSystem.pushResults(this, [ FISH_NAME ]);
|
||||
} else {
|
||||
this.searchSystem.pushResults(this, []);
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this.getInitialResultSet(terms);
|
||||
this.getInitialResultSet(terms);
|
||||
},
|
||||
|
||||
activateResult: function(fish, params) {
|
||||
|
@ -53,10 +53,10 @@ const Source = new Lang.Class({
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(app, window) {
|
||||
this.parent(app.get_name());
|
||||
this._window = window;
|
||||
this._app = app;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this.parent(app.get_name());
|
||||
|
||||
this.signalIDs = [];
|
||||
this.signalIDs.push(this._window.connect('notify::demands-attention', Lang.bind(this, function() { this.destroy(); })));
|
||||
@ -73,8 +73,8 @@ const Source = new Lang.Class({
|
||||
this.signalIDs = [];
|
||||
},
|
||||
|
||||
createNotificationIcon : function() {
|
||||
return this._app.create_icon_texture(this.ICON_SIZE);
|
||||
createIcon : function(size) {
|
||||
return this._app.create_icon_texture(size);
|
||||
},
|
||||
|
||||
open : function(notification) {
|
||||
|
@ -13,66 +13,40 @@ const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
const WINDOW_ANIMATION_TIME = 0.25;
|
||||
const DIM_DESATURATION = 0.6;
|
||||
const DIM_BRIGHTNESS = -0.1;
|
||||
const DIM_TIME = 0.500;
|
||||
const UNDIM_TIME = 0.250;
|
||||
|
||||
var dimShader = undefined;
|
||||
|
||||
function getDimShaderSource() {
|
||||
if (!dimShader)
|
||||
dimShader = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
|
||||
return dimShader;
|
||||
}
|
||||
|
||||
function getTopInvisibleBorder(metaWindow) {
|
||||
let outerRect = metaWindow.get_outer_rect();
|
||||
let inputRect = metaWindow.get_input_rect();
|
||||
return outerRect.y - inputRect.y;
|
||||
}
|
||||
|
||||
const WindowDimmer = new Lang.Class({
|
||||
Name: 'WindowDimmer',
|
||||
|
||||
_init: function(actor) {
|
||||
if (Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL)) {
|
||||
this._effect = new Clutter.ShaderEffect({ shader_type: Clutter.ShaderType.FRAGMENT_SHADER });
|
||||
this._effect.set_shader_source(getDimShaderSource());
|
||||
} else {
|
||||
this._effect = null;
|
||||
}
|
||||
|
||||
this._desaturateEffect = new Clutter.DesaturateEffect();
|
||||
this._brightnessEffect = new Clutter.BrightnessContrastEffect();
|
||||
actor.add_effect(this._desaturateEffect);
|
||||
actor.add_effect(this._brightnessEffect);
|
||||
this.actor = actor;
|
||||
this._dimFactor = 0.0;
|
||||
},
|
||||
|
||||
set dimFraction(fraction) {
|
||||
this._dimFraction = fraction;
|
||||
|
||||
if (this._effect == null)
|
||||
return;
|
||||
|
||||
if (!Meta.prefs_get_attach_modal_dialogs()) {
|
||||
this._effect.enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fraction > 0.01) {
|
||||
Shell.shader_effect_set_double_uniform(this._effect, 'height', this.actor.get_height());
|
||||
Shell.shader_effect_set_double_uniform(this._effect, 'fraction', fraction);
|
||||
|
||||
if (!this._effect.actor)
|
||||
this.actor.add_effect(this._effect);
|
||||
} else {
|
||||
if (this._effect.actor)
|
||||
this.actor.remove_effect(this._effect);
|
||||
}
|
||||
setEnabled: function(enabled) {
|
||||
this._desaturateEffect.enabled = enabled;
|
||||
this._brightnessEffect.enabled = enabled;
|
||||
},
|
||||
|
||||
get dimFraction() {
|
||||
return this._dimFraction;
|
||||
set dimFactor(factor) {
|
||||
this._dimFactor = factor;
|
||||
this._desaturateEffect.set_factor(factor * DIM_DESATURATION);
|
||||
this._brightnessEffect.set_brightness(factor * DIM_BRIGHTNESS);
|
||||
},
|
||||
|
||||
_dimFraction: 0.0
|
||||
get dimFactor() {
|
||||
return this._dimFactor;
|
||||
}
|
||||
});
|
||||
|
||||
function getWindowDimmer(actor) {
|
||||
@ -93,6 +67,7 @@ const WindowManager = new Lang.Class({
|
||||
this._unmaximizing = [];
|
||||
this._mapping = [];
|
||||
this._destroying = [];
|
||||
this._movingWindow = null;
|
||||
|
||||
this._dimmedWindows = [];
|
||||
|
||||
@ -124,6 +99,14 @@ const WindowManager = new Lang.Class({
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
Meta.keybindings_set_custom_handler('switch-to-workspace-down',
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
Meta.keybindings_set_custom_handler('move-to-workspace-left',
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
Meta.keybindings_set_custom_handler('move-to-workspace-right',
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
Meta.keybindings_set_custom_handler('move-to-workspace-up',
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
Meta.keybindings_set_custom_handler('move-to-workspace-down',
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
Meta.keybindings_set_custom_handler('switch-windows',
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
Meta.keybindings_set_custom_handler('switch-group',
|
||||
@ -134,14 +117,18 @@ const WindowManager = new Lang.Class({
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
Meta.keybindings_set_custom_handler('switch-panels',
|
||||
Lang.bind(this, this._startA11ySwitcher));
|
||||
global.display.add_keybinding('open-application-menu',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Lang.bind(this, this._openAppMenu));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
this._undimWindow(this._dimmedWindows[i], true);
|
||||
this._undimWindow(this._dimmedWindows[i]);
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
this._dimWindow(this._dimmedWindows[i], true);
|
||||
this._dimWindow(this._dimmedWindows[i]);
|
||||
}));
|
||||
},
|
||||
|
||||
@ -153,12 +140,14 @@ const WindowManager = new Lang.Class({
|
||||
this._animationBlockCount = Math.max(0, this._animationBlockCount - 1);
|
||||
},
|
||||
|
||||
_shouldAnimate : function(actor) {
|
||||
if (Main.overview.visible || this._animationBlockCount > 0)
|
||||
_shouldAnimate: function() {
|
||||
return !(Main.overview.visible || this._animationBlockCount > 0);
|
||||
},
|
||||
|
||||
_shouldAnimateActor: function(actor) {
|
||||
if (!this._shouldAnimate())
|
||||
return false;
|
||||
if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
|
||||
return false;
|
||||
return true;
|
||||
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
|
||||
},
|
||||
|
||||
_removeEffect : function(list, actor) {
|
||||
@ -171,7 +160,7 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_minimizeWindow : function(shellwm, actor) {
|
||||
if (!this._shouldAnimate(actor)) {
|
||||
if (!this._shouldAnimateActor(actor)) {
|
||||
shellwm.completed_minimize(actor);
|
||||
return;
|
||||
}
|
||||
@ -255,43 +244,47 @@ const WindowManager = new Lang.Class({
|
||||
window._dimmed = true;
|
||||
this._dimmedWindows.push(window);
|
||||
if (!Main.overview.visible)
|
||||
this._dimWindow(window, true);
|
||||
this._dimWindow(window);
|
||||
} else if (!shouldDim && window._dimmed) {
|
||||
window._dimmed = false;
|
||||
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
|
||||
return win != window;
|
||||
});
|
||||
if (!Main.overview.visible)
|
||||
this._undimWindow(window, true);
|
||||
this._undimWindow(window);
|
||||
}
|
||||
},
|
||||
|
||||
_dimWindow: function(window, animate) {
|
||||
_dimWindow: function(window) {
|
||||
let actor = window.get_compositor_private();
|
||||
if (!actor)
|
||||
return;
|
||||
if (animate)
|
||||
Tweener.addTween(getWindowDimmer(actor),
|
||||
{ dimFraction: 1.0,
|
||||
time: DIM_TIME,
|
||||
transition: 'linear'
|
||||
});
|
||||
else
|
||||
getWindowDimmer(actor).dimFraction = 1.0;
|
||||
let dimmer = getWindowDimmer(actor);
|
||||
let enabled = Meta.prefs_get_attach_modal_dialogs();
|
||||
dimmer.setEnabled(enabled);
|
||||
if (!enabled)
|
||||
return;
|
||||
Tweener.addTween(dimmer,
|
||||
{ dimFactor: 1.0,
|
||||
time: DIM_TIME,
|
||||
transition: 'linear'
|
||||
});
|
||||
},
|
||||
|
||||
_undimWindow: function(window, animate) {
|
||||
_undimWindow: function(window) {
|
||||
let actor = window.get_compositor_private();
|
||||
if (!actor)
|
||||
return;
|
||||
if (animate)
|
||||
Tweener.addTween(getWindowDimmer(actor),
|
||||
{ dimFraction: 0.0,
|
||||
time: UNDIM_TIME,
|
||||
transition: 'linear'
|
||||
});
|
||||
else
|
||||
getWindowDimmer(actor).dimFraction = 0.0;
|
||||
let dimmer = getWindowDimmer(actor);
|
||||
let enabled = Meta.prefs_get_attach_modal_dialogs();
|
||||
dimmer.setEnabled(enabled);
|
||||
if (!enabled)
|
||||
return;
|
||||
Tweener.addTween(dimmer,
|
||||
{ dimFactor: 0.0,
|
||||
time: UNDIM_TIME,
|
||||
transition: 'linear'
|
||||
});
|
||||
},
|
||||
|
||||
_mapWindow : function(shellwm, actor) {
|
||||
@ -311,28 +304,12 @@ const WindowManager = new Lang.Class({
|
||||
}));
|
||||
if (actor.meta_window.is_attached_dialog()) {
|
||||
this._checkDimming(actor.get_meta_window().get_transient_for());
|
||||
if (this._shouldAnimate()) {
|
||||
actor.set_scale(1.0, 0.0);
|
||||
actor.show();
|
||||
this._mapping.push(actor);
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_y: 1,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: this._mapWindowDone,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [shellwm, actor],
|
||||
onOverwrite: this._mapWindowOverwrite,
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
if (!this._shouldAnimate()) {
|
||||
shellwm.completed_map(actor);
|
||||
return;
|
||||
}
|
||||
shellwm.completed_map(actor);
|
||||
return;
|
||||
}
|
||||
if (!this._shouldAnimate(actor)) {
|
||||
} else if (!this._shouldAnimateActor(actor)) {
|
||||
shellwm.completed_map(actor);
|
||||
return;
|
||||
}
|
||||
@ -388,7 +365,7 @@ const WindowManager = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
actor.set_scale(1.0, 1.0);
|
||||
actor.opacity = 255;
|
||||
actor.show();
|
||||
this._destroying.push(actor);
|
||||
|
||||
@ -398,7 +375,7 @@ const WindowManager = new Lang.Class({
|
||||
}));
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_y: 0,
|
||||
{ opacity: 0,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: this._destroyWindowDone,
|
||||
@ -460,11 +437,13 @@ const WindowManager = new Lang.Class({
|
||||
this._switchData = switchData;
|
||||
switchData.inGroup = new Clutter.Group();
|
||||
switchData.outGroup = new Clutter.Group();
|
||||
switchData.movingWindowBin = new Clutter.Group();
|
||||
switchData.windows = [];
|
||||
|
||||
let wgroup = global.window_group;
|
||||
wgroup.add_actor(switchData.inGroup);
|
||||
wgroup.add_actor(switchData.outGroup);
|
||||
wgroup.add_actor(switchData.movingWindowBin);
|
||||
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let window = windows[i];
|
||||
@ -472,7 +451,12 @@ const WindowManager = new Lang.Class({
|
||||
if (!window.meta_window.showing_on_its_workspace())
|
||||
continue;
|
||||
|
||||
if (window.get_workspace() == from) {
|
||||
if (this._movingWindow && window.meta_window == this._movingWindow) {
|
||||
switchData.movingWindow = { window: window,
|
||||
parent: window.get_parent() };
|
||||
switchData.windows.push(switchData.movingWindow);
|
||||
window.reparent(switchData.movingWindowBin);
|
||||
} else if (window.get_workspace() == from) {
|
||||
switchData.windows.push({ window: window,
|
||||
parent: window.get_parent() });
|
||||
window.reparent(switchData.outGroup);
|
||||
@ -487,6 +471,8 @@ const WindowManager = new Lang.Class({
|
||||
switchData.inGroup.set_position(-xDest, -yDest);
|
||||
switchData.inGroup.raise_top();
|
||||
|
||||
switchData.movingWindowBin.raise_top();
|
||||
|
||||
Tweener.addTween(switchData.outGroup,
|
||||
{ x: xDest,
|
||||
y: yDest,
|
||||
@ -524,6 +510,10 @@ const WindowManager = new Lang.Class({
|
||||
Tweener.removeTweens(switchData.outGroup);
|
||||
switchData.inGroup.destroy();
|
||||
switchData.outGroup.destroy();
|
||||
switchData.movingWindowBin.destroy();
|
||||
|
||||
if (this._movingWindow)
|
||||
this._movingWindow = null;
|
||||
|
||||
shellwm.completed_switch_workspace();
|
||||
},
|
||||
@ -531,7 +521,7 @@ const WindowManager = new Lang.Class({
|
||||
_startAppSwitcher : function(display, screen, window, binding) {
|
||||
/* prevent a corner case where both popups show up at once */
|
||||
if (this._workspaceSwitcherPopup != null)
|
||||
this._workspaceSwitcherPopup.actor.hide();
|
||||
this._workspaceSwitcherPopup.destroy();
|
||||
|
||||
let tabPopup = new AltTab.AltTabPopup();
|
||||
|
||||
@ -547,80 +537,64 @@ const WindowManager = new Lang.Class({
|
||||
Main.ctrlAltTabManager.popup(backwards, binding.get_mask());
|
||||
},
|
||||
|
||||
_openAppMenu : function(display, screen, window, event, binding) {
|
||||
Main.panel.openAppMenu();
|
||||
},
|
||||
|
||||
_showWorkspaceSwitcher : function(display, screen, window, binding) {
|
||||
if (screen.n_workspaces == 1)
|
||||
return;
|
||||
|
||||
if (this._workspaceSwitcherPopup == null)
|
||||
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
|
||||
let [action,,,direction] = binding.get_name().split('-');
|
||||
let direction = Meta.MotionDirection[direction.toUpperCase()];
|
||||
let newWs;
|
||||
|
||||
if (binding.get_name() == 'switch-to-workspace-up')
|
||||
this.actionMoveWorkspaceUp();
|
||||
else if (binding.get_name() == 'switch-to-workspace-down')
|
||||
this.actionMoveWorkspaceDown();
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing.
|
||||
// else if (binding.get_name() == 'switch-to-workspace-left')
|
||||
// this.actionMoveWorkspaceLeft();
|
||||
// else if (binding.get_name() == 'switch-to-workspace-right')
|
||||
// this.actionMoveWorkspaceRight();
|
||||
|
||||
if (direction != Meta.MotionDirection.UP &&
|
||||
direction != Meta.MotionDirection.DOWN)
|
||||
return;
|
||||
|
||||
if (action == 'switch')
|
||||
newWs = this.actionMoveWorkspace(direction);
|
||||
else
|
||||
newWs = this.actionMoveWindow(window, direction);
|
||||
|
||||
if (!Main.overview.visible) {
|
||||
if (this._workspaceSwitcherPopup == null) {
|
||||
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
|
||||
this._workspaceSwitcherPopup.connect('destroy', Lang.bind(this, function() {
|
||||
this._workspaceSwitcherPopup = null;
|
||||
}));
|
||||
}
|
||||
this._workspaceSwitcherPopup.display(direction, newWs.index());
|
||||
}
|
||||
},
|
||||
|
||||
actionMoveWorkspaceLeft: function() {
|
||||
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
else if (!rtl && activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
actionMoveWorkspace: function(direction) {
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let toActivate = activeWorkspace.get_neighbor(direction);
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
if (activeWorkspace != toActivate)
|
||||
toActivate.activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
|
||||
return toActivate;
|
||||
},
|
||||
|
||||
actionMoveWorkspaceRight: function() {
|
||||
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (rtl && activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
else if (!rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
actionMoveWindow: function(window, direction) {
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let toActivate = activeWorkspace.get_neighbor(direction);
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
if (activeWorkspace != toActivate) {
|
||||
// This won't have any effect for "always sticky" windows
|
||||
// (like desktop windows or docks)
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
|
||||
this._movingWindow = window;
|
||||
window.change_workspace(toActivate);
|
||||
|
||||
global.display.clear_mouse_mode();
|
||||
toActivate.activate_with_focus (window, global.get_current_time());
|
||||
}
|
||||
|
||||
return toActivate;
|
||||
},
|
||||
|
||||
actionMoveWorkspaceUp: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
|
||||
},
|
||||
|
||||
actionMoveWorkspaceDown: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
|
||||
}
|
||||
});
|
||||
|
@ -301,7 +301,8 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
if (!this._zoomLightbox)
|
||||
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ fadeTime: LIGHTBOX_FADE_TIME });
|
||||
{ fadeInTime: LIGHTBOX_FADE_TIME,
|
||||
fadeOutTime: LIGHTBOX_FADE_TIME });
|
||||
this._zoomLightbox.show();
|
||||
|
||||
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
||||
@ -1121,26 +1122,6 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_showAllOverlays: function() {
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let overlay = this._windowOverlays[i];
|
||||
this._showWindowOverlay(clone, overlay,
|
||||
this.metaWorkspace == null || this.metaWorkspace == currentWorkspace);
|
||||
}
|
||||
},
|
||||
|
||||
_hideAllOverlays: function() {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
Tweener.removeTweens(clone.actor);
|
||||
let overlay = this._windowOverlays[i];
|
||||
if (overlay)
|
||||
overlay.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_delayedWindowRepositioning: function() {
|
||||
if (this._windowIsZooming)
|
||||
return true;
|
||||
@ -1252,7 +1233,7 @@ const Workspace = new Lang.Class({
|
||||
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
||||
return;
|
||||
|
||||
let clone = this._addWindowClone(win);
|
||||
let [clone, overlay] = this._addWindowClone(win);
|
||||
|
||||
if (win._overviewHint) {
|
||||
let x = win._overviewHint.x - this.actor.x;
|
||||
@ -1262,6 +1243,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
clone.actor.set_position (x, y);
|
||||
clone.actor.set_scale (scale, scale);
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
|
||||
} else {
|
||||
// Position new windows at the top corner of the workspace rather
|
||||
// than where they were placed for real to avoid the window
|
||||
@ -1321,7 +1303,10 @@ const Workspace = new Lang.Class({
|
||||
|
||||
this.leavingOverview = true;
|
||||
|
||||
this._hideAllOverlays();
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
Tweener.removeTweens(clone.actor);
|
||||
}
|
||||
|
||||
if (this._repositionWindowsId > 0) {
|
||||
Mainloop.source_remove(this._repositionWindowsId);
|
||||
@ -1336,6 +1321,10 @@ const Workspace = new Lang.Class({
|
||||
// Position and scale the windows.
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let overlay = this._windowOverlays[i];
|
||||
|
||||
if (overlay)
|
||||
overlay.hide();
|
||||
|
||||
clone.zoomFromOverview();
|
||||
|
||||
@ -1360,7 +1349,6 @@ const Workspace = new Lang.Class({
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
@ -1451,7 +1439,7 @@ const Workspace = new Lang.Class({
|
||||
this._windows.push(clone);
|
||||
this._windowOverlays.push(overlay);
|
||||
|
||||
return clone;
|
||||
return [clone, overlay];
|
||||
},
|
||||
|
||||
_onShowOverlayClose: function (windowOverlay) {
|
||||
|
@ -3,18 +3,17 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ANIMATION_TIME = 0.1;
|
||||
const DISPLAY_TIMEOUT = 600;
|
||||
|
||||
const UP = -1;
|
||||
const DOWN = 1;
|
||||
|
||||
const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
Name: 'WorkspaceSwitcherPopup',
|
||||
|
||||
@ -43,12 +42,14 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
|
||||
this.actor.add_actor(this._container);
|
||||
|
||||
this._redraw();
|
||||
|
||||
this._position();
|
||||
this._redisplay();
|
||||
|
||||
this.actor.hide();
|
||||
|
||||
this._globalSignals = [];
|
||||
this._globalSignals.push(global.screen.connect('workspace-added', Lang.bind(this, this._redisplay)));
|
||||
this._globalSignals.push(global.screen.connect('workspace-removed', Lang.bind(this, this._redisplay)));
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
|
||||
},
|
||||
|
||||
@ -104,15 +105,15 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_redraw : function(direction, activeWorkspaceIndex) {
|
||||
_redisplay: function() {
|
||||
this._list.destroy_all_children();
|
||||
|
||||
for (let i = 0; i < global.screen.n_workspaces; i++) {
|
||||
let indicator = null;
|
||||
|
||||
if (i == activeWorkspaceIndex && direction == UP)
|
||||
if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.UP)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
|
||||
else if(i == activeWorkspaceIndex && direction == DOWN)
|
||||
else if(i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.DOWN)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
|
||||
else
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-box' });
|
||||
@ -120,13 +121,13 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
this._list.add_actor(indicator);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
_position: function() {
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
|
||||
let [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
|
||||
let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
|
||||
this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
|
||||
this._container.y = primary.y + Main.panel.actor.height +
|
||||
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
|
||||
Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2);
|
||||
},
|
||||
|
||||
_show : function() {
|
||||
@ -134,12 +135,14 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
this._position();
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
display : function(direction, activeWorkspaceIndex) {
|
||||
this._redraw(direction, activeWorkspaceIndex);
|
||||
this._direction = direction;
|
||||
this._activeWorkspaceIndex = activeWorkspaceIndex;
|
||||
|
||||
this._redisplay();
|
||||
if (this._timeoutId != 0)
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
|
||||
@ -152,8 +155,22 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
Tweener.addTween(this._container, { opacity: 0.0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() { this.actor.hide(); },
|
||||
onComplete: function() { this.destroy(); },
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._timeoutId)
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
|
||||
for (let i = 0; i < this._globalSignals.length; i++)
|
||||
global.screen.disconnect(this._globalSignals[i]);
|
||||
|
||||
this.actor.destroy();
|
||||
|
||||
this.emit('destroy');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(WorkspaceSwitcherPopup.prototype);
|
||||
|
@ -667,7 +667,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
if (this._dropWorkspace != -1)
|
||||
return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, time);
|
||||
else if (this._dropPlaceholderPos != -1)
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
return source.realWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP;
|
||||
else
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
@ -187,7 +187,7 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
activeWorkspace.actor.raise_top();
|
||||
|
||||
this.actor.remove_clip(this._x, this._y, this._width, this._height);
|
||||
this.actor.remove_clip();
|
||||
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomFromOverview();
|
||||
@ -529,9 +529,11 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._updateAlwaysZoom();
|
||||
}));
|
||||
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._workspacesChanged));
|
||||
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
|
||||
this._nWorkspacesChangedId = 0;
|
||||
this._itemDragBeginId = 0;
|
||||
this._itemDragCancelledId = 0;
|
||||
this._itemDragEndId = 0;
|
||||
@ -570,9 +572,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._onRestacked));
|
||||
|
||||
if (this._nWorkspacesChangedId == 0)
|
||||
this._nWorkspacesChangedId = global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._workspacesChanged));
|
||||
if (this._itemDragBeginId == 0)
|
||||
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
@ -844,10 +843,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
if (!primaryView)
|
||||
return;
|
||||
primaryView.actor.opacity = opacity;
|
||||
if (opacity == 0)
|
||||
primaryView.actor.hide();
|
||||
else
|
||||
primaryView.actor.show();
|
||||
primaryView.actor.visible = opacity != 0;
|
||||
}));
|
||||
}));
|
||||
},
|
||||
@ -928,19 +924,16 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
let oldNumWorkspaces = this._workspaces[0].length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
if (oldNumWorkspaces == newNumWorkspaces)
|
||||
return;
|
||||
|
||||
this._updateAlwaysZoom();
|
||||
this._updateZoom();
|
||||
|
||||
if (this._workspacesViews == null)
|
||||
return;
|
||||
|
||||
let oldNumWorkspaces = this._workspaces[0].length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
let lostWorkspaces = [];
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
@ -1055,10 +1048,10 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
_onScrollEvent: function (actor, event) {
|
||||
switch ( event.get_scroll_direction() ) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspaceUp();
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspaceDown();
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/gnome-shell-extension-prefs.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
data/org.gnome.shell.gschema.xml.in.in
|
||||
js/extensionPrefs/main.js
|
||||
js/gdm/loginDialog.js
|
||||
js/gdm/powerMenu.js
|
||||
@ -9,10 +9,10 @@ js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/autorunManager.js
|
||||
js/ui/calendar.js
|
||||
js/ui/contactDisplay.js
|
||||
js/ui/dash.js
|
||||
js/ui/dateMenu.js
|
||||
js/ui/endSessionDialog.js
|
||||
js/ui/extensionDownloader.js
|
||||
js/ui/extensionSystem.js
|
||||
js/ui/keyboard.js
|
||||
js/ui/keyringPrompt.js
|
||||
@ -37,10 +37,12 @@ js/ui/status/network.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/volume.js
|
||||
js/ui/telepathyClient.js
|
||||
js/ui/unlockDialog.js
|
||||
js/ui/userMenu.js
|
||||
js/ui/viewSelector.js
|
||||
js/ui/wanda.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
src/calendar-server/evolution-calendar.desktop.in.in
|
||||
src/gvc/gvc-mixer-control.c
|
||||
src/main.c
|
||||
src/shell-app.c
|
||||
|
@ -1,2 +1,2 @@
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
data/org.gnome.shell.evolution.calendar.gschema.xml.in
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
|
132
po/ca.po
@ -3,19 +3,21 @@
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
# Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>, 2009.
|
||||
# Gil Forcada <gilforcada@guifi.net>, 2010, 2011, 2012.
|
||||
# Jordi Serratosa <jordis@softcatala.cat>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-03-19 22:35+0100\n"
|
||||
"PO-Revision-Date: 2012-03-19 22:35+0100\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2012-04-01 22:09+0000\n"
|
||||
"PO-Revision-Date: 2012-04-02 00:08+0200\n"
|
||||
"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
|
||||
"Language-Team: català; valencià <tradgnome@softcatala.org>\n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bits\n"
|
||||
"Language: ca\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||
@ -134,35 +136,43 @@ msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr "Si és «true» (cert) es mostra el número de la setmana en el calendari."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Keybinding to open the application menu"
|
||||
msgstr "Vinculació per obrir el menú d'aplicació"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid "Keybinding to open the application menu."
|
||||
msgstr "La vinculació per obrir el menú d'aplicació."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid "Which keyboard to use"
|
||||
msgstr "Quin tipus de teclat s'ha d'utilitzar"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid "The type of keyboard to use."
|
||||
msgstr "El tipus de teclat que s'utilitzarà."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Mostra l'hora amb segons"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Si és «true» (cert) es mostren els segons."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Show date in clock"
|
||||
msgstr "Mostra la data en el rellotge"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr ""
|
||||
"Si és «true» (cert) es mostra la data en el rellotge a més a més de l'hora."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "Framerate used for recording screencasts."
|
||||
msgstr "Imatges per segon per enregistrar els screencasts."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -170,11 +180,11 @@ msgstr ""
|
||||
"Les imatges per segon, en fotogrames per segon, de l'screencast enregistrat "
|
||||
"per l'eina d'enregistrament del GNOME Shell."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "El conducte GStreamer per enregistrar els screencasts"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:28
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
@ -200,11 +210,11 @@ msgstr ""
|
||||
"enregistra amb el format WEBM i utilitza el còdec VP8. El %T és una variable "
|
||||
"per estimar el nombre de fils d'execució paral·lels òptims del sistema."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:27
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:29
|
||||
msgid "File extension used for storing the screencast"
|
||||
msgstr "Extensió de fitxer per desar l'screencast"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:28
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:30
|
||||
msgid ""
|
||||
"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 "
|
||||
@ -505,7 +515,7 @@ msgstr "Ocupat"
|
||||
|
||||
#: ../js/ui/contactDisplay.js:102
|
||||
msgid "Offline"
|
||||
msgstr "Fora de línia"
|
||||
msgstr "Fora de línia"
|
||||
|
||||
#: ../js/ui/contactDisplay.js:153
|
||||
msgid "CONTACTS"
|
||||
@ -684,51 +694,51 @@ msgstr "Contrasenya:"
|
||||
msgid "Type again:"
|
||||
msgstr "Torneu a escriure-la:"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:725
|
||||
#: ../js/ui/lookingGlass.js:732
|
||||
msgid "No extensions installed"
|
||||
msgstr "No hi ha cap extensió instal·lada"
|
||||
|
||||
#. Translators: argument is an extension UUID.
|
||||
#: ../js/ui/lookingGlass.js:779
|
||||
#: ../js/ui/lookingGlass.js:786
|
||||
#, c-format
|
||||
msgid "%s has not emitted any errors."
|
||||
msgstr "%s no ha emès cap error."
|
||||
|
||||
#: ../js/ui/lookingGlass.js:785
|
||||
#: ../js/ui/lookingGlass.js:792
|
||||
msgid "Hide Errors"
|
||||
msgstr "Amaga els errors"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
|
||||
#: ../js/ui/lookingGlass.js:796 ../js/ui/lookingGlass.js:847
|
||||
msgid "Show Errors"
|
||||
msgstr "Mostra els errors"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:798
|
||||
#: ../js/ui/lookingGlass.js:805
|
||||
msgid "Enabled"
|
||||
msgstr "Habilitat"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
|
||||
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1093
|
||||
msgid "Disabled"
|
||||
msgstr "Inhabilitat"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:803
|
||||
#: ../js/ui/lookingGlass.js:810
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:805
|
||||
#: ../js/ui/lookingGlass.js:812
|
||||
msgid "Out of date"
|
||||
msgstr "Fora d'hora"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:807
|
||||
#: ../js/ui/lookingGlass.js:814
|
||||
msgid "Downloading"
|
||||
msgstr "S'està baixant"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:828
|
||||
#: ../js/ui/lookingGlass.js:835
|
||||
msgid "View Source"
|
||||
msgstr "Mostra el codi font"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:834
|
||||
#: ../js/ui/lookingGlass.js:841
|
||||
msgid "Web Page"
|
||||
msgstr "Pàgina web"
|
||||
|
||||
@ -796,15 +806,15 @@ msgstr "La xarxa sense fil requereix autenticació"
|
||||
#: ../js/ui/networkAgent.js:330
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Passwords or encryption keys are required to access the wireless network "
|
||||
"'%s'."
|
||||
"Passwords or encryption keys are required to access the wireless network '%"
|
||||
"s'."
|
||||
msgstr ""
|
||||
"Per accedir a la xarxa sense fil «%s» calen les contrasenyes o les claus "
|
||||
"d'encriptació."
|
||||
|
||||
#: ../js/ui/networkAgent.js:334
|
||||
msgid "Wired 802.1X authentication"
|
||||
msgstr "Autenticació 802.1X amb fil"
|
||||
msgstr "Autenticació 802.1X amb fil"
|
||||
|
||||
#: ../js/ui/networkAgent.js:336
|
||||
msgid "Network name: "
|
||||
@ -812,15 +822,15 @@ msgstr "Nom de la xarxa: "
|
||||
|
||||
#: ../js/ui/networkAgent.js:341
|
||||
msgid "DSL authentication"
|
||||
msgstr "Autenticació DSL"
|
||||
msgstr "Autenticació DSL"
|
||||
|
||||
#: ../js/ui/networkAgent.js:348
|
||||
msgid "PIN code required"
|
||||
msgstr "Cal que introduïu el codi PIN"
|
||||
msgstr "Cal que introduïu el codi PIN"
|
||||
|
||||
#: ../js/ui/networkAgent.js:349
|
||||
msgid "PIN code is needed for the mobile broadband device"
|
||||
msgstr "Cal que introduïu el codi PIN del dispositiu de banda ampla mòbil"
|
||||
msgstr "Cal que introduïu el codi PIN del dispositiu de banda ampla mòbil"
|
||||
|
||||
#: ../js/ui/networkAgent.js:350
|
||||
msgid "PIN: "
|
||||
@ -828,12 +838,12 @@ msgstr "PIN: "
|
||||
|
||||
#: ../js/ui/networkAgent.js:356
|
||||
msgid "Mobile broadband network password"
|
||||
msgstr "Contrasenya de la xarxa de banda ampla mòbil"
|
||||
msgstr "Contrasenya de la xarxa de banda ampla mòbil"
|
||||
|
||||
#: ../js/ui/networkAgent.js:357
|
||||
#, c-format
|
||||
msgid "A password is required to connect to '%s'."
|
||||
msgstr "Cal que introduïu una contrasenya per connectar-vos a «%s»."
|
||||
msgstr "Cal que introduïu una contrasenya per connectar-vos a «%s»."
|
||||
|
||||
#: ../js/ui/overview.js:90
|
||||
msgid "Undo"
|
||||
@ -857,17 +867,17 @@ msgstr "Aplicacions"
|
||||
msgid "Dash"
|
||||
msgstr "Quadre d'aplicacions"
|
||||
|
||||
#: ../js/ui/panel.js:591
|
||||
#: ../js/ui/panel.js:592
|
||||
msgid "Quit"
|
||||
msgstr "Surt"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:623
|
||||
#: ../js/ui/panel.js:624
|
||||
msgid "Activities"
|
||||
msgstr "Activitats"
|
||||
|
||||
#: ../js/ui/panel.js:998
|
||||
#: ../js/ui/panel.js:999
|
||||
msgid "Top Bar"
|
||||
msgstr "Barra superior"
|
||||
|
||||
@ -921,11 +931,11 @@ msgstr "toggle-switch-intl"
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Introduïu una ordre:"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:331
|
||||
#: ../js/ui/searchDisplay.js:332
|
||||
msgid "Searching..."
|
||||
msgstr "S'està cercant..."
|
||||
|
||||
#: ../js/ui/searchDisplay.js:413
|
||||
#: ../js/ui/searchDisplay.js:414
|
||||
msgid "No matching results."
|
||||
msgstr "No s'ha trobat cap coincidència."
|
||||
|
||||
@ -1068,7 +1078,7 @@ msgstr "Paràmetres de so"
|
||||
#: ../js/ui/status/bluetooth.js:372
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr "Hi ha una petició d'autorització des de %s"
|
||||
msgstr "Hi ha una sol·licitud d'autorització des de %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:378
|
||||
#, c-format
|
||||
@ -1238,7 +1248,7 @@ msgstr "Paràmetres de xarxa"
|
||||
|
||||
#: ../js/ui/status/network.js:1739
|
||||
msgid "Connection failed"
|
||||
msgstr "Ha fallat la connexió"
|
||||
msgstr "Ha fallat la connexió"
|
||||
|
||||
#: ../js/ui/status/network.js:1740
|
||||
msgid "Activation of network connection failed"
|
||||
@ -1367,11 +1377,11 @@ msgstr "Trucada"
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:287
|
||||
msgid "File Transfer"
|
||||
msgstr "Transferència de fitxers"
|
||||
msgstr "Transferència de fitxers"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:369
|
||||
msgid "Subscription request"
|
||||
msgstr "Teniu una petició de subscripció"
|
||||
msgstr "Teniu una sol·licitud de subscripció"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:405
|
||||
msgid "Connection error"
|
||||
@ -1482,7 +1492,7 @@ msgstr "En/na %s us envia %s"
|
||||
#: ../js/ui/telepathyClient.js:1194
|
||||
#, c-format
|
||||
msgid "%s would like permission to see when you are online"
|
||||
msgstr "%s vol poder saber quan esteu en línia"
|
||||
msgstr "%s vol poder saber quan esteu en línia"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1287
|
||||
msgid "Network error"
|
||||
@ -1490,7 +1500,7 @@ msgstr "Error de la xarxa"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1289
|
||||
msgid "Authentication failed"
|
||||
msgstr "Ha fallat l'autenticació"
|
||||
msgstr "Ha fallat l'autenticació"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1291
|
||||
msgid "Encryption error"
|
||||
@ -1502,19 +1512,19 @@ msgstr "No s'ha proporcionat el certificat"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1295
|
||||
msgid "Certificate untrusted"
|
||||
msgstr "El certificat no és de confiança"
|
||||
msgstr "El certificat no és de confiança"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1297
|
||||
msgid "Certificate expired"
|
||||
msgstr "El certificat ha vençut"
|
||||
msgstr "El certificat ha vençut"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1299
|
||||
msgid "Certificate not activated"
|
||||
msgstr "El certificat no està activat"
|
||||
msgstr "El certificat no està activat"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1301
|
||||
msgid "Certificate hostname mismatch"
|
||||
msgstr "No coincideix el nom de la màquina del certificat"
|
||||
msgstr "No coincideix el nom de la màquina del certificat"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1303
|
||||
msgid "Certificate fingerprint mismatch"
|
||||
@ -1530,33 +1540,33 @@ msgstr "S'ha establert l'estat a fora de línia"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1309
|
||||
msgid "Encryption is not available"
|
||||
msgstr "L'encriptació no està disponible"
|
||||
msgstr "L'encriptació no està disponible"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1311
|
||||
msgid "Certificate is invalid"
|
||||
msgstr "El certificat no és vàlid"
|
||||
msgstr "El certificat no és vàlid"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1313
|
||||
msgid "Connection has been refused"
|
||||
msgstr "S'ha rebutjat la connexió"
|
||||
msgstr "S'ha rebutjat la connexió"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1315
|
||||
msgid "Connection can't be established"
|
||||
msgstr "No es pot establir la connexió"
|
||||
msgstr "No es pot establir la connexió"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1317
|
||||
msgid "Connection has been lost"
|
||||
msgstr "S'ha perdut la connexió"
|
||||
msgstr "S'ha perdut la connexió"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1319
|
||||
msgid "This account is already connected to the server"
|
||||
msgstr "Aquest compte ja està connectat al servidor"
|
||||
msgstr "Aquest compte ja està connectat al servidor"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1321
|
||||
msgid ""
|
||||
"Connection has been replaced by a new connection using the same resource"
|
||||
msgstr ""
|
||||
"S'ha reemplaçat la connexió per una altra de nova fent servir el mateix "
|
||||
"S'ha reemplaçat la connexió per una altra de nova fent servir el mateix "
|
||||
"recurs"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1323
|
||||
@ -1565,7 +1575,7 @@ msgstr "Ja existeix aquest compte al servidor"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1325
|
||||
msgid "Server is currently too busy to handle the connection"
|
||||
msgstr "El servidor està massa ocupat per gestionar la connexió"
|
||||
msgstr "El servidor està massa ocupat per gestionar la connexió"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1327
|
||||
msgid "Certificate has been revoked"
|
||||
@ -1575,8 +1585,8 @@ msgstr "S'ha revocat el certificat"
|
||||
msgid ""
|
||||
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
|
||||
msgstr ""
|
||||
"El certificat utilitza un algorisme criptògraf no segur o la seva fortalesa "
|
||||
"criptogràfica és feble"
|
||||
"El certificat utilitza un algorisme criptògraf no segur o la seva fortalesa "
|
||||
"criptogràfica és feble"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1331
|
||||
msgid ""
|
||||
@ -1584,7 +1594,7 @@ msgid ""
|
||||
"chain, exceed the limits imposed by the cryptography library"
|
||||
msgstr ""
|
||||
"La llargada del certificat del servidor o la profunditat de la cadena de "
|
||||
"certificació excedeix els límits de la biblioteca criptogràfica"
|
||||
"certificació excedeix els límits de la biblioteca criptogràfica"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1333
|
||||
msgid "Internal error"
|
||||
|
1148
po/ca@valencia.po
83
po/cs.po
@ -1,27 +1,26 @@
|
||||
# Czech translation of gnome-shell.
|
||||
# Copyright (C) 2009, 2010, 2011 the author(s) of gnome-shell.
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
#
|
||||
# Andre Klapper <ak-47@gmx.net>, 2009.
|
||||
# Petr Kovar <pknbe@volny.cz>, 2009, 2010, 2011.
|
||||
# Adam Matoušek <adydas95@gmail.com>, 2012
|
||||
# Petr Kovar <pknbe@volny.cz>, 2009, 2010, 2011, 2012.
|
||||
# Adam Matoušek <adydas95@gmail.com>, 2012
|
||||
# Marek Černocký <marek@manet.cz>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2012-03-19 14:09+0000\n"
|
||||
"PO-Revision-Date: 2012-03-22 11:28+0100\n"
|
||||
"Last-Translator: Marek Černocký <marek@manet.cz>\n"
|
||||
"POT-Creation-Date: 2012-03-30 17:59+0000\n"
|
||||
"PO-Revision-Date: 2012-04-16 15:34+0200\n"
|
||||
"Last-Translator: Petr Kovar <pknbe@volny.cz>\n"
|
||||
"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cs\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Lokalize 1.2\n"
|
||||
"X-Generator: Virtaal 0.7.1\n"
|
||||
"X-Project-Style: gnome\n"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||
msgid "GNOME Shell"
|
||||
@ -134,34 +133,42 @@ msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr "Je-li zapnuto, zobrazovat v kalendáři čísla týdnů dle ISO."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Keybinding to open the application menu"
|
||||
msgstr "Klávesová zkratka na otevření nabídky aplikace"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid "Keybinding to open the application menu."
|
||||
msgstr "Klávesová zkratka na otevření nabídky aplikace."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid "Which keyboard to use"
|
||||
msgstr "Která klávesnice se má používat"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid "The type of keyboard to use."
|
||||
msgstr "Typ klávesnice, který se má používat."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Zobrazovat čas včetně sekund"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Je-li zapnuto, zobrazovat čas včetně sekund."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Show date in clock"
|
||||
msgstr "Zobrazovat v hodinách datum"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr "Je-li zapnuto, zobrazovat v hodinách kromě času i datum."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "Framerate used for recording screencasts."
|
||||
msgstr "Frekvence snímků při nahrávání dění na obrazovce."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -169,11 +176,11 @@ msgstr ""
|
||||
"Frekvence snímků za sekundu výsledné nahrávky dění na obrazovce, která byla "
|
||||
"nahrána záznamovým programem GNOME Shell."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "Roura systému gstreamer určená ke kódování nahrávky dění na obrazovce"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:28
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
@ -197,11 +204,11 @@ msgstr ""
|
||||
"%T ! queue ! webmmux“ s nahráváním do WEBM s kodekem VP8. %T je použito jako "
|
||||
"zástupný symbol odhadu nejvhodnějšího počtu vláken na systému."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:27
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:29
|
||||
msgid "File extension used for storing the screencast"
|
||||
msgstr "Přípona souboru s nahrávkou dění na obrazovce"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:28
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:30
|
||||
msgid ""
|
||||
"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 "
|
||||
@ -697,51 +704,51 @@ msgstr "Heslo:"
|
||||
msgid "Type again:"
|
||||
msgstr "Napište znovu:"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:725
|
||||
#: ../js/ui/lookingGlass.js:732
|
||||
msgid "No extensions installed"
|
||||
msgstr "Nejsou nainstalována žádná rozšíření"
|
||||
|
||||
#. Translators: argument is an extension UUID.
|
||||
#: ../js/ui/lookingGlass.js:779
|
||||
#: ../js/ui/lookingGlass.js:786
|
||||
#, c-format
|
||||
msgid "%s has not emitted any errors."
|
||||
msgstr "Rozšíření %s nevyvolalo žádné chyby."
|
||||
|
||||
#: ../js/ui/lookingGlass.js:785
|
||||
#: ../js/ui/lookingGlass.js:792
|
||||
msgid "Hide Errors"
|
||||
msgstr "Skrývat chyby"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
|
||||
#: ../js/ui/lookingGlass.js:796 ../js/ui/lookingGlass.js:847
|
||||
msgid "Show Errors"
|
||||
msgstr "Zobrazovat chyby"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:798
|
||||
#: ../js/ui/lookingGlass.js:805
|
||||
msgid "Enabled"
|
||||
msgstr "Povoleno"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
|
||||
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1093
|
||||
msgid "Disabled"
|
||||
msgstr "Zakázáno"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:803
|
||||
#: ../js/ui/lookingGlass.js:810
|
||||
msgid "Error"
|
||||
msgstr "Chyba"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:805
|
||||
#: ../js/ui/lookingGlass.js:812
|
||||
msgid "Out of date"
|
||||
msgstr "Neaktuální"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:807
|
||||
#: ../js/ui/lookingGlass.js:814
|
||||
msgid "Downloading"
|
||||
msgstr "Stahování"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:828
|
||||
#: ../js/ui/lookingGlass.js:835
|
||||
msgid "View Source"
|
||||
msgstr "Zobrazit zdroj"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:834
|
||||
#: ../js/ui/lookingGlass.js:841
|
||||
msgid "Web Page"
|
||||
msgstr "Webová stránka"
|
||||
|
||||
@ -809,8 +816,8 @@ msgstr "K bezdrátové síti je vyžadováno ověření"
|
||||
#: ../js/ui/networkAgent.js:330
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Passwords or encryption keys are required to access the wireless network "
|
||||
"'%s'."
|
||||
"Passwords or encryption keys are required to access the wireless network '%"
|
||||
"s'."
|
||||
msgstr ""
|
||||
"Pro přístup k bezdrátové síti „%s“ jsou vyžadována hesla nebo šifrovací "
|
||||
"klíče."
|
||||
@ -870,17 +877,17 @@ msgstr "Aplikace"
|
||||
msgid "Dash"
|
||||
msgstr "Oblíbené"
|
||||
|
||||
#: ../js/ui/panel.js:591
|
||||
#: ../js/ui/panel.js:592
|
||||
msgid "Quit"
|
||||
msgstr "Ukončit"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:623
|
||||
#: ../js/ui/panel.js:624
|
||||
msgid "Activities"
|
||||
msgstr "Činnosti"
|
||||
|
||||
#: ../js/ui/panel.js:998
|
||||
#: ../js/ui/panel.js:999
|
||||
msgid "Top Bar"
|
||||
msgstr "Horní lišta"
|
||||
|
||||
@ -934,11 +941,11 @@ msgstr "toggle-switch-intl"
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Zadejte prosím příkaz:"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:331
|
||||
#: ../js/ui/searchDisplay.js:332
|
||||
msgid "Searching..."
|
||||
msgstr "Hledá se…"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:413
|
||||
#: ../js/ui/searchDisplay.js:414
|
||||
msgid "No matching results."
|
||||
msgstr "Neodpovídá ani jeden z výsledků."
|
||||
|
||||
|
116
po/de.po
@ -11,15 +11,16 @@
|
||||
# Jakob Kramer <jakob.kramer@gmx.de>, 2010.
|
||||
# Paul Seyfert <pseyfert@mathphys.fsk.uni-heidelberg.de>, 2010, 2011.
|
||||
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009, 2010, 2011, 2012.
|
||||
# Wolfgang Stöggl <c72578@yahoo.de>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2012-03-18 00:40+0000\n"
|
||||
"PO-Revision-Date: 2012-03-18 01:04+0100\n"
|
||||
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
|
||||
"POT-Creation-Date: 2012-04-13 19:40+0000\n"
|
||||
"PO-Revision-Date: 2012-04-14 16:04+0200\n"
|
||||
"Last-Translator: Wolfgang Stoeggl <c72578@yahoo.de>\n"
|
||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -142,34 +143,42 @@ msgstr ""
|
||||
"Wenn dieser Wert gesetzt ist, wird der ISO-Wochentag im Kalender angezeigt."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Keybinding to open the application menu"
|
||||
msgstr "Tastenkombination zum Öffnen des Anwendungsmenüs"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid "Keybinding to open the application menu."
|
||||
msgstr "Tastenkombination zum Öffnen des Anwendungsmenüs."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid "Which keyboard to use"
|
||||
msgstr "Zu verwendende Tastatur"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid "The type of keyboard to use."
|
||||
msgstr "Der Typ der zu verwendenden Tastatur"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Zeit sekundengenau anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Legt fest, ob in der Uhrzeit Sekunden angezeigt werden."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Show date in clock"
|
||||
msgstr "Datum in der Uhr anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr "Legt fest, ob das Datum zusätzlich zur Uhrzeit angezeigt wird."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "Framerate used for recording screencasts."
|
||||
msgstr "Bildwiederholungsrate zur Aufnahme von Screencasts"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -178,12 +187,12 @@ msgstr ""
|
||||
"der GNOME-Shell aufgezeichnet werden soll, in Einzelbildern pro Sekunde."
|
||||
|
||||
# hmm Enkodieren oder Kodieren?
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "Die GStreamer-Weiterleitung zur Enkodierung des Screencasts"
|
||||
|
||||
# Hier blicke ich überhaupt nicht durch.
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:28
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
@ -210,11 +219,11 @@ msgstr ""
|
||||
"mittels des VP8-Codecs aufzeichnet. %T wird als Platzhalter für die "
|
||||
"vermutete optimale Thread-Anzahl auf dem System verwendet."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:27
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:29
|
||||
msgid "File extension used for storing the screencast"
|
||||
msgstr "Die Dateiendung zum Speichern des Screencast"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:28
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:30
|
||||
msgid ""
|
||||
"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 "
|
||||
@ -238,53 +247,53 @@ msgstr "<b>Erweiterung</b>"
|
||||
msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "Wählen Sie oben eine Erweiterung aus, die Sie konfigurieren wollen."
|
||||
|
||||
#: ../js/gdm/loginDialog.js:624
|
||||
#: ../js/gdm/loginDialog.js:627
|
||||
msgid "Session..."
|
||||
msgstr "Sitzung …"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:786
|
||||
#: ../js/gdm/loginDialog.js:789
|
||||
msgctxt "title"
|
||||
msgid "Sign In"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#. Translators: this message is shown below the password entry field
|
||||
#. to indicate the user can swipe their finger instead
|
||||
#: ../js/gdm/loginDialog.js:831
|
||||
#: ../js/gdm/loginDialog.js:834
|
||||
msgid "(or swipe finger)"
|
||||
msgstr "(oder benutzen Sie den Fingerabdruckleser)"
|
||||
|
||||
#. translators: this message is shown below the user list on the
|
||||
#. login screen. It can be activated to reveal an entry for
|
||||
#. manually entering the username.
|
||||
#: ../js/gdm/loginDialog.js:852
|
||||
#: ../js/gdm/loginDialog.js:855
|
||||
msgid "Not listed?"
|
||||
msgstr "Nicht aufgeführt?"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1020 ../js/ui/endSessionDialog.js:401
|
||||
#: ../js/ui/extensionSystem.js:399 ../js/ui/networkAgent.js:153
|
||||
#: ../js/gdm/loginDialog.js:1023 ../js/ui/endSessionDialog.js:401
|
||||
#: ../js/ui/extensionSystem.js:400 ../js/ui/networkAgent.js:153
|
||||
#: ../js/ui/polkitAuthenticationAgent.js:175 ../js/ui/status/bluetooth.js:462
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1025
|
||||
#: ../js/gdm/loginDialog.js:1028
|
||||
msgctxt "button"
|
||||
msgid "Sign In"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1377
|
||||
#: ../js/gdm/loginDialog.js:1380
|
||||
msgid "Login Window"
|
||||
msgstr "Anmeldefenster"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:152 ../js/ui/userMenu.js:597
|
||||
#: ../js/gdm/powerMenu.js:155 ../js/ui/userMenu.js:597
|
||||
#: ../js/ui/userMenu.js:599 ../js/ui/userMenu.js:668
|
||||
msgid "Suspend"
|
||||
msgstr "Bereitschaft"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:157
|
||||
#: ../js/gdm/powerMenu.js:160
|
||||
msgid "Restart"
|
||||
msgstr "Neu starten"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:162
|
||||
#: ../js/gdm/powerMenu.js:165
|
||||
msgid "Power Off"
|
||||
msgstr "Ausschalten"
|
||||
|
||||
@ -679,11 +688,11 @@ msgstr[1] "Das System wird automatisch in %d Sekunden neu gestartet."
|
||||
msgid "Restarting the system."
|
||||
msgstr "Neustart des Systems."
|
||||
|
||||
#: ../js/ui/extensionSystem.js:403
|
||||
#: ../js/ui/extensionSystem.js:404
|
||||
msgid "Install"
|
||||
msgstr "Installieren"
|
||||
|
||||
#: ../js/ui/extensionSystem.js:407
|
||||
#: ../js/ui/extensionSystem.js:408
|
||||
#, c-format
|
||||
msgid "Download and install '%s' from extensions.gnome.org?"
|
||||
msgstr "»%s« von extensions.gnome.org herunterladen und installieren?"
|
||||
@ -692,7 +701,8 @@ msgstr "»%s« von extensions.gnome.org herunterladen und installieren?"
|
||||
msgid "tray"
|
||||
msgstr "Benachrichtigungsfeld"
|
||||
|
||||
#: ../js/ui/keyboard.js:544 ../js/ui/status/power.js:203
|
||||
#: ../js/ui/keyboard.js:544 ../js/ui/status/keyboard.js:44
|
||||
#: ../js/ui/status/power.js:203
|
||||
msgid "Keyboard"
|
||||
msgstr "Tastatur"
|
||||
|
||||
@ -704,51 +714,51 @@ msgstr "Passwort:"
|
||||
msgid "Type again:"
|
||||
msgstr "Erneut eingeben:"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:725
|
||||
#: ../js/ui/lookingGlass.js:732
|
||||
msgid "No extensions installed"
|
||||
msgstr "Keine Erweiterungen installiert"
|
||||
|
||||
#. Translators: argument is an extension UUID.
|
||||
#: ../js/ui/lookingGlass.js:779
|
||||
#: ../js/ui/lookingGlass.js:786
|
||||
#, c-format
|
||||
msgid "%s has not emitted any errors."
|
||||
msgstr "%s hat keine Fehler ausgegeben."
|
||||
|
||||
#: ../js/ui/lookingGlass.js:785
|
||||
#: ../js/ui/lookingGlass.js:792
|
||||
msgid "Hide Errors"
|
||||
msgstr "Fehler verbergen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
|
||||
#: ../js/ui/lookingGlass.js:796 ../js/ui/lookingGlass.js:847
|
||||
msgid "Show Errors"
|
||||
msgstr "Fehler anzeigen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:798
|
||||
#: ../js/ui/lookingGlass.js:805
|
||||
msgid "Enabled"
|
||||
msgstr "Aktiviert"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
|
||||
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1082
|
||||
msgid "Disabled"
|
||||
msgstr "Deaktiviert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:803
|
||||
#: ../js/ui/lookingGlass.js:810
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:805
|
||||
#: ../js/ui/lookingGlass.js:812
|
||||
msgid "Out of date"
|
||||
msgstr "Veraltet"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:807
|
||||
#: ../js/ui/lookingGlass.js:814
|
||||
msgid "Downloading"
|
||||
msgstr "Herunterladen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:828
|
||||
#: ../js/ui/lookingGlass.js:835
|
||||
msgid "View Source"
|
||||
msgstr "Quelle zeigen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:834
|
||||
#: ../js/ui/lookingGlass.js:841
|
||||
msgid "Web Page"
|
||||
msgstr "Webseite"
|
||||
|
||||
@ -859,32 +869,36 @@ msgstr "Es wird ein Passwort benötigt, um sich mit »%s« zu verbinden."
|
||||
msgid "Undo"
|
||||
msgstr "Rückgängig"
|
||||
|
||||
#: ../js/ui/overview.js:199
|
||||
#: ../js/ui/overview.js:132
|
||||
msgid "Overview"
|
||||
msgstr "Übersicht"
|
||||
|
||||
#: ../js/ui/overview.js:202
|
||||
msgid "Windows"
|
||||
msgstr "Fenster"
|
||||
|
||||
#: ../js/ui/overview.js:202
|
||||
#: ../js/ui/overview.js:205
|
||||
msgid "Applications"
|
||||
msgstr "Anwendungen"
|
||||
|
||||
# Würde ich so übernehmen, oder evtl. »Dock«.
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/overview.js:228
|
||||
#: ../js/ui/overview.js:231
|
||||
msgid "Dash"
|
||||
msgstr "Dash"
|
||||
|
||||
#: ../js/ui/panel.js:591
|
||||
#: ../js/ui/panel.js:592
|
||||
msgid "Quit"
|
||||
msgstr "Beenden"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:623
|
||||
#: ../js/ui/panel.js:624
|
||||
msgid "Activities"
|
||||
msgstr "Aktivitäten"
|
||||
|
||||
#: ../js/ui/panel.js:998
|
||||
#: ../js/ui/panel.js:999
|
||||
msgid "Top Bar"
|
||||
msgstr "Obere Leiste"
|
||||
|
||||
@ -938,11 +952,11 @@ msgstr "toggle-switch-intl"
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Bitte geben Sie einen Befehl ein:"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:331
|
||||
#: ../js/ui/searchDisplay.js:332
|
||||
msgid "Searching..."
|
||||
msgstr "Suche läuft …"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:413
|
||||
#: ../js/ui/searchDisplay.js:415
|
||||
msgid "No matching results."
|
||||
msgstr "Keine passenden Ergebnisse."
|
||||
|
||||
@ -1193,7 +1207,7 @@ msgstr "Verbindung gescheitert"
|
||||
|
||||
#: ../js/ui/status/network.js:585 ../js/ui/status/network.js:1505
|
||||
msgid "More..."
|
||||
msgstr "Mehr ..."
|
||||
msgstr "Mehr …"
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
@ -1720,7 +1734,7 @@ msgstr "»%s« ist bereit"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1100
|
||||
#: ../src/gvc/gvc-mixer-control.c:1089
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
@ -1729,14 +1743,14 @@ msgstr[1] "%u Ausgänge"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1110
|
||||
#: ../src/gvc/gvc-mixer-control.c:1099
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "%u Eingang"
|
||||
msgstr[1] "%u Eingänge"
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:1408
|
||||
#: ../src/gvc/gvc-mixer-control.c:1397
|
||||
msgid "System Sounds"
|
||||
msgstr "Systemklänge"
|
||||
|
||||
|