Compare commits
355 Commits
wip/pad-os
...
3.23.92
Author | SHA1 | Date | |
---|---|---|---|
3b7a4b08e2 | |||
09af4433b0 | |||
50df3084f7 | |||
e90734913d | |||
af18a0cf40 | |||
3d399796a6 | |||
60a2794c8b | |||
e039871298 | |||
e057333bf3 | |||
e0c0d9223e | |||
ada21c975f | |||
b4df747464 | |||
9a38011e1e | |||
e0f1fc2694 | |||
da5390340e | |||
3a6b41495a | |||
ce5875f365 | |||
4373d390dc | |||
9ed512e5c5 | |||
c5856196a7 | |||
afc3a8a7e7 | |||
4714c73f0f | |||
742155c384 | |||
2d814bfc5e | |||
8f8e512b37 | |||
3c828c8387 | |||
7c96b39bef | |||
64dbc8aa7f | |||
5649b9c64a | |||
3d0aab3b66 | |||
ce419da6ec | |||
63b49c65ab | |||
ff5bd0b925 | |||
0892220f63 | |||
1c95c9e3ff | |||
4b80cbe1cd | |||
46f3712421 | |||
18aa4ff30c | |||
8a75143a6e | |||
384a6e8684 | |||
a0008f7471 | |||
b53f95a9a0 | |||
1af88e52dd | |||
535028c9a7 | |||
df816368a5 | |||
f34202c3c8 | |||
28c61754a1 | |||
6e46166df4 | |||
15feaa1074 | |||
d7c532daee | |||
427ec6834d | |||
e594b30ead | |||
f43a8a75e4 | |||
645aa01efd | |||
252dce1ec2 | |||
da0066eb84 | |||
adc811ff8f | |||
5117ccdeef | |||
19816523c7 | |||
846e3f8243 | |||
d3c050b88d | |||
f3d1c78c7d | |||
c4f2bb5fe0 | |||
fec511c786 | |||
8a6157c7c1 | |||
d3bb7903e2 | |||
239b67eff6 | |||
0e0caee6ba | |||
62606c68b9 | |||
da831e894c | |||
4b166dcc79 | |||
796fdca5c5 | |||
c6f22826cf | |||
4e491b6f75 | |||
0569bb18f5 | |||
0353ebde5d | |||
b5130c5943 | |||
d54db8ffb3 | |||
0ff5fc8dbb | |||
32ec9959ef | |||
838721fc31 | |||
63e9c98248 | |||
6df30cbb64 | |||
c102a89962 | |||
ff5e39ab79 | |||
607b2efcce | |||
27d010110a | |||
e13602b896 | |||
6777670f0d | |||
b05739fa7e | |||
38854fb06a | |||
b091cfea80 | |||
a8955ffe05 | |||
475fd72ae1 | |||
31675d1812 | |||
cbb0c1c091 | |||
c1234f7793 | |||
1e6c44cb6b | |||
2202b9330f | |||
0b332fc019 | |||
4f4163eb05 | |||
649d360289 | |||
9bcdd9c274 | |||
fbc60199bc | |||
785c813771 | |||
75f8279a19 | |||
5d07832e96 | |||
7395aaf9b4 | |||
2c070d38fb | |||
2c5bc4a1a9 | |||
fa82af251f | |||
e08f2a4a04 | |||
30e17036e8 | |||
c75785efff | |||
3bf89055e3 | |||
a76869216a | |||
a9fd8bfa5e | |||
02a7b0dcfd | |||
dd8c06f2c5 | |||
c63b7f0c3f | |||
a46ea3f8a0 | |||
93071d9167 | |||
2b2e9d4098 | |||
0429aad8bf | |||
2e74920a64 | |||
b5bf82b5db | |||
98cdd44543 | |||
304b68eff9 | |||
18074951b9 | |||
0008ef70e1 | |||
c3cdbd0dac | |||
aefd61c3db | |||
f7752ac699 | |||
d017e6749c | |||
ddfdfaed78 | |||
a870a4d6de | |||
e6e786a19c | |||
01975b61f5 | |||
3c61bef92d | |||
050f2090fb | |||
7746f1a5c4 | |||
97a1cdbe7a | |||
b1dcea7cf1 | |||
9f6f48025d | |||
5e66ac2674 | |||
93c66b3537 | |||
e0d7d28c20 | |||
a82c564a73 | |||
6a40e72329 | |||
3737a9950c | |||
7784bc0905 | |||
49607e1313 | |||
a81f18592a | |||
2ff988ef37 | |||
5136369c18 | |||
7557207b47 | |||
f9d1e2fec0 | |||
f9a03f212c | |||
d200fb1d14 | |||
eb844b095a | |||
dc638c04a6 | |||
b3cbce97ed | |||
14a32c128d | |||
2e332ffd12 | |||
71d9d483f2 | |||
1ef6262139 | |||
da7db509e7 | |||
2a525bd8e8 | |||
51da2bf363 | |||
d2c0ade880 | |||
bcc3eccdab | |||
e581e249ad | |||
c131c44ef6 | |||
cdd23d9cc7 | |||
dc110db4e0 | |||
678670bf1b | |||
07fc15a1eb | |||
83005e27da | |||
56b20ef779 | |||
04adeec8f5 | |||
62d640cd9e | |||
9d7ad6748c | |||
accd24e379 | |||
75d797a0ac | |||
e1e321d3a7 | |||
f14f238150 | |||
ab3200019a | |||
28c028f7ac | |||
08a159d0d8 | |||
26aa32c6cc | |||
09e6bb5d56 | |||
e661d904de | |||
407d51e871 | |||
c4a07fad83 | |||
9ea8fdc1d1 | |||
3d6bf43649 | |||
7bc1d57ad7 | |||
68b671a4f4 | |||
1de1fd44c5 | |||
e5101eb407 | |||
48e0d86340 | |||
c64cf30160 | |||
ff9d777c01 | |||
d8c33deb33 | |||
c03ff0bd4f | |||
d597633d8f | |||
4f6eb49be3 | |||
d23bd4c4a6 | |||
7f12265685 | |||
250023b823 | |||
3b52691ab4 | |||
b6cd548186 | |||
b3fabf11b1 | |||
15be562fdf | |||
599fa0b76c | |||
c520eb4de0 | |||
e33c68a415 | |||
97bd224261 | |||
503e086c56 | |||
7dd6b7f04f | |||
0b05b7a527 | |||
7e5274619a | |||
900957d658 | |||
e006b9b400 | |||
d042dd73aa | |||
347972e45f | |||
7e803fdf23 | |||
ccacb5f6de | |||
5117139add | |||
d5c0514e21 | |||
d81a6bdf41 | |||
2812afed22 | |||
f5bd86fa11 | |||
b625f1692c | |||
36ac1f8902 | |||
bec4b5c127 | |||
78cd8b9f9a | |||
205880e74e | |||
009d021e4f | |||
39a840e2c3 | |||
56d0d7253b | |||
7a29cc47d4 | |||
d769b72c43 | |||
a3439a5230 | |||
9f45161133 | |||
ed99bef458 | |||
c9ad54cd82 | |||
d7e42d5d6e | |||
5b339c8371 | |||
9c6e68f3e7 | |||
70526a8025 | |||
c405081d89 | |||
384e01b368 | |||
f819654ec8 | |||
4670db6629 | |||
6ebabd50c6 | |||
0c22a21a24 | |||
70a0c4211c | |||
a21af541c4 | |||
e07ba91486 | |||
a22e9ce9cd | |||
d4ce51b1b7 | |||
830005069c | |||
8c49267658 | |||
d0bab1f7ac | |||
f00826f3fb | |||
717c0ea19f | |||
3cd8dd0f32 | |||
aedd616346 | |||
b3de3ff00b | |||
b66dff8aed | |||
ff814df03a | |||
c9a528025c | |||
5f3ec8a6f1 | |||
becd29c50a | |||
a029a35050 | |||
d6a78d61d1 | |||
9be46bd212 | |||
4df3afced0 | |||
90f8e1df80 | |||
ea2ac89e61 | |||
695bfb9616 | |||
5dbdde8c15 | |||
854bfc17ed | |||
c8b192bcae | |||
f07306897f | |||
44d731fff5 | |||
52f939f64c | |||
f1d12c18d8 | |||
373ebb50eb | |||
b0b5be63b9 | |||
68f439425b | |||
cae4d92191 | |||
f336295475 | |||
06d0e7d74a | |||
5e0e3edc7b | |||
02a51bfa65 | |||
dbd04df311 | |||
e6adcd99c7 | |||
3171819c36 | |||
e4976b535d | |||
06d1602f17 | |||
603d0dfbc3 | |||
0b6c5b4620 | |||
d7894dbc44 | |||
d25d8126ea | |||
2402d3e115 | |||
ac4284d348 | |||
f9ef80749a | |||
22f0d3076e | |||
127b6dca1c | |||
8cfff33291 | |||
21f534c7f0 | |||
9faa1db942 | |||
2fc9bffb23 | |||
f16eae45a2 | |||
622e73c34c | |||
eb1c3d7045 | |||
fc26895f70 | |||
ca224b379f | |||
42b609b852 | |||
a24169e2aa | |||
144d1dca88 | |||
38ab6ba3ec | |||
bc6a0626c1 | |||
497f4f4a3c | |||
8c8c89a5f1 | |||
d658a5148a | |||
2c3e0dab0f | |||
e2400e35bd | |||
fc4562491b | |||
6e3bedb888 | |||
2b8332df5e | |||
795e7d4b59 | |||
522d46a525 | |||
dcecf5e350 | |||
a61c929450 | |||
5992e9f524 | |||
ee4cd05ebd | |||
af5e65fd79 | |||
284cbf3ea7 | |||
c3e68955e3 | |||
ffa28e7ee7 | |||
78d81f909c | |||
34551bf48b | |||
e02cd53ac5 | |||
b7f5154bd3 | |||
d80247918c | |||
bb8c2d3e13 | |||
6e0e37519d | |||
5c19ae5878 | |||
8713473c64 | |||
a4b4f2f6fd | |||
03f22135b5 | |||
5b79f657eb |
4
.gitignore
vendored
4
.gitignore
vendored
@ -16,7 +16,6 @@ config.log
|
||||
config.status
|
||||
config
|
||||
configure
|
||||
data/50-gnome-shell-*.xml
|
||||
data/org.gnome.Shell.desktop
|
||||
data/org.gnome.Shell.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
@ -26,8 +25,6 @@ data/gschemas.compiled
|
||||
data/perf-background.xml
|
||||
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
|
||||
data/org.gnome.Shell.PortalHelper.desktop
|
||||
data/org.gnome.Shell.PortalHelper.service
|
||||
data/theme/.sass-cache
|
||||
@ -74,7 +71,6 @@ 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
|
||||
|
233
NEWS
233
NEWS
@ -1,3 +1,236 @@
|
||||
3.23.92
|
||||
=======
|
||||
* Implement DND to overview on wayland [Hyungwon; #765003]
|
||||
* Make telepathy optional at runtime [Florian; #771721, #779878]
|
||||
* Don't show forecasts for NYC when geoclue gets stuck [Sebastian; #779898]
|
||||
* Add bottom edge drag gesture to bring up the OSK [Jan-Michael; #757712]
|
||||
* Allow switching between pads in the same group [Carlos; #779986]
|
||||
* Ignore showBanners policy for critical notifications [Florian; #779974]
|
||||
* Misc. bug fixes [Florian; #779435, #779819, #779820]
|
||||
|
||||
Contributors:
|
||||
Jan-Michael Brummer, Allan Day, Carlos Garnacho, Hyungwon Hwang,
|
||||
Sebastian Keller, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Enrico Nicoletto [pt_BR], Jiri Grönroos [fi], Chao-Hsiung Liao [zh_TW],
|
||||
Piotr Drąg [pl], Piotr Drąg [he], Balázs Meskó [hu], Kris Thomsen [da],
|
||||
Yuras Shumovich [be], Sveinn í Felli [is], Inaki Larranaga Murgoitio [eu],
|
||||
Changwoo Ryu [ko], Jordi Mas [ca], Aurimas Černius [lt],
|
||||
Мирослав Николић [sr, sr@latin], Christian Kirbach [de], Anders Jonsson [sv],
|
||||
Fabio Tomat [fur], GNOME Translation Robot [gd], Dušan Kazik [sk],
|
||||
Kukuh Syafaat [id], Marek Černocký [cs], Stas Solovey [ru],
|
||||
Milo Casagrande [it], Fran Dieguez [gl], Daniel Boles [gl], A S Alam [pa],
|
||||
Daniel Mustieles [es]
|
||||
|
||||
3.23.91
|
||||
=======
|
||||
* Use the original timestamps for restored notifications [Florian; #766410]
|
||||
* Add weather information to date+time drop-down [Florian; #754031]
|
||||
* Refine message list layout in date+time drop-down [Florian; #775763]
|
||||
* Make next/prev media controls insensitive when unavailable [Florian; #773884]
|
||||
* Misc. bug fixes [Piotr, Bastien, Florian; #772210, #769546, #775799]
|
||||
|
||||
Contributors:
|
||||
Piotr Drąg, Carlos Garnacho, Florian Müllner, Bastien Nocera
|
||||
|
||||
Translations:
|
||||
Baurzhan Muftakhidinov [kk], Jordi Mas [ca], Ask Hjorth Larsen [da],
|
||||
Inaki Larranaga Murgoitio [eu], Daniel Mustieles [es], Dušan Kazik [sk],
|
||||
Aurimas Černius [lt], Jiri Grönroos [fi], Kjartan Maraas [nb],
|
||||
Piotr Drąg [pl], Daniel Korostil [uk], Kukuh Syafaat [id],
|
||||
Milo Casagrande [it], Fabio Tomat [fur], Rafael Fontenelle [pt_BR],
|
||||
Fran Dieguez [gl], Мирослав Николић [sr, sr@latin], Balázs Meskó [hu],
|
||||
Chao-Hsiung Liao [zh_TW]
|
||||
|
||||
3.23.90
|
||||
=======
|
||||
* Handle Ctrl+Q and Ctrl+W in portal window [Bastien; #764133]
|
||||
* Allow to scroll through ibus candidates with mouse [Peng; #776032]
|
||||
* Reload apps on .desktop file content changes [Adrian; #773636]
|
||||
* Use private data/cache directories in portal helper [Bastien; #775639]
|
||||
* Fix subsurfaces not showing up in previews [Rui; #756715]
|
||||
* Fix theme node transitions [Florian; #778145]
|
||||
* Update pad (o)leds on mode switches [Carlos; #776543]
|
||||
* Add security indicators to defend against malicious portals [Bastien; #749197]
|
||||
* Don't allow type ahead at the login screen [Ray; #766139]
|
||||
* Don't fail to load because of TLS errors [Bastien; #778253]
|
||||
* Ensure the network lists remains sorted on rename [Benjamin; #778686]
|
||||
* Toggle power-off/suspend button on long-press [Florian; #721173]
|
||||
* Add "kill-switch" for user extensions [Florian; #778664]
|
||||
* Add night light indicator to status area [Florian; #741224]
|
||||
* Misc. bug fixes [Michael, Bastien, Carlos, Rui, Florian, Alan, Philip, Jonas;
|
||||
#759793, #735233, #762444, #777784, #777934, #778158, #776199, #778425,
|
||||
#771098, #778552, #777317, #778660, #778661, #745626, #778672]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Philip Chimento,
|
||||
Alan Coopersmith, Piotr Drąg, Carlos Garnacho, Yuri Konotopov,
|
||||
Lionel Landwerlin, Rui Matos, Florian Müllner, Bastien Nocera,
|
||||
Adrian Perez de Castro, Robert Roth, Ray Strode, Peng Wu
|
||||
|
||||
Translations:
|
||||
Jiri Grönroos [fi], Balázs Meskó [hu], Gábor Kelemen [hu],
|
||||
Daniel Mustieles [es], Dušan Kazik [sk],
|
||||
Piotr Drąg [ar, eu, fa, hr, pa, pt, sr, sr@latin], Rafael Fontenelle [pt_BR],
|
||||
Jordi Mas [ca], Piotr Drąg [pl], Alexandre Franke [fr],
|
||||
Baurzhan Muftakhidinov [kk], Yuras Shumovich [be], Mandy Wang [zh_CN],
|
||||
Marek Černocký [cs], Kukuh Syafaat [id], Kjartan Maraas [nb],
|
||||
Daniel Korostil [uk]
|
||||
|
||||
3.23.3
|
||||
======
|
||||
* Fix replacing of GNotifications [Florian; #775149]
|
||||
* Prepare for mozjs31 GJS [Philip; #775374]
|
||||
* Misc. bug fixes [Niels, Jonas; #775507, #776130]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Michael Catanzaro, Philip Chimento, Niels De Graef,
|
||||
Carlos Garnacho, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Muhammet Kara [tr], Christian Kirbach [de], Baurzhan Muftakhidinov [kk],
|
||||
Cheng-Chia Tseng [zh_TW], A S Alam [pa], Gianvito Cavasoli [it]
|
||||
|
||||
3.23.2
|
||||
======
|
||||
* Implement Pad configuration OSD [Carlos; #771067]
|
||||
* Show overview on three-finger touchpad pinch [Carlos; #765937]
|
||||
* Summarize network sections with too many devices [Florian; #773892]
|
||||
* Always show primary network icon when connected [Florian; #773890]
|
||||
* Fix fullscreen transitions on wayland [Rui; #770345]
|
||||
* Work around portal failures by using a URL without HTPPS redirect [Debarshi; #769940]
|
||||
* Fix app view hiding when no usage data is available [Florian, Xiaoguang; #774381]
|
||||
* Misc. bug fixes [Florian, Ray; #773875, #740043, #773893, #774643, #774805]
|
||||
|
||||
Contributors:
|
||||
Carlos Garnacho, Rui Matos, Florian Müllner, Debarshi Ray, Ray Strode,
|
||||
Xiaoguang Wang
|
||||
|
||||
Translations:
|
||||
Balázs Meskó [hu], Fabio Tomat [fur], Marek Cernocky [cs], Stas Solovey [ru],
|
||||
Daniel Mustieles [es], Marek Černocký [cs], Piotr Drąg [pl],
|
||||
Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Jiri Grönroos [fi],
|
||||
Kjartan Maraas [nb]
|
||||
|
||||
3.23.1
|
||||
======
|
||||
* Request periodic scans while WiFi list is open [Dan; #767918]
|
||||
* Include extension UUID in structured log metadata [Jonh; #770717]
|
||||
* Line-wrap PAM messages on login screen [Tao; #764445]
|
||||
* Add a way to launch an app on the discrete GPU [Bastien; #773117]
|
||||
* Only allow graphs to lift screen shield when locked [Florian; #773328]
|
||||
* Add reload option to gnome-shell-extension-tool [Jonh; #772593]
|
||||
* Update background animations when resuming from suspend [Florian; #773265]
|
||||
* Misc. bug fixes [Cosimo, Bastien, Florian, Philip, Carlos; #772723, #772287,
|
||||
#756432, #772386, #772386, #773085, #773634]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Philip Chimento, Carlos Garcia Campos, Florian Müllner,
|
||||
Bastien Nocera, Jonh Wendell, Dan Williams, Tao Yang
|
||||
|
||||
Translations:
|
||||
Fabio Tomat [fur], Philip Chimento [zh_CN], YunQiang Su [zh_CN],
|
||||
Jordi Mas [ca], Piotr Drąg [pl], Muhammet Kara [tr], Marek Černocký [cs],
|
||||
Daniel Korostil [uk], Dušan Kazik [sk]
|
||||
|
||||
3.22.1
|
||||
======
|
||||
* Fix hidden network indicator on startup [Florian; #772249]
|
||||
* Fix order of windows with modal dialogs in window switcher [Florian; #747153]
|
||||
* Fix feedback loop between StClipboard and X11 bridge [Carlos; #760745]
|
||||
* Reliably match windows from Flatpak apps [Florian; #772615]
|
||||
* Misc. bug fixes [Philip; #742249]
|
||||
|
||||
Contributors:
|
||||
Philip Chimento, Carlos Garnacho, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Inaki Larranaga Murgoitio [eu], Khaled Hosny [ar], BM [uz@cyrillic],
|
||||
Milo Casagrande [it], Cheng-Chia Tseng [zh_TW], gogo [hr]
|
||||
|
||||
3.22.0
|
||||
======
|
||||
* Misc. bug fixes [Florian, Rui; #771391, #771536] #771656]
|
||||
|
||||
Contributors:
|
||||
Rui Matos, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Ask Hjorth Larsen [da], GNOME Translation Robot [gd], Alexandre Franke [fr],
|
||||
Daniel Korostil [uk], Jordi Mas [ca], Khaled Hosny [ar], David King [en_GB]
|
||||
|
||||
3.21.92
|
||||
=======
|
||||
* Adjust screen capture to work with multiple stage views [Jonas; #770128]
|
||||
* Improve handling of cycle shortcuts [Florian; #771063]
|
||||
* Fix windows not getting undimmed in some cases [Rui; #770163, #752524]
|
||||
* Disable extension version check by default [Florian; #770887]
|
||||
* Misc. bug fixes [Rui, Florian, Michael; #770382, #770888, #770328]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Michael Catanzaro, Fran Dieguez, Olivier Fourdan, Rui Matos,
|
||||
Florian Müllner
|
||||
|
||||
Translations:
|
||||
Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Aurimas Černius [lt],
|
||||
Muhammet Kara [tr], Trần Ngọc Quân [vi], A S Alam [pa], Yosef Or Boczko [he],
|
||||
Anders Jonsson [sv], Tiago Santos [pt], Hannie Dumoleyn [nl],
|
||||
Rūdolfs Mazurs [lv], Claude Paroz [fr], Arash Mousavi [fa],
|
||||
Fran Dieguez [gl], Stas Solovey [ru], Tom Tryfonidis [el]
|
||||
|
||||
3.21.91
|
||||
=======
|
||||
Translations:
|
||||
Mario Blättermann [de], Jiri Grönroos [fi], Dušan Kazik [sk],
|
||||
Andika Triwidada [id], Daniel Mustieles [es], Fabio Tomat [fur],
|
||||
Enrico Nicoletto [pt_BR], Matej Urbančič [sl], Мирослав Николић [sr, sr@latin]
|
||||
|
||||
3.21.90.1
|
||||
=========
|
||||
Contributors:
|
||||
Piotr Drąg
|
||||
|
||||
Translations:
|
||||
Marek Černocký [cs], Balázs Úr [hu]
|
||||
|
||||
3.21.90
|
||||
=======
|
||||
* Improve on-screen keyboard on wayland [Carlos; #765009]
|
||||
* Misc. bug fixes [Florian; #769156, #769216, #769074]
|
||||
|
||||
Contributors:
|
||||
Carlos Garnacho, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Fabio Tomat [fur], Tiago Santos [pt], Daniel Mustieles [es],
|
||||
Bernd Homuth [de], Aurimas Černius [lt], Balázs Úr [hu],
|
||||
Yosef Or Boczko [he], Jiri Grönroos [fi], Marek Cernocky [cs],
|
||||
Muhammet Kara [tr], Enrico Nicoletto [pt_BR], Andika Triwidada [id]
|
||||
|
||||
3.21.4
|
||||
======
|
||||
* overview: Fix switching workspaces when scrolling on non-primary monitors
|
||||
[Florian; #766883, #768316]
|
||||
* Fix crash when using screen recorder under wayland [Rui; #767001]
|
||||
* Update theme on video memory purge errors [Rui; #739178]
|
||||
* Free old backgrounds immediately [Hyungwon; #766353]
|
||||
* Add support for system upgrades to end session dialog [Kalev; #763611]
|
||||
* Fix maximized windows flickering to the wrong size on restart [Owen; #761566]
|
||||
* Hide ignored events in calendar as well [Florian; #768538]
|
||||
* calendar: Only hide dismissed occurrence of recurring event [Florian; #748226]
|
||||
* Provide org.freedesktop.impl.portal.access implementation [Florian; #768669]
|
||||
* Misc. bug fixes and cleanups [Rui, Florian, Marinus, Jonas; #767954, #768317,
|
||||
#746867, #762206, #768956, #768979]
|
||||
|
||||
Contributors:
|
||||
Jonas Ådahl, Piotr Drąg, Hyungwon Hwang, Kalev Lember, Rui Matos,
|
||||
Florian Müllner, Marinus Schraal, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Andika Triwidada [id], Daniel Mustieles [es], Bruce Cowan [en_GB],
|
||||
Dušan Kazik [sk], Piotr Drąg [pl], Chao-Hsiung Liao [zh_HK]
|
||||
|
||||
3.21.3
|
||||
======
|
||||
* Do not disable suspend action when locked [Florian; #725960]
|
||||
|
22
autogen.sh
22
autogen.sh
@ -4,8 +4,10 @@
|
||||
srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
(test -f $srcdir/configure.ac \
|
||||
&& test -d $srcdir/src) || {
|
||||
pushd $srcdir
|
||||
|
||||
(test -f configure.ac \
|
||||
&& test -d src) || {
|
||||
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
|
||||
echo " top-level gnome-shell directory"
|
||||
exit 1
|
||||
@ -19,9 +21,13 @@ then
|
||||
fi
|
||||
git submodule update
|
||||
|
||||
which gnome-autogen.sh || {
|
||||
echo "You need to install gnome-common from GNOME Git (or from"
|
||||
echo "your OS vendor's package manager)."
|
||||
exit 1
|
||||
}
|
||||
. gnome-autogen.sh
|
||||
aclocal --install || exit 1
|
||||
gtkdocize --copy || exit 1
|
||||
intltoolize --force --copy --automake || exit 1
|
||||
autoreconf --verbose --force --install || exit 1
|
||||
|
||||
popd
|
||||
|
||||
if [ "$NOCONFIGURE" = "" ]; then
|
||||
$srcdir/configure "$@" || exit 1
|
||||
fi
|
||||
|
@ -1029,6 +1029,7 @@ NPP_GetValue(NPP instance,
|
||||
if (!instance->pdata)
|
||||
return NPERR_INVALID_INSTANCE_ERROR;
|
||||
|
||||
funcs.retainobject (instance->pdata);
|
||||
*(NPObject**)value = instance->pdata;
|
||||
break;
|
||||
|
||||
|
49
configure.ac
49
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.21.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.23.92],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AX_IS_RELEASE([git-directory])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@ -17,20 +17,20 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
# Initialize libtool
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT([disable-static])
|
||||
|
||||
# i18n
|
||||
IT_PROG_INTLTOOL([0.40])
|
||||
|
||||
GETTEXT_PACKAGE=gnome-shell
|
||||
AC_SUBST(GETTEXT_PACKAGE)
|
||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||
[The prefix for our gettext translation domains.])
|
||||
|
||||
AM_GNU_GETTEXT_VERSION([0.19.6])
|
||||
AM_GNU_GETTEXT([external])
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.22])
|
||||
|
||||
AC_PATH_PROG([XSLTPROC], [xsltproc])
|
||||
@ -41,6 +41,15 @@ GLIB_GSETTINGS
|
||||
AM_PATH_PYTHON([3])
|
||||
AC_SUBST(PYTHON)
|
||||
|
||||
# We depend on a specific version of the libmutter API. The mutter variants of
|
||||
# the Cogl and Clutter libraries also use this API version.
|
||||
LIBMUTTER_API_VERSION=0
|
||||
|
||||
LIBMUTTER=libmutter-$LIBMUTTER_API_VERSION
|
||||
LIBMUTTER_COGL=mutter-cogl-$LIBMUTTER_API_VERSION
|
||||
LIBMUTTER_COGL_PANGO=mutter-cogl-pango-$LIBMUTTER_API_VERSION
|
||||
LIBMUTTER_CLUTTER=mutter-clutter-$LIBMUTTER_API_VERSION
|
||||
|
||||
# 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.11.92
|
||||
@ -52,7 +61,7 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules mutter-clutter-1.0)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules $LIBMUTTER_CLUTTER)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
@ -74,15 +83,13 @@ AS_IF([test x$enable_systemd != xno], [
|
||||
|
||||
AC_MSG_RESULT($enable_systemd)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.21.5
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=1.45.4
|
||||
GJS_MIN_VERSION=1.39.0
|
||||
MUTTER_MIN_VERSION=3.21.3
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=1.49.1
|
||||
GJS_MIN_VERSION=1.47.0
|
||||
MUTTER_MIN_VERSION=3.23.92
|
||||
GTK_MIN_VERSION=3.15.0
|
||||
GIO_MIN_VERSION=2.45.3
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.17.2
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.7.5
|
||||
@ -95,15 +102,14 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
atk-bridge-2.0
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
gjs-1.0 >= $GJS_MIN_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
mutter-clutter-1.0 >= $CLUTTER_MIN_VERSION
|
||||
mutter-cogl-pango-1.0
|
||||
$LIBMUTTER_CLUTTER >= $MUTTER_MIN_VERSION
|
||||
$LIBMUTTER_COGL_PANGO
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra libcanberra-gtk3
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION
|
||||
gcr-base-3 >= $GCR_MIN_VERSION"
|
||||
if test x$have_systemd = xyes; then
|
||||
@ -111,16 +117,18 @@ if test x$have_systemd = xyes; then
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
|
||||
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(MUTTER, $LIBMUTTER >= $MUTTER_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, mutter-clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, $LIBMUTTER_CLUTTER gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
PKG_CHECK_MODULES(TRAY, mutter-clutter-1.0 gtk+-3.0)
|
||||
PKG_CHECK_MODULES(TRAY, $LIBMUTTER_CLUTTER gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.21.3)
|
||||
|
||||
AC_SUBST(LIBMUTTER_API_VERSION)
|
||||
|
||||
AC_ARG_ENABLE(browser-plugin,
|
||||
[AS_HELP_STRING([--enable-browser-plugin],
|
||||
[Enable browser plugin [default=yes]])],,
|
||||
@ -145,10 +153,10 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir $LIBMUTTER`
|
||||
AC_SUBST(MUTTER_GIR_DIR)
|
||||
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir $LIBMUTTER`
|
||||
AC_SUBST(MUTTER_TYPELIB_DIR)
|
||||
|
||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
||||
@ -254,7 +262,6 @@ AC_CONFIG_FILES([
|
||||
docs/reference/st/Makefile
|
||||
docs/reference/st/st-docs.sgml
|
||||
js/Makefile
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
src/Makefile
|
||||
src/gvc/Makefile
|
||||
browser-plugin/Makefile
|
||||
|
@ -1,24 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="System"
|
||||
name="System"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-message-tray"
|
||||
_description="Show the notification list"/>
|
||||
description="Show the notification list"/>
|
||||
|
||||
<KeyListEntry name="focus-active-notification"
|
||||
_description="Focus the active notification"/>
|
||||
description="Focus the active notification"/>
|
||||
|
||||
<KeyListEntry name="toggle-overview"
|
||||
_description="Show the overview"/>
|
||||
description="Show the overview"/>
|
||||
|
||||
<KeyListEntry name="toggle-application-view"
|
||||
_description="Show all applications"/>
|
||||
description="Show all applications"/>
|
||||
|
||||
<KeyListEntry name="open-application-menu"
|
||||
_description="Open the application menu"/>
|
||||
description="Open the application menu"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
@ -14,8 +14,9 @@ servicedir = $(datadir)/dbus-1/services
|
||||
service_DATA = org.gnome.Shell.PortalHelper.service
|
||||
|
||||
CLEANFILES += \
|
||||
org.gnome.Shell.PortalHelper.service \
|
||||
org.gnome.Shell.PortalHelper.desktop \
|
||||
org.gnome.Shell.PortalHelper.service \
|
||||
org.gnome.Shell.PortalHelper.desktop \
|
||||
org.gnome.Shell.PortalHelper.desktop.in \
|
||||
$(NULL)
|
||||
|
||||
endif
|
||||
@ -31,10 +32,13 @@ endif
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) $(MSGFMT) --desktop --template $(builddir)/$< \
|
||||
-d $(top_srcdir)/po -o $@
|
||||
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = \
|
||||
org.gnome.Shell.PadOsd.xml \
|
||||
org.gnome.Shell.Screencast.xml \
|
||||
org.gnome.Shell.Screenshot.xml \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
@ -60,6 +64,7 @@ dist_theme_files = \
|
||||
theme/gnome-shell-sass/NEWS \
|
||||
theme/gnome-shell-sass/README \
|
||||
theme/gnome-shell-sass/gnome-shell-sass.doap \
|
||||
theme/pad-osd.css \
|
||||
theme/parse-sass.sh \
|
||||
$(NULL)
|
||||
|
||||
@ -83,14 +88,11 @@ perf-background.xml: perf-background.xml.in
|
||||
$< > $@ || rm $@
|
||||
|
||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
keys_in_files = 50-gnome-shell-system.xml.in
|
||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||
keys_DATA = 50-gnome-shell-system.xml
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
|
||||
%.gschema.xml.in: %.gschema.xml.in.in Makefile
|
||||
%.gschema.xml: %.gschema.xml.in Makefile
|
||||
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
|
||||
$< > $@ || rm $@
|
||||
|
||||
@ -109,15 +111,16 @@ convert_DATA = gnome-shell-overrides.convert
|
||||
EXTRA_DIST = \
|
||||
org.gnome.Shell.desktop.in.in \
|
||||
gnome-shell-extension-prefs.desktop.in.in \
|
||||
$(portal_DATA) \
|
||||
$(introspection_DATA) \
|
||||
$(menu_DATA) \
|
||||
$(convert_DATA) \
|
||||
$(keys_in_files) \
|
||||
$(keys_DATA) \
|
||||
$(dist_theme_files) \
|
||||
perf-background.xml.in \
|
||||
org.gnome.Shell.PortalHelper.desktop.in \
|
||||
org.gnome.Shell.PortalHelper.desktop.in.in \
|
||||
org.gnome.Shell.PortalHelper.service.in \
|
||||
org.gnome.shell.gschema.xml.in.in \
|
||||
org.gnome.shell.gschema.xml.in \
|
||||
gnome-shell-theme.gresource.xml \
|
||||
$(resource_files) \
|
||||
$(NULL)
|
||||
@ -126,11 +129,9 @@ CLEANFILES += \
|
||||
org.gnome.Shell.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(keys_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
perf-background.xml \
|
||||
gschemas.compiled \
|
||||
org.gnome.shell.gschema.valid \
|
||||
org.gnome.shell.gschema.xml.in \
|
||||
gnome-shell-theme.gresource \
|
||||
$(NULL)
|
||||
|
@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell Extension Preferences
|
||||
_Comment=Configure GNOME Shell Extensions
|
||||
Name=Shell Extensions
|
||||
Comment=Configure GNOME Shell Extensions
|
||||
Exec=@bindir@/gnome-shell-extension-prefs %u
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
|
@ -22,6 +22,7 @@
|
||||
<file>no-events.svg</file>
|
||||
<file>no-notifications.svg</file>
|
||||
<file>noise-texture.png</file>
|
||||
<file>pad-osd.css</file>
|
||||
<file>page-indicator-active.svg</file>
|
||||
<file>page-indicator-inactive.svg</file>
|
||||
<file>page-indicator-checked.svg</file>
|
||||
|
28
data/org.gnome.Shell.PadOsd.xml
Normal file
28
data/org.gnome.Shell.PadOsd.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
|
||||
<!--
|
||||
org.gnome.Shell.PadOSD:
|
||||
@short_description: Pad OSD interface
|
||||
|
||||
The interface used to show button map OSD on pad devices.
|
||||
-->
|
||||
<interface name='org.gnome.Shell.Wacom.PadOsd'>
|
||||
|
||||
<!--
|
||||
Show:
|
||||
@device_node: device node file, usually in /dev/input/...
|
||||
@edition_mode: whether toggling edition mode on when showing
|
||||
|
||||
Shows the pad button map OSD for the requested device, the OSD
|
||||
will be shown according the current device settings (output
|
||||
mapping, left handed mode, ...)
|
||||
-->
|
||||
<method name='Show'>
|
||||
<arg name='device_node' direction='in' type='o'/>
|
||||
<arg name='edition_mode' direction='in' type='b'/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
@ -1,9 +1,10 @@
|
||||
[Desktop Entry]
|
||||
_Name=Network Login
|
||||
Name=Network Login
|
||||
Type=Application
|
||||
Exec=gapplication launch org.gnome.Shell.PortalHelper
|
||||
DBusActivatable=true
|
||||
NoDisplay=true
|
||||
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
|
||||
Icon=network-workgroup
|
||||
StartupNotify=true
|
||||
OnlyShowIn=GNOME;
|
@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell
|
||||
_Comment=Window management and application launching
|
||||
Name=GNOME Shell
|
||||
Comment=Window management and application launching
|
||||
Exec=@bindir@/gnome-shell
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
|
@ -3,84 +3,92 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="development-tools" type="b">
|
||||
<default>true</default>
|
||||
<_summary>
|
||||
<summary>
|
||||
Enable internal tools useful for developers and testers from Alt-F2
|
||||
</_summary>
|
||||
<_description>
|
||||
</summary>
|
||||
<description>
|
||||
Allows access to internal debugging and monitoring tools
|
||||
using the Alt-F2 dialog.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="enabled-extensions" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>UUIDs of extensions to enable</_summary>
|
||||
<_description>
|
||||
<summary>UUIDs of extensions to enable</summary>
|
||||
<description>
|
||||
GNOME Shell extensions have a UUID property; this key lists extensions
|
||||
which should be loaded. Any extension that wants to be loaded needs
|
||||
to be in this list. You can also manipulate this list with the
|
||||
EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="disable-user-extensions" type="b">
|
||||
<default>false</default>
|
||||
<summary>Disable user extensions</summary>
|
||||
<description>
|
||||
Disable all extensions the user has enabled without affecting
|
||||
the “enabled-extension” setting.
|
||||
</description>
|
||||
</key>
|
||||
<key name="disable-extension-version-validation" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Disables the validation of extension version compatibility</_summary>
|
||||
<_description>
|
||||
<default>true</default>
|
||||
<summary>Disables the validation of extension version compatibility</summary>
|
||||
<description>
|
||||
GNOME Shell will only load extensions that claim to support the current
|
||||
running version. Enabling this option will disable this check and try to
|
||||
load all extensions regardless of the versions they claim to support.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="favorite-apps" type="as">
|
||||
<default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
|
||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||
<_description>
|
||||
<summary>List of desktop file IDs for favorite applications</summary>
|
||||
<description>
|
||||
The applications corresponding to these identifiers
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="app-picker-view" type="u">
|
||||
<default>0</default>
|
||||
<_summary>App Picker View</_summary>
|
||||
<_description>
|
||||
<summary>App Picker View</summary>
|
||||
<description>
|
||||
Index of the currently selected view in the application picker.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
<summary>History for command (Alt-F2) dialog</summary>
|
||||
</key>
|
||||
<key name="looking-glass-history" type="as">
|
||||
<default>[]</default>
|
||||
<!-- Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass -->
|
||||
<_summary>History for the looking glass dialog</_summary>
|
||||
<summary>History for the looking glass dialog</summary>
|
||||
</key>
|
||||
<key name="always-show-log-out" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
|
||||
<_description>
|
||||
This key overrides the automatic hiding of the 'Log out'
|
||||
<summary>Always show the “Log out” menu item in the user menu.</summary>
|
||||
<description>
|
||||
This key overrides the automatic hiding of the “Log out”
|
||||
menu item in single-user, single-session situations.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="remember-mount-password" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
||||
<_description>
|
||||
<summary>Whether to remember password for mounting encrypted or remote filesystems</summary>
|
||||
<description>
|
||||
The shell will request a password when an encrypted device or a
|
||||
remote filesystem is mounted. If the password can be saved for
|
||||
future use a 'Remember Password' checkbox will be present.
|
||||
future use a “Remember Password” checkbox will be present.
|
||||
This key sets the default state of the checkbox.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="had-bluetooth-devices-setup" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Whether the default Bluetooth adapter had set up devices associated to it</_summary>
|
||||
<_description>
|
||||
<summary>Whether the default Bluetooth adapter had set up devices associated to it</summary>
|
||||
<description>
|
||||
The shell will only show a Bluetooth menu item if a Bluetooth
|
||||
adapter is powered, or if there were devices set up associated
|
||||
with the default adapter. This will be reset if the default
|
||||
adapter is ever seen not to have devices associated to it.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||
@ -90,44 +98,44 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="open-application-menu" type="as">
|
||||
<default>["<Super>F10"]</default>
|
||||
<_summary>Keybinding to open the application menu</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to open the application menu</summary>
|
||||
<description>
|
||||
Keybinding to open the application menu.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="toggle-application-view" type="as">
|
||||
<default>["<Super>a"]</default>
|
||||
<_summary>Keybinding to open the "Show Applications" view</_summary>
|
||||
<_description>
|
||||
Keybinding to open the "Show Applications" view of the Activities
|
||||
<summary>Keybinding to open the “Show Applications” view</summary>
|
||||
<description>
|
||||
Keybinding to open the “Show Applications” view of the Activities
|
||||
Overview.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="toggle-overview" type="as">
|
||||
<default>["<Super>s"]</default>
|
||||
<_summary>Keybinding to open the overview</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to open the overview</summary>
|
||||
<description>
|
||||
Keybinding to open the Activities Overview.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="toggle-message-tray" type="as">
|
||||
<default>["<Super>v","<Super>m"]</default>
|
||||
<_summary>Keybinding to toggle the visibility of the notification list</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to toggle the visibility of the notification list</summary>
|
||||
<description>
|
||||
Keybinding to toggle the visibility of the notification list.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="focus-active-notification" type="as">
|
||||
<default>["<Super>n"]</default>
|
||||
<_summary>Keybinding to focus the active notification</_summary>
|
||||
<_description>
|
||||
<summary>Keybinding to focus the active notification</summary>
|
||||
<description>
|
||||
Keybinding to focus the active notification.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
<key name="pause-resume-tweens" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</_summary>
|
||||
<_description></_description>
|
||||
<summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -135,10 +143,10 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="keyboard-type" type="s">
|
||||
<default>'touch'</default>
|
||||
<_summary>Which keyboard to use</_summary>
|
||||
<_description>
|
||||
<summary>Which keyboard to use</summary>
|
||||
<description>
|
||||
The type of keyboard to use.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -147,11 +155,11 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>false</default>
|
||||
<_summary>Limit switcher to current workspace.</_summary>
|
||||
<_description>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
If true, only applications that have windows on the current workspace are shown in the switcher.
|
||||
Otherwise, all applications are included.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -165,20 +173,20 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode">
|
||||
<default>'both'</default>
|
||||
<_summary>The application icon mode.</_summary>
|
||||
<_description>
|
||||
<summary>The application icon mode.</summary>
|
||||
<description>
|
||||
Configures how the windows are shown in the switcher. Valid possibilities
|
||||
are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only'
|
||||
(shows only the application icon) or 'both'.
|
||||
</_description>
|
||||
are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only”
|
||||
(shows only the application icon) or “both”.
|
||||
</description>
|
||||
</key>
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>true</default>
|
||||
<_summary>Limit switcher to current workspace.</_summary>
|
||||
<_description>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
If true, only windows from the current workspace are shown in the switcher.
|
||||
Otherwise, all windows are included.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -186,43 +194,43 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="attach-modal-dialogs" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Attach modal dialog to the parent window</_summary>
|
||||
<_description>
|
||||
<summary>Attach modal dialog to the parent window</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running
|
||||
GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="edge-tiling" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
|
||||
<_description>
|
||||
<summary>Enable edge tiling when dropping windows on screen edges</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="dynamic-workspaces" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Workspaces are managed dynamically</_summary>
|
||||
<_description>
|
||||
<summary>Workspaces are managed dynamically</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="workspaces-only-on-primary" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Workspaces only on primary monitor</_summary>
|
||||
<_description>
|
||||
<summary>Workspaces only on primary monitor</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="focus-change-on-pointer-rest" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Delay focus changes in mouse mode until the pointer stops moving</_summary>
|
||||
<_description>
|
||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
@ -562,6 +562,18 @@ StScrollBar {
|
||||
background-color: #eeeeec;
|
||||
border-radius: 0.3em; }
|
||||
|
||||
/* Pad OSD */
|
||||
.pad-osd-window {
|
||||
padding: 32px;
|
||||
background-color: rgba(0, 0, 0, 0.8); }
|
||||
.pad-osd-window .pad-osd-title-box {
|
||||
spacing: 12px; }
|
||||
.pad-osd-window .pad-osd-title-menu-box {
|
||||
spacing: 6px; }
|
||||
|
||||
.combo-box-label {
|
||||
width: 15em; }
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-popup {
|
||||
padding: 8px;
|
||||
@ -604,6 +616,10 @@ StScrollBar {
|
||||
width: 96px;
|
||||
height: 96px; }
|
||||
|
||||
/* Window Cycler */
|
||||
.cycler-highlight {
|
||||
border: 5px solid #215d9c; }
|
||||
|
||||
/* Workspace Switcher */
|
||||
.workspace-switcher-group {
|
||||
padding: 12px; }
|
||||
@ -722,9 +738,20 @@ StScrollBar {
|
||||
.datemenu-displays-section {
|
||||
padding-bottom: 3em; }
|
||||
|
||||
.datemenu-displays-box {
|
||||
spacing: 1em; }
|
||||
|
||||
.datemenu-calendar-column {
|
||||
border: 0 solid #0d0d0d; }
|
||||
.datemenu-calendar-column:ltr {
|
||||
border-left-width: 1px; }
|
||||
.datemenu-calendar-column:rtl {
|
||||
border-right-width: 1px; }
|
||||
|
||||
.datemenu-today-button,
|
||||
.world-clocks-button,
|
||||
.message-list-section-title {
|
||||
.weather-button,
|
||||
.events-section-title {
|
||||
border-radius: 4px;
|
||||
padding: .4em; }
|
||||
|
||||
@ -737,12 +764,15 @@ StScrollBar {
|
||||
.datemenu-today-button:hover, .datemenu-today-button:focus,
|
||||
.world-clocks-button:hover,
|
||||
.world-clocks-button:focus,
|
||||
.message-list-section-title:hover,
|
||||
.message-list-section-title:focus {
|
||||
.weather-button:hover,
|
||||
.weather-button:focus,
|
||||
.events-section-title:hover,
|
||||
.events-section-title:focus {
|
||||
background-color: #0d0d0d; }
|
||||
.datemenu-today-button:active,
|
||||
.world-clocks-button:active,
|
||||
.message-list-section-title:active {
|
||||
.weather-button:active,
|
||||
.events-section-title:active {
|
||||
color: white;
|
||||
background-color: #215d9c; }
|
||||
|
||||
@ -750,13 +780,17 @@ StScrollBar {
|
||||
font-size: 1.5em; }
|
||||
|
||||
.world-clocks-header,
|
||||
.message-list-section-title {
|
||||
.weather-header,
|
||||
.events-section-title {
|
||||
color: #999999;
|
||||
font-weight: bold; }
|
||||
|
||||
.world-clocks-grid {
|
||||
spacing-rows: 0.4em; }
|
||||
|
||||
.weather-box {
|
||||
spacing: 0.4em; }
|
||||
|
||||
.calendar-month-label {
|
||||
color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
@ -841,69 +875,68 @@ StScrollBar {
|
||||
.message-list {
|
||||
width: 31.5em; }
|
||||
|
||||
.message-list-clear-button.button {
|
||||
background-color: transparent;
|
||||
margin: 1.5em 1.5em 0; }
|
||||
.message-list-clear-button.button:hover, .message-list-clear-button.button:focus {
|
||||
background-color: #0d0d0d; }
|
||||
|
||||
.message-list-sections {
|
||||
spacing: 1.5em; }
|
||||
spacing: 1em; }
|
||||
|
||||
.message-list-section,
|
||||
.message-list-section-list {
|
||||
spacing: 0.7em; }
|
||||
|
||||
.message-list-section-title-box {
|
||||
spacing: 0.4em; }
|
||||
|
||||
.message-list-section-close > StIcon {
|
||||
icon-size: 16px;
|
||||
border-radius: 8px;
|
||||
color: #000;
|
||||
background-color: #666666; }
|
||||
|
||||
/* FIXME: how do you do this in sass? */
|
||||
.message-list-section-close:hover > StIcon,
|
||||
.message-list-section-close:focus > StIcon {
|
||||
background-color: #999999; }
|
||||
|
||||
.message {
|
||||
background-color: #0d0d0d;
|
||||
border-radius: 3px; }
|
||||
.message:hover, .message:focus {
|
||||
background-color: #262626; }
|
||||
background-color: #0d0d0d; }
|
||||
|
||||
.message-icon-bin {
|
||||
padding: 8px 0px 8px 8px; }
|
||||
padding: 10px 3px 10px 10px; }
|
||||
.message-icon-bin:rtl {
|
||||
padding: 8px 8px 8px 0px; }
|
||||
padding: 10px 10px 10px 3px; }
|
||||
|
||||
.message-icon-bin > StIcon {
|
||||
icon-size: 32px; }
|
||||
|
||||
.message-secondary-bin:ltr {
|
||||
padding-left: 8px; }
|
||||
.message-secondary-bin:rtl {
|
||||
padding-right: 8px; }
|
||||
color: #cccccc;
|
||||
icon-size: 16px;
|
||||
-st-icon-style: symbolic; }
|
||||
|
||||
.message-secondary-bin {
|
||||
color: #999999; }
|
||||
padding: 0 12px; }
|
||||
|
||||
.message-secondary-bin > .event-time {
|
||||
color: #999999;
|
||||
font-size: 0.7em;
|
||||
/* HACK: the label should be baseline-aligned with a 1em label,
|
||||
fake this with some bottom padding */
|
||||
padding-bottom: 0.13em; }
|
||||
|
||||
.message-secondary-bin > StIcon {
|
||||
icon-size: 16px; }
|
||||
|
||||
.message-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em; }
|
||||
color: #f2f2f2; }
|
||||
|
||||
.message-content {
|
||||
padding: 8px;
|
||||
font-size: .9em; }
|
||||
color: #cccccc;
|
||||
padding: 10px; }
|
||||
|
||||
.message-media-control {
|
||||
padding: 6px; }
|
||||
padding: 12px;
|
||||
color: #cccccc; }
|
||||
.message-media-control:last-child:ltr {
|
||||
padding-right: 18px; }
|
||||
.message-media-control:last-child:rtl {
|
||||
padding-left: 18px; }
|
||||
.message-media-control:hover {
|
||||
color: #fff; }
|
||||
.message-media-control:insensitive {
|
||||
color: #999999; }
|
||||
|
||||
.media-message-cover-icon {
|
||||
icon-size: 32px; }
|
||||
icon-size: 48px !important; }
|
||||
.media-message-cover-icon.fallback {
|
||||
color: #1a1a1a;
|
||||
background-color: #000;
|
||||
|
Submodule data/theme/gnome-shell-sass updated: 7ab2789464...e94bce1fcf
@ -562,6 +562,18 @@ StScrollBar {
|
||||
background-color: #eeeeec;
|
||||
border-radius: 0.3em; }
|
||||
|
||||
/* Pad OSD */
|
||||
.pad-osd-window {
|
||||
padding: 32px;
|
||||
background-color: rgba(0, 0, 0, 0.8); }
|
||||
.pad-osd-window .pad-osd-title-box {
|
||||
spacing: 12px; }
|
||||
.pad-osd-window .pad-osd-title-menu-box {
|
||||
spacing: 6px; }
|
||||
|
||||
.combo-box-label {
|
||||
width: 15em; }
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-popup {
|
||||
padding: 8px;
|
||||
@ -604,6 +616,10 @@ StScrollBar {
|
||||
width: 96px;
|
||||
height: 96px; }
|
||||
|
||||
/* Window Cycler */
|
||||
.cycler-highlight {
|
||||
border: 5px solid #215d9c; }
|
||||
|
||||
/* Workspace Switcher */
|
||||
.workspace-switcher-group {
|
||||
padding: 12px; }
|
||||
@ -722,9 +738,20 @@ StScrollBar {
|
||||
.datemenu-displays-section {
|
||||
padding-bottom: 3em; }
|
||||
|
||||
.datemenu-displays-box {
|
||||
spacing: 1em; }
|
||||
|
||||
.datemenu-calendar-column {
|
||||
border: 0 solid #454c4c; }
|
||||
.datemenu-calendar-column:ltr {
|
||||
border-left-width: 1px; }
|
||||
.datemenu-calendar-column:rtl {
|
||||
border-right-width: 1px; }
|
||||
|
||||
.datemenu-today-button,
|
||||
.world-clocks-button,
|
||||
.message-list-section-title {
|
||||
.weather-button,
|
||||
.events-section-title {
|
||||
border-radius: 4px;
|
||||
padding: .4em; }
|
||||
|
||||
@ -737,12 +764,15 @@ StScrollBar {
|
||||
.datemenu-today-button:hover, .datemenu-today-button:focus,
|
||||
.world-clocks-button:hover,
|
||||
.world-clocks-button:focus,
|
||||
.message-list-section-title:hover,
|
||||
.message-list-section-title:focus {
|
||||
.weather-button:hover,
|
||||
.weather-button:focus,
|
||||
.events-section-title:hover,
|
||||
.events-section-title:focus {
|
||||
background-color: #454c4c; }
|
||||
.datemenu-today-button:active,
|
||||
.world-clocks-button:active,
|
||||
.message-list-section-title:active {
|
||||
.weather-button:active,
|
||||
.events-section-title:active {
|
||||
color: white;
|
||||
background-color: #215d9c; }
|
||||
|
||||
@ -750,13 +780,17 @@ StScrollBar {
|
||||
font-size: 1.5em; }
|
||||
|
||||
.world-clocks-header,
|
||||
.message-list-section-title {
|
||||
.weather-header,
|
||||
.events-section-title {
|
||||
color: #8e8e80;
|
||||
font-weight: bold; }
|
||||
|
||||
.world-clocks-grid {
|
||||
spacing-rows: 0.4em; }
|
||||
|
||||
.weather-box {
|
||||
spacing: 0.4em; }
|
||||
|
||||
.calendar-month-label {
|
||||
color: #e2e2df;
|
||||
font-weight: bold;
|
||||
@ -841,69 +875,68 @@ StScrollBar {
|
||||
.message-list {
|
||||
width: 31.5em; }
|
||||
|
||||
.message-list-clear-button.button {
|
||||
background-color: transparent;
|
||||
margin: 1.5em 1.5em 0; }
|
||||
.message-list-clear-button.button:hover, .message-list-clear-button.button:focus {
|
||||
background-color: #454c4c; }
|
||||
|
||||
.message-list-sections {
|
||||
spacing: 1.5em; }
|
||||
spacing: 1em; }
|
||||
|
||||
.message-list-section,
|
||||
.message-list-section-list {
|
||||
spacing: 0.7em; }
|
||||
|
||||
.message-list-section-title-box {
|
||||
spacing: 0.4em; }
|
||||
|
||||
.message-list-section-close > StIcon {
|
||||
icon-size: 16px;
|
||||
border-radius: 8px;
|
||||
color: #393f3f;
|
||||
background-color: #59594f; }
|
||||
|
||||
/* FIXME: how do you do this in sass? */
|
||||
.message-list-section-close:hover > StIcon,
|
||||
.message-list-section-close:focus > StIcon {
|
||||
background-color: #8e8e80; }
|
||||
|
||||
.message {
|
||||
background-color: #454c4c;
|
||||
border-radius: 3px; }
|
||||
.message:hover, .message:focus {
|
||||
background-color: #5d6767; }
|
||||
background-color: #454c4c; }
|
||||
|
||||
.message-icon-bin {
|
||||
padding: 8px 0px 8px 8px; }
|
||||
padding: 10px 3px 10px 10px; }
|
||||
.message-icon-bin:rtl {
|
||||
padding: 8px 8px 8px 0px; }
|
||||
padding: 10px 10px 10px 3px; }
|
||||
|
||||
.message-icon-bin > StIcon {
|
||||
icon-size: 32px; }
|
||||
|
||||
.message-secondary-bin:ltr {
|
||||
padding-left: 8px; }
|
||||
.message-secondary-bin:rtl {
|
||||
padding-right: 8px; }
|
||||
color: #bebeb6;
|
||||
icon-size: 16px;
|
||||
-st-icon-style: symbolic; }
|
||||
|
||||
.message-secondary-bin {
|
||||
color: #8e8e80; }
|
||||
padding: 0 12px; }
|
||||
|
||||
.message-secondary-bin > .event-time {
|
||||
color: #8e8e80;
|
||||
font-size: 0.7em;
|
||||
/* HACK: the label should be baseline-aligned with a 1em label,
|
||||
fake this with some bottom padding */
|
||||
padding-bottom: 0.13em; }
|
||||
|
||||
.message-secondary-bin > StIcon {
|
||||
icon-size: 16px; }
|
||||
|
||||
.message-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em; }
|
||||
color: #e2e2df; }
|
||||
|
||||
.message-content {
|
||||
padding: 8px;
|
||||
font-size: .9em; }
|
||||
color: #bebeb6;
|
||||
padding: 10px; }
|
||||
|
||||
.message-media-control {
|
||||
padding: 6px; }
|
||||
padding: 12px;
|
||||
color: #bebeb6; }
|
||||
.message-media-control:last-child:ltr {
|
||||
padding-right: 18px; }
|
||||
.message-media-control:last-child:rtl {
|
||||
padding-left: 18px; }
|
||||
.message-media-control:hover {
|
||||
color: #eeeeec; }
|
||||
.message-media-control:insensitive {
|
||||
color: #8e8e80; }
|
||||
|
||||
.media-message-cover-icon {
|
||||
icon-size: 32px; }
|
||||
icon-size: 48px !important; }
|
||||
.media-message-cover-icon.fallback {
|
||||
color: #515a5a;
|
||||
background-color: #393f3f;
|
||||
|
30
data/theme/pad-osd.css
Normal file
30
data/theme/pad-osd.css
Normal file
@ -0,0 +1,30 @@
|
||||
.Leader {
|
||||
stroke-width: .5 !important;
|
||||
stroke: #535353;
|
||||
fill: none !important;
|
||||
}
|
||||
|
||||
.Button {
|
||||
stroke-width: .25;
|
||||
stroke: #ededed;
|
||||
fill: #ededed;
|
||||
}
|
||||
|
||||
.Ring {
|
||||
stroke-width: .5 !important;
|
||||
stroke: #535353 !important;
|
||||
fill: none !important;
|
||||
}
|
||||
|
||||
.Label {
|
||||
stroke: none !important;
|
||||
stroke-width: .1 !important;
|
||||
font-size: .1 !important;
|
||||
fill: transparent !important;
|
||||
}
|
||||
|
||||
.TouchStrip, .TouchRing {
|
||||
stroke-width: .1 !important;
|
||||
stroke: #ededed !important;
|
||||
fill: #535353 !important;
|
||||
}
|
@ -50,7 +50,6 @@
|
||||
<xi:include href="xml/shell-util.xml"/>
|
||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
||||
<xi:include href="xml/shell-tp-client.xml"/>
|
||||
</chapter>
|
||||
<chapter id="object-tree">
|
||||
<title>Object Hierarchy</title>
|
||||
|
@ -11,6 +11,7 @@ misc/config.js: misc/config.js.in Makefile
|
||||
-e "s|[@]datadir@|$(datadir)|g" \
|
||||
-e "s|[@]libexecdir@|$(libexecdir)|g" \
|
||||
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \
|
||||
-e "s|[@]LIBMUTTER_API_VERSION@|$(LIBMUTTER_API_VERSION)|g" \
|
||||
$< > $@
|
||||
|
||||
js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate-dependencies $(srcdir)/js-resources.gresource.xml)
|
||||
|
@ -143,16 +143,21 @@ const Application = new Lang.Class({
|
||||
this._window = new Gtk.ApplicationWindow({ application: app,
|
||||
window_position: Gtk.WindowPosition.CENTER });
|
||||
|
||||
this._window.set_size_request(800, 500);
|
||||
this._window.set_default_size(800, 500);
|
||||
|
||||
this._titlebar = new Gtk.HeaderBar({ show_close_button: true,
|
||||
title: _("GNOME Shell Extensions") });
|
||||
title: _("Shell Extensions") });
|
||||
this._window.set_titlebar(this._titlebar);
|
||||
|
||||
let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
shadow_type: Gtk.ShadowType.IN,
|
||||
halign: Gtk.Align.CENTER,
|
||||
margin: 18 });
|
||||
let killSwitch = new Gtk.Switch({ valign: Gtk.Align.CENTER });
|
||||
this._titlebar.pack_end(killSwitch);
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
this._settings.bind('disable-user-extensions', killSwitch, 'active',
|
||||
Gio.SettingsBindFlags.BIND_DEFAULT |
|
||||
Gio.SettingsBindFlags.INVERT_BOOLEAN);
|
||||
|
||||
let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
this._window.add(scroll);
|
||||
|
||||
this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE });
|
||||
@ -245,6 +250,18 @@ const Application = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const DescriptionLabel = new Lang.Class({
|
||||
Name: 'DescriptionLabel',
|
||||
Extends: Gtk.Label,
|
||||
|
||||
vfunc_get_preferred_height_for_width: function(width) {
|
||||
// Hack: Request the maximum height allowed by the line limit
|
||||
if (this.lines > 0)
|
||||
return this.parent(0);
|
||||
return this.parent(width);
|
||||
}
|
||||
});
|
||||
|
||||
const ExtensionRow = new Lang.Class({
|
||||
Name: 'ExtensionRow',
|
||||
Extends: Gtk.ListBoxRow,
|
||||
@ -263,6 +280,10 @@ const ExtensionRow = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._switch.sensitive = this._canEnable();
|
||||
}));
|
||||
this._settings.connect('changed::disable-user-extensions',
|
||||
Lang.bind(this, function() {
|
||||
this._switch.sensitive = this._canEnable();
|
||||
}));
|
||||
|
||||
this._buildUI();
|
||||
},
|
||||
@ -271,7 +292,8 @@ const ExtensionRow = new Lang.Class({
|
||||
let extension = ExtensionUtils.extensions[this.uuid];
|
||||
|
||||
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
|
||||
hexpand: true, margin: 12, spacing: 6 });
|
||||
hexpand: true, margin_end: 24, spacing: 24,
|
||||
margin: 12 });
|
||||
this.add(hbox);
|
||||
|
||||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
|
||||
@ -285,9 +307,9 @@ const ExtensionRow = new Lang.Class({
|
||||
vbox.add(label);
|
||||
|
||||
let desc = extension.metadata.description.split('\n')[0];
|
||||
label = new Gtk.Label({ label: desc,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
halign: Gtk.Align.START });
|
||||
label = new DescriptionLabel({ label: desc, wrap: true, lines: 2,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
xalign: 0, yalign: 0 });
|
||||
vbox.add(label);
|
||||
|
||||
let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
|
||||
@ -318,7 +340,8 @@ const ExtensionRow = new Lang.Class({
|
||||
let extension = ExtensionUtils.extensions[this.uuid];
|
||||
let checkVersion = !this._settings.get_boolean('disable-extension-version-validation');
|
||||
|
||||
return !(checkVersion && ExtensionUtils.isOutOfDate(extension));
|
||||
return !this._settings.get_boolean('disable-user-extensions') &&
|
||||
!(checkVersion && ExtensionUtils.isOutOfDate(extension));
|
||||
},
|
||||
|
||||
_isEnabled: function() {
|
||||
|
@ -13,6 +13,7 @@ const Params = imports.misc.params;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
const Pango = imports.gi.Pango;
|
||||
|
||||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 16;
|
||||
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
|
||||
@ -113,6 +114,7 @@ const AuthPrompt = new Lang.Class({
|
||||
this._message = new St.Label({ opacity: 0,
|
||||
styleClass: 'login-dialog-message' });
|
||||
this._message.clutter_text.line_wrap = true;
|
||||
this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
|
||||
|
||||
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
|
||||
|
@ -804,6 +804,11 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
this._user = null;
|
||||
|
||||
if (this._nextSignalId) {
|
||||
this._authPrompt.disconnect(this._nextSignalId);
|
||||
this._nextSignalId = 0;
|
||||
}
|
||||
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
if (!this._disableUserList)
|
||||
this._showUserList();
|
||||
@ -1220,7 +1225,7 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._authPrompt.addCharacter(unichar);
|
||||
// Don't allow type ahead at the login screen
|
||||
},
|
||||
|
||||
finish: function(onComplete) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
<file>misc/params.js</file>
|
||||
<file>misc/smartcardManager.js</file>
|
||||
<file>misc/util.js</file>
|
||||
<file>misc/weather.js</file>
|
||||
|
||||
<file>perf/core.js</file>
|
||||
<file>perf/hwtest.js</file>
|
||||
@ -72,6 +73,7 @@
|
||||
<file>ui/osdMonitorLabeler.js</file>
|
||||
<file>ui/overview.js</file>
|
||||
<file>ui/overviewControls.js</file>
|
||||
<file>ui/padOsd.js</file>
|
||||
<file>ui/panel.js</file>
|
||||
<file>ui/panelMenu.js</file>
|
||||
<file>ui/pointerWatcher.js</file>
|
||||
@ -116,6 +118,7 @@
|
||||
<file>ui/status/brightness.js</file>
|
||||
<file>ui/status/location.js</file>
|
||||
<file>ui/status/keyboard.js</file>
|
||||
<file>ui/status/nightLight.js</file>
|
||||
<file>ui/status/network.js</file>
|
||||
<file>ui/status/power.js</file>
|
||||
<file>ui/status/rfkill.js</file>
|
||||
|
@ -15,3 +15,5 @@ const LOCALEDIR = '@datadir@/locale';
|
||||
/* other standard directories */
|
||||
const LIBEXECDIR = '@libexecdir@';
|
||||
const SYSCONFDIR = '@sysconfdir@';
|
||||
/* g-i package versions */
|
||||
const LIBMUTTER_API_VERSION = '@LIBMUTTER_API_VERSION@'
|
||||
|
@ -6,9 +6,7 @@
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const ShellJS = imports.gi.ShellJS;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
@ -21,14 +19,25 @@ const ExtensionType = {
|
||||
// Maps uuid -> metadata object
|
||||
const extensions = {};
|
||||
|
||||
/**
|
||||
* getCurrentExtension:
|
||||
*
|
||||
* Returns the current extension, or null if not called from an extension.
|
||||
*/
|
||||
function getCurrentExtension() {
|
||||
let stack = (new Error()).stack;
|
||||
let stack = (new Error()).stack.split('\n');
|
||||
let extensionStackLine;
|
||||
|
||||
// Assuming we're importing this directly from an extension (and we shouldn't
|
||||
// ever not be), its UUID should be directly in the path here.
|
||||
let extensionStackLine = stack.split('\n')[1];
|
||||
// Search for an occurrence of an extension stack frame
|
||||
// Start at 1 because 0 is the stack frame of this function
|
||||
for (let i = 1; i < stack.length; i++) {
|
||||
if (stack[i].indexOf('/gnome-shell/extensions/') > -1) {
|
||||
extensionStackLine = stack[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!extensionStackLine)
|
||||
throw new Error('Could not find current extension');
|
||||
return null;
|
||||
|
||||
// The stack line is like:
|
||||
// init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
|
||||
@ -38,7 +47,7 @@ function getCurrentExtension() {
|
||||
// @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
|
||||
let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
|
||||
if (!match)
|
||||
throw new Error('Could not find current extension');
|
||||
return null;
|
||||
|
||||
let path = match[1];
|
||||
let file = Gio.File.new_for_path(path);
|
||||
@ -52,7 +61,7 @@ function getCurrentExtension() {
|
||||
file = file.get_parent();
|
||||
}
|
||||
|
||||
throw new Error('Could not find current extension');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,12 +149,13 @@ function createExtensionObject(uuid, dir, type) {
|
||||
return extension;
|
||||
}
|
||||
|
||||
var _extension = null;
|
||||
|
||||
function installImporter(extension) {
|
||||
_extension = extension;
|
||||
ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path);
|
||||
_extension = null;
|
||||
let oldSearchPath = imports.searchPath.slice(); // make a copy
|
||||
imports.searchPath = [extension.dir.get_parent().get_path()];
|
||||
// importing a "subdir" creates a new importer object that doesn't affect
|
||||
// the global one
|
||||
extension.imports = imports[extension.uuid];
|
||||
imports.searchPath = oldSearchPath;
|
||||
}
|
||||
|
||||
const ExtensionFinder = new Lang.Class({
|
||||
|
@ -6,10 +6,11 @@ const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
let IBusCandidatePopup;
|
||||
try {
|
||||
var IBus = imports.gi.IBus;
|
||||
_checkIBusVersion(1, 5, 2);
|
||||
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
|
||||
IBusCandidatePopup = imports.ui.ibusCandidatePopup;
|
||||
} catch (e) {
|
||||
var IBus = null;
|
||||
log(e);
|
||||
|
@ -128,7 +128,8 @@ const KeyboardManager = new Lang.Class({
|
||||
if (!found)
|
||||
[, , id] = GnomeDesktop.get_input_source_from_locale(DEFAULT_LOCALE);
|
||||
|
||||
let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
|
||||
let _layout, _variant;
|
||||
[found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
|
||||
if (found)
|
||||
return { layout: _layout, variant: _variant };
|
||||
else
|
||||
|
131
js/misc/util.js
131
js/misc/util.js
@ -1,9 +1,12 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gettext = imports.gettext;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
@ -94,7 +97,7 @@ function spawnApp(argv) {
|
||||
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
|
||||
|
||||
let context = global.create_app_launch_context(0, -1);
|
||||
app.launch([], context);
|
||||
app.launch([], context, false);
|
||||
} catch(err) {
|
||||
_handleSpawnError(argv[0], err);
|
||||
}
|
||||
@ -161,6 +164,41 @@ function _handleSpawnError(command, err) {
|
||||
Main.notifyError(title, err.message);
|
||||
}
|
||||
|
||||
function formatTimeSpan(date) {
|
||||
let now = GLib.DateTime.new_now_local();
|
||||
|
||||
let timespan = now.difference(date);
|
||||
|
||||
let minutesAgo = timespan / GLib.TIME_SPAN_MINUTE;
|
||||
let hoursAgo = timespan / GLib.TIME_SPAN_HOUR;
|
||||
let daysAgo = timespan / GLib.TIME_SPAN_DAY;
|
||||
let weeksAgo = daysAgo / 7;
|
||||
let monthsAgo = daysAgo / 30;
|
||||
let yearsAgo = weeksAgo / 52;
|
||||
|
||||
if (minutesAgo < 5)
|
||||
return _("Just now");
|
||||
if (hoursAgo < 1)
|
||||
return Gettext.ngettext("%d minute ago",
|
||||
"%d minutes ago", minutesAgo).format(minutesAgo);
|
||||
if (daysAgo < 1)
|
||||
return Gettext.ngettext("%d hour ago",
|
||||
"%d hours ago", hoursAgo).format(hoursAgo);
|
||||
if (daysAgo < 2)
|
||||
return _("Yesterday");
|
||||
if (daysAgo < 15)
|
||||
return Gettext.ngettext("%d day ago",
|
||||
"%d days ago", daysAgo).format(daysAgo);
|
||||
if (weeksAgo < 8)
|
||||
return Gettext.ngettext("%d week ago",
|
||||
"%d weeks ago", weeksAgo).format(weeksAgo);
|
||||
if (yearsAgo < 1)
|
||||
return Gettext.ngettext("%d month ago",
|
||||
"%d months ago", monthsAgo).format(monthsAgo);
|
||||
return Gettext.ngettext("%d year ago",
|
||||
"%d years ago", yearsAgo).format(yearsAgo);
|
||||
}
|
||||
|
||||
function formatTime(time, params) {
|
||||
let date;
|
||||
// HACK: The built-in Date type sucks at timezones, which we need for the
|
||||
@ -398,3 +436,94 @@ function ensureActorVisibleInScrollView(scrollView, actor) {
|
||||
time: SCROLL_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
||||
const AppSettingsMonitor = new Lang.Class({
|
||||
Name: 'AppSettingsMonitor',
|
||||
|
||||
_init: function(appId, schemaId) {
|
||||
this._appId = appId;
|
||||
this._schemaId = schemaId;
|
||||
|
||||
this._app = null;
|
||||
this._settings = null;
|
||||
this._handlers = [];
|
||||
|
||||
this._schemaSource = Gio.SettingsSchemaSource.get_default();
|
||||
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appSystem.connect('installed-changed',
|
||||
Lang.bind(this, this._onInstalledChanged));
|
||||
this._onInstalledChanged();
|
||||
},
|
||||
|
||||
get available() {
|
||||
return this._app != null && this._settings != null;
|
||||
},
|
||||
|
||||
activateApp: function() {
|
||||
if (this._app)
|
||||
this._app.activate();
|
||||
},
|
||||
|
||||
watchSetting: function(key, callback) {
|
||||
let handler = { id: 0, key: key, callback: callback };
|
||||
this._handlers.push(handler);
|
||||
|
||||
this._connectHandler(handler);
|
||||
},
|
||||
|
||||
_connectHandler: function(handler) {
|
||||
if (!this._settings || handler.id > 0)
|
||||
return;
|
||||
|
||||
handler.id = this._settings.connect('changed::' + handler.key,
|
||||
handler.callback);
|
||||
handler.callback(this._settings, handler.key);
|
||||
},
|
||||
|
||||
_disconnectHandler: function(handler) {
|
||||
if (this._settings && handler.id > 0)
|
||||
this._settings.disconnect(handler.id);
|
||||
handler.id = 0;
|
||||
},
|
||||
|
||||
_onInstalledChanged: function() {
|
||||
let hadApp = (this._app != null);
|
||||
this._app = this._appSystem.lookup_app(this._appId);
|
||||
let haveApp = (this._app != null);
|
||||
|
||||
if (hadApp == haveApp)
|
||||
return;
|
||||
|
||||
if (haveApp)
|
||||
this._checkSettings();
|
||||
else
|
||||
this._setSettings(null);
|
||||
},
|
||||
|
||||
_setSettings: function(settings) {
|
||||
this._handlers.forEach((handler) => { this._disconnectHandler(handler); });
|
||||
|
||||
let hadSettings = (this._settings != null);
|
||||
this._settings = settings;
|
||||
let haveSettings = (this._settings != null);
|
||||
|
||||
this._handlers.forEach((handler) => { this._connectHandler(handler); });
|
||||
|
||||
if (hadSettings != haveSettings)
|
||||
this.emit('available-changed');
|
||||
},
|
||||
|
||||
_checkSettings: function() {
|
||||
let schema = this._schemaSource.lookup(this._schemaId, true);
|
||||
if (schema) {
|
||||
this._setSettings(new Gio.Settings({ settings_schema: schema }));
|
||||
} else if (this._app) {
|
||||
Mainloop.timeout_add_seconds(1, () => {
|
||||
this._checkSettings();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AppSettingsMonitor.prototype);
|
||||
|
188
js/misc/weather.js
Normal file
188
js/misc/weather.js
Normal file
@ -0,0 +1,188 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Geoclue = imports.gi.Geoclue;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GWeather = imports.gi.GWeather;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Util = imports.misc.util;
|
||||
|
||||
// Minimum time between updates to show loading indication
|
||||
const UPDATE_THRESHOLD = 10 * GLib.TIME_SPAN_MINUTE;
|
||||
|
||||
const WeatherClient = new Lang.Class({
|
||||
Name: 'WeatherClient',
|
||||
|
||||
_init: function() {
|
||||
this._loading = false;
|
||||
this._lastUpdate = GLib.DateTime.new_from_unix_local(0);
|
||||
|
||||
this._useAutoLocation = false;
|
||||
this._mostRecentLocation = null;
|
||||
|
||||
this._gclueService = null;
|
||||
this._gclueStarted = false;
|
||||
this._gclueStarting = false;
|
||||
this._gclueLocationChangedId = 0;
|
||||
|
||||
this._world = GWeather.Location.get_world();
|
||||
|
||||
let providers = GWeather.Provider.METAR |
|
||||
GWeather.Provider.YR_NO |
|
||||
GWeather.Provider.OWM;
|
||||
this._weatherInfo = new GWeather.Info({ enabled_providers: providers });
|
||||
this._weatherInfo.connect_after('updated', () => {
|
||||
this._lastUpdate = GLib.DateTime.new_now_local();
|
||||
this.emit('changed');
|
||||
});
|
||||
|
||||
this._weatherAppMon = new Util.AppSettingsMonitor('org.gnome.Weather.Application.desktop',
|
||||
'org.gnome.Weather.Application');
|
||||
this._weatherAppMon.connect('available-changed', () => { this.emit('changed'); });
|
||||
this._weatherAppMon.watchSetting('automatic-location',
|
||||
Lang.bind(this, this._onAutomaticLocationChanged));
|
||||
this._weatherAppMon.watchSetting('locations',
|
||||
Lang.bind(this, this._onLocationsChanged));
|
||||
},
|
||||
|
||||
get available() {
|
||||
return this._weatherAppMon.available;
|
||||
},
|
||||
|
||||
get loading() {
|
||||
return this._loading;
|
||||
},
|
||||
|
||||
get info() {
|
||||
return this._weatherInfo;
|
||||
},
|
||||
|
||||
activateApp: function() {
|
||||
this._weatherAppMon.activateApp();
|
||||
},
|
||||
|
||||
update: function() {
|
||||
let now = GLib.DateTime.new_now_local();
|
||||
// Update without loading indication if the current info is recent enough
|
||||
if (this._weatherInfo.is_valid() &&
|
||||
now.difference(this._lastUpdate) < UPDATE_THRESHOLD)
|
||||
this._weatherInfo.update();
|
||||
else
|
||||
this._loadInfo();
|
||||
},
|
||||
|
||||
_loadInfo: function() {
|
||||
let id = this._weatherInfo.connect('updated', () => {
|
||||
this._weatherInfo.disconnect(id);
|
||||
this._loading = false;
|
||||
});
|
||||
|
||||
this._loading = true;
|
||||
this.emit('changed');
|
||||
|
||||
this._weatherInfo.update();
|
||||
},
|
||||
|
||||
_locationsEqual: function(loc1, loc2) {
|
||||
if (loc1 == loc2)
|
||||
return true;
|
||||
|
||||
if (loc1 == null || loc2 == null)
|
||||
return false;
|
||||
|
||||
return loc1.equal(loc2);
|
||||
},
|
||||
|
||||
_setLocation: function(location) {
|
||||
if (this._locationsEqual(this._weatherInfo.location, location))
|
||||
return;
|
||||
|
||||
this._weatherInfo.abort();
|
||||
this._weatherInfo.set_location(location);
|
||||
|
||||
if (location)
|
||||
this._loadInfo();
|
||||
else
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_updateLocationMonitoring: function() {
|
||||
if (this._useAutoLocation) {
|
||||
if (this._gclueLocationChangedId != 0 || this._gclueService == null)
|
||||
return;
|
||||
|
||||
this._gclueLocationChangedId =
|
||||
this._gclueService.connect('notify::location',
|
||||
Lang.bind(this, this._onGClueLocationChanged));
|
||||
this._onGClueLocationChanged();
|
||||
} else {
|
||||
if (this._gclueLocationChangedId)
|
||||
this._gclueService.disconnect(this._gclueLocationChangedId);
|
||||
this._gclueLocationChangedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_startGClueService: function() {
|
||||
if (this._gclueStarting)
|
||||
return;
|
||||
|
||||
this._gclueStarting = true;
|
||||
|
||||
Geoclue.Simple.new('org.gnome.Shell', Geoclue.AccuracyLevel.CITY, null,
|
||||
(o, res) => {
|
||||
try {
|
||||
this._gclueService = Geoclue.Simple.new_finish(res);
|
||||
} catch(e) {
|
||||
log('Failed to connect to Geoclue2 service: ' + e.message);
|
||||
this._setLocation(this._mostRecentLocation);
|
||||
return;
|
||||
}
|
||||
this._gclueStarted = true;
|
||||
this._gclueService.get_client().distance_threshold = 100;
|
||||
this._updateLocationMonitoring();
|
||||
});
|
||||
},
|
||||
|
||||
_onGClueLocationChanged: function() {
|
||||
let geoLocation = this._gclueService.location;
|
||||
let location = GWeather.Location.new_detached(geoLocation.description,
|
||||
null,
|
||||
geoLocation.latitude,
|
||||
geoLocation.longitude);
|
||||
this._setLocation(location);
|
||||
},
|
||||
|
||||
_onAutomaticLocationChanged: function(settings, key) {
|
||||
let useAutoLocation = settings.get_boolean(key);
|
||||
if (this._useAutoLocation == useAutoLocation)
|
||||
return;
|
||||
|
||||
this._useAutoLocation = useAutoLocation;
|
||||
|
||||
this._updateLocationMonitoring();
|
||||
|
||||
if (this._useAutoLocation)
|
||||
this._startGClueService();
|
||||
else
|
||||
this._setLocation(this._mostRecentLocation);
|
||||
},
|
||||
|
||||
_onLocationsChanged: function(settings, key) {
|
||||
let serialized = settings.get_value(key).deep_unpack().shift();
|
||||
let mostRecentLocation = null;
|
||||
|
||||
if (serialized)
|
||||
mostRecentLocation = this._world.deserialize(serialized);
|
||||
|
||||
if (this._locationsEqual(this._mostRecentLocation, mostRecentLocation))
|
||||
return;
|
||||
|
||||
this._mostRecentLocation = mostRecentLocation;
|
||||
|
||||
if (!this._useAutoLocation || !this._gclueStarted)
|
||||
this._setLocation(this._mostRecentLocation);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(WeatherClient.prototype);
|
@ -12,6 +12,7 @@ const WebKit = imports.gi.WebKit2;
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
|
||||
const PortalHelperResult = {
|
||||
CANCELLED: 0,
|
||||
@ -19,7 +20,15 @@ const PortalHelperResult = {
|
||||
RECHECK: 2
|
||||
};
|
||||
|
||||
const PortalHelperSecurityLevel = {
|
||||
NOT_YET_DETERMINED: 0,
|
||||
SECURE: 1,
|
||||
INSECURE: 2
|
||||
};
|
||||
|
||||
const INACTIVITY_TIMEOUT = 30000; //ms
|
||||
const CONNECTIVITY_CHECK_HOST = 'nmcheck.gnome.org';
|
||||
const CONNECTIVITY_CHECK_URI = 'http://' + CONNECTIVITY_CHECK_HOST;
|
||||
const CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT = 30 * GLib.USEC_PER_SEC;
|
||||
|
||||
const HelperDBusInterface = '<node> \
|
||||
@ -42,6 +51,71 @@ const HelperDBusInterface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PortalHeaderBar = new Lang.Class({
|
||||
Name: 'PortalHeaderBar',
|
||||
Extends: Gtk.HeaderBar,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ show_close_button: true });
|
||||
|
||||
// See ephy-title-box.c in epiphany for the layout
|
||||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
|
||||
spacing: 0 });
|
||||
this.set_custom_title(vbox);
|
||||
|
||||
/* TRANSLATORS: this is the title of the wifi captive portal login window */
|
||||
let titleLabel = new Gtk.Label({ label: _("Hotspot Login"),
|
||||
wrap: false,
|
||||
single_line_mode: true,
|
||||
ellipsize: Pango.EllipsizeMode.END });
|
||||
titleLabel.get_style_context().add_class('title');
|
||||
vbox.add(titleLabel);
|
||||
|
||||
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
|
||||
spacing: 4,
|
||||
halign: Gtk.Align.CENTER,
|
||||
valign: Gtk.Align.BASELINE });
|
||||
hbox.get_style_context().add_class('subtitle');
|
||||
vbox.add(hbox);
|
||||
|
||||
this._lockImage = new Gtk.Image({ icon_size: Gtk.IconSize.MENU,
|
||||
valign: Gtk.Align.BASELINE });
|
||||
hbox.add(this._lockImage);
|
||||
|
||||
this.subtitleLabel = new Gtk.Label({ wrap: false,
|
||||
single_line_mode: true,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
valign: Gtk.Align.BASELINE,
|
||||
selectable: true});
|
||||
this.subtitleLabel.get_style_context().add_class('subtitle');
|
||||
hbox.add(this.subtitleLabel);
|
||||
|
||||
vbox.show_all();
|
||||
},
|
||||
|
||||
setSubtitle: function(label) {
|
||||
this.subtitleLabel.set_text(label);
|
||||
},
|
||||
|
||||
setSecurityIcon: function(securityLevel) {
|
||||
switch (securityLevel) {
|
||||
case PortalHelperSecurityLevel.NOT_YET_DETERMINED:
|
||||
this._lockImage.hide();
|
||||
break;
|
||||
case PortalHelperSecurityLevel.SECURE:
|
||||
this._lockImage.show();
|
||||
this._lockImage.set_from_icon_name("channel-secure-symbolic", Gtk.IconSize.MENU);
|
||||
this._lockImage.set_tooltip_text(null);
|
||||
break;
|
||||
case PortalHelperSecurityLevel.INSECURE:
|
||||
this._lockImage.show();
|
||||
this._lockImage.set_from_icon_name("channel-insecure-symbolic", Gtk.IconSize.MENU);
|
||||
this._lockImage.set_tooltip_text(_('Your connection to this hotspot login is not secure. Passwords or other information you enter on this page can be viewed by people nearby.'));
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const PortalWindow = new Lang.Class({
|
||||
Name: 'PortalWindow',
|
||||
Extends: Gtk.ApplicationWindow,
|
||||
@ -49,8 +123,14 @@ const PortalWindow = new Lang.Class({
|
||||
_init: function(application, url, timestamp, doneCallback) {
|
||||
this.parent({ application: application });
|
||||
|
||||
this.connect('delete-event', Lang.bind(this, this.destroyWindow));
|
||||
this._headerBar = new PortalHeaderBar();
|
||||
this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED);
|
||||
this.set_titlebar(this._headerBar);
|
||||
this._headerBar.show();
|
||||
|
||||
if (!url) {
|
||||
url = 'http://www.gnome.org';
|
||||
url = CONNECTIVITY_CHECK_URI;
|
||||
this._originalUrlWasGnome = true;
|
||||
} else {
|
||||
this._originalUrlWasGnome = false;
|
||||
@ -61,29 +141,43 @@ const PortalWindow = new Lang.Class({
|
||||
this._doneCallback = doneCallback;
|
||||
this._lastRecheck = 0;
|
||||
this._recheckAtExit = false;
|
||||
let cacheDir = GLib.Dir.make_tmp("gnome-shell-portal-helper-XXXXXXXX");
|
||||
this._cacheDir = Gio.File.new_for_path(cacheDir);
|
||||
|
||||
this._webView = new WebKit.WebView();
|
||||
let dataManager = new WebKit.WebsiteDataManager({ base_data_directory: cacheDir,
|
||||
base_cache_directory: cacheDir });
|
||||
this._webContext = new WebKit.WebContext({ website_data_manager: dataManager });
|
||||
this._webContext.set_cache_model(WebKit.CacheModel.DOCUMENT_VIEWER);
|
||||
|
||||
this._webView = WebKit.WebView.new_with_context(this._webContext);
|
||||
this._webView.connect('decide-policy', Lang.bind(this, this._onDecidePolicy));
|
||||
this._webView.connect('load-changed', Lang.bind(this, this._onLoadChanged));
|
||||
this._webView.connect('insecure-content-detected', Lang.bind(this, this._onInsecureContentDetected));
|
||||
this._webView.connect('load-failed-with-tls-errors', Lang.bind(this, this._onLoadFailedWithTlsErrors));
|
||||
this._webView.load_uri(url);
|
||||
this._webView.connect('notify::title', Lang.bind(this, this._syncTitle));
|
||||
this._syncTitle();
|
||||
this._webView.connect('notify::uri', Lang.bind(this, this._syncUri));
|
||||
this._syncUri();
|
||||
|
||||
this.add(this._webView);
|
||||
this._webView.show();
|
||||
this.set_size_request(600, 450);
|
||||
this.maximize();
|
||||
this.present_with_time(timestamp);
|
||||
|
||||
this.application.set_accels_for_action('app.quit', ['<Primary>q', '<Primary>w']);
|
||||
},
|
||||
|
||||
_syncTitle: function() {
|
||||
let title = this._webView.title;
|
||||
destroyWindow: function() {
|
||||
this.destroy();
|
||||
FileUtils.recursivelyDeleteDir(this._cacheDir, true);
|
||||
},
|
||||
|
||||
if (title) {
|
||||
this.title = title;
|
||||
} else {
|
||||
/* TRANSLATORS: this is the title of the wifi captive portal login
|
||||
* window, until we know the title of the actual login page */
|
||||
this.title = _("Web Authentication Redirect");
|
||||
}
|
||||
_syncUri: function() {
|
||||
let uri = this._webView.uri;
|
||||
if (uri)
|
||||
this._headerBar.setSubtitle(GLib.uri_unescape_string(uri, null));
|
||||
else
|
||||
this._headerBar.setSubtitle('');
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
@ -99,6 +193,32 @@ const PortalWindow = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_onLoadChanged: function(view, loadEvent) {
|
||||
if (loadEvent == WebKit.LOAD_STARTED) {
|
||||
this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED);
|
||||
} else if (loadEvent == WebKit.LOAD_COMMITTED) {
|
||||
let tlsInfo = this._webView.get_tls_info();
|
||||
let ret = tlsInfo[0];
|
||||
let flags = tlsInfo[2];
|
||||
if (ret && flags == 0)
|
||||
this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.SECURE);
|
||||
else
|
||||
this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE);
|
||||
}
|
||||
},
|
||||
|
||||
_onInsecureContentDetected: function () {
|
||||
this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE);
|
||||
},
|
||||
|
||||
_onLoadFailedWithTlsErrors: function (view, failingURI, certificate, errors) {
|
||||
this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE);
|
||||
let uri = new Soup.URI(failingURI);
|
||||
this._webContext.allow_tls_certificate_for_host(certificate, uri.get_host());
|
||||
this._webView.load_uri(failingURI);
|
||||
return true;
|
||||
},
|
||||
|
||||
_onDecidePolicy: function(view, decision, type) {
|
||||
if (type == WebKit.PolicyDecisionType.NEW_WINDOW_ACTION) {
|
||||
decision.ignore();
|
||||
@ -112,12 +232,12 @@ const PortalWindow = new Lang.Class({
|
||||
let uri = new Soup.URI(request.get_uri());
|
||||
|
||||
if (!uri.host_equal(this._uri) && this._originalUrlWasGnome) {
|
||||
if (uri.get_host() == 'www.gnome.org' && this._everSeenRedirect) {
|
||||
if (uri.get_host() == CONNECTIVITY_CHECK_HOST && this._everSeenRedirect) {
|
||||
// Yay, we got to gnome!
|
||||
decision.ignore();
|
||||
this._doneCallback(PortalHelperResult.COMPLETED);
|
||||
return true;
|
||||
} else if (uri.get_host() != 'www.gnome.org') {
|
||||
} else if (uri.get_host() != CONNECTIVITY_CHECK_HOST) {
|
||||
this._everSeenRedirect = true;
|
||||
}
|
||||
}
|
||||
@ -166,6 +286,10 @@ const WebPortalHelper = new Lang.Class({
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(HelperDBusInterface, this);
|
||||
this._queue = [];
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'quit' });
|
||||
action.connect('activate', () => { this.active_window.destroyWindow(); });
|
||||
this.add_action(action);
|
||||
},
|
||||
|
||||
vfunc_dbus_register: function(connection, path) {
|
||||
@ -197,7 +321,7 @@ const WebPortalHelper = new Lang.Class({
|
||||
|
||||
if (obj.connection == connection) {
|
||||
if (obj.window)
|
||||
obj.window.destroy();
|
||||
obj.window.destroyWindow();
|
||||
this._queue.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
116
js/ui/altTab.js
116
js/ui/altTab.js
@ -33,10 +33,9 @@ const AppIconMode = {
|
||||
};
|
||||
|
||||
function _createWindowClone(window, size) {
|
||||
let windowTexture = window.get_texture();
|
||||
let [width, height] = windowTexture.get_size();
|
||||
let [width, height] = window.get_size();
|
||||
let scale = Math.min(1.0, size / width, size / height);
|
||||
return new Clutter.Clone({ source: windowTexture,
|
||||
return new Clutter.Clone({ source: window,
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -46,6 +45,19 @@ function _createWindowClone(window, size) {
|
||||
y_expand: true });
|
||||
};
|
||||
|
||||
function getWindows(workspace) {
|
||||
// We ignore skip-taskbar windows in switchers, but if they are attached
|
||||
// to their parent, their position in the MRU list may be more appropriate
|
||||
// than the parent; so start with the complete list ...
|
||||
let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL,
|
||||
workspace);
|
||||
// ... map windows to their parent where appropriate ...
|
||||
return windows.map(w => {
|
||||
return w.is_attached_dialog() ? w.get_transient_for() : w;
|
||||
// ... and filter out skip-taskbar windows and duplicates
|
||||
}).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) == i);
|
||||
}
|
||||
|
||||
const AppSwitcherPopup = new Lang.Class({
|
||||
Name: 'AppSwitcherPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
@ -354,6 +366,67 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const CyclerHighlight = new Lang.Class({
|
||||
Name: 'CyclerHighlight',
|
||||
|
||||
_init: function() {
|
||||
this._window = null;
|
||||
|
||||
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
|
||||
this._clone = new Clutter.Clone();
|
||||
this.actor.add_actor(this._clone);
|
||||
|
||||
this._highlight = new St.Widget({ style_class: 'cycler-highlight' });
|
||||
this.actor.add_actor(this._highlight);
|
||||
|
||||
let coordinate = Clutter.BindCoordinate.ALL;
|
||||
let constraint = new Clutter.BindConstraint({ coordinate: coordinate });
|
||||
this._clone.bind_property('source', constraint, 'source', 0);
|
||||
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this.actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._onAllocationChanged));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
},
|
||||
|
||||
set window(w) {
|
||||
if (this._window == w)
|
||||
return;
|
||||
|
||||
this._window = w;
|
||||
|
||||
if (this._clone.source)
|
||||
this._clone.source.sync_visibility();
|
||||
|
||||
let windowActor = this._window ? this._window.get_compositor_private()
|
||||
: null;
|
||||
|
||||
if (windowActor)
|
||||
windowActor.hide();
|
||||
|
||||
this._clone.source = windowActor;
|
||||
},
|
||||
|
||||
_onAllocationChanged: function() {
|
||||
if (!this._window) {
|
||||
this._highlight.set_size(0, 0);
|
||||
this._highlight.hide();
|
||||
} else {
|
||||
let [x, y] = this.actor.allocation.get_origin();
|
||||
let rect = this._window.get_frame_rect();
|
||||
this._highlight.set_size(rect.width, rect.height);
|
||||
this._highlight.set_position(rect.x - x, rect.y - y);
|
||||
this._highlight.show();
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this.window = null;
|
||||
}
|
||||
});
|
||||
|
||||
const CyclerPopup = new Lang.Class({
|
||||
Name: 'CyclerPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
@ -367,6 +440,9 @@ const CyclerPopup = new Lang.Class({
|
||||
if (this._items.length == 0)
|
||||
return;
|
||||
|
||||
this._highlight = new CyclerHighlight();
|
||||
global.window_group.add_actor(this._highlight.actor);
|
||||
|
||||
// We don't show an actual popup, so just provide what SwitcherPopup
|
||||
// expects instead of inheriting from SwitcherList
|
||||
this._switcherList = { actor: new St.Widget(),
|
||||
@ -375,11 +451,37 @@ const CyclerPopup = new Lang.Class({
|
||||
},
|
||||
|
||||
_highlightItem: function(index, justOutline) {
|
||||
Main.activateWindow(this._items[index]);
|
||||
this._highlight.window = this._items[index];
|
||||
global.window_group.set_child_above_sibling(this._highlight.actor, null);
|
||||
},
|
||||
|
||||
_finish: function() {
|
||||
this._highlightItem(this._selectedIndex);
|
||||
let window = this._items[this._selectedIndex];
|
||||
let ws = window.get_workspace();
|
||||
let activeWs = global.screen.get_active_workspace();
|
||||
|
||||
if (window.minimized) {
|
||||
Main.wm.skipNextEffect(window.get_compositor_private());
|
||||
window.unminimize();
|
||||
}
|
||||
|
||||
if (activeWs == ws) {
|
||||
Main.activateWindow(window);
|
||||
} else {
|
||||
// If the selected window is on a different workspace, we don't
|
||||
// want it to disappear, then slide in with the workspace; instead,
|
||||
// always activate it on the active workspace ...
|
||||
activeWs.activate_with_focus(window, global.get_current_time());
|
||||
|
||||
// ... then slide it over to the original workspace if necessary
|
||||
Main.wm.actionMoveWindow(window, ws);
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._highlight.actor.destroy();
|
||||
|
||||
this.parent();
|
||||
}
|
||||
@ -427,7 +529,7 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
|
||||
_getWindowList: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||
return getWindows(workspace);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
@ -465,7 +567,7 @@ const WindowCyclerPopup = new Lang.Class({
|
||||
|
||||
_getWindows: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||
return getWindows(workspace);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
|
@ -67,7 +67,7 @@ const Animation = new Lang.Class({
|
||||
},
|
||||
|
||||
_animationsLoaded: function() {
|
||||
this._isLoaded = true;
|
||||
this._isLoaded = this._animations.get_n_children() > 0;
|
||||
|
||||
if (this._isPlaying)
|
||||
this.play();
|
||||
|
@ -60,6 +60,18 @@ const PAGE_SWITCH_TIME = 0.3;
|
||||
const VIEWS_SWITCH_TIME = 0.4;
|
||||
const VIEWS_SWITCH_ANIMATION_DELAY = 0.1;
|
||||
|
||||
const SWITCHEROO_BUS_NAME = 'net.hadess.SwitcherooControl';
|
||||
const SWITCHEROO_OBJECT_PATH = '/net/hadess/SwitcherooControl';
|
||||
|
||||
const SwitcherooProxyInterface = '<node> \
|
||||
<interface name="net.hadess.SwitcherooControl"> \
|
||||
<property name="HasDualGpu" type="b" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const SwitcherooProxy = Gio.DBusProxy.makeProxyWrapper(SwitcherooProxyInterface);
|
||||
let discreteGpuAvailable = false;
|
||||
|
||||
function _getCategories(info) {
|
||||
let categoriesStr = info.get_categories();
|
||||
if (!categoriesStr)
|
||||
@ -198,6 +210,14 @@ const BaseAppView = new Lang.Class({
|
||||
},
|
||||
|
||||
animate: function(animationDirection, onComplete) {
|
||||
if (onComplete) {
|
||||
let animationDoneId = this._grid.connect('animation-done', Lang.bind(this,
|
||||
function () {
|
||||
this._grid.disconnect(animationDoneId);
|
||||
onComplete();
|
||||
}));
|
||||
}
|
||||
|
||||
if (animationDirection == IconGrid.AnimationDirection.IN) {
|
||||
let toAnimate = this._grid.actor.connect('notify::allocation', Lang.bind(this,
|
||||
function() {
|
||||
@ -213,14 +233,6 @@ const BaseAppView = new Lang.Class({
|
||||
} else {
|
||||
this._doSpringAnimation(animationDirection);
|
||||
}
|
||||
|
||||
if (onComplete) {
|
||||
let animationDoneId = this._grid.connect('animation-done', Lang.bind(this,
|
||||
function () {
|
||||
this._grid.disconnect(animationDoneId);
|
||||
onComplete();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
animateSwitch: function(animationDirection) {
|
||||
@ -887,6 +899,8 @@ const ControlsBoxLayout = Lang.Class({
|
||||
const ViewStackLayout = new Lang.Class({
|
||||
Name: 'ViewStackLayout',
|
||||
Extends: Clutter.BinLayout,
|
||||
Signals: { 'allocated-size-changed': { param_types: [GObject.TYPE_INT,
|
||||
GObject.TYPE_INT] } },
|
||||
|
||||
vfunc_allocate: function (actor, box, flags) {
|
||||
let availWidth = box.x2 - box.x1;
|
||||
@ -897,7 +911,6 @@ const ViewStackLayout = new Lang.Class({
|
||||
this.parent(actor, box, flags);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ViewStackLayout.prototype);
|
||||
|
||||
const AppDisplay = new Lang.Class({
|
||||
Name: 'AppDisplay',
|
||||
@ -969,10 +982,36 @@ const AppDisplay = new Lang.Class({
|
||||
initialView = Views.ALL;
|
||||
this._showView(initialView);
|
||||
this._updateFrequentVisibility();
|
||||
|
||||
Gio.DBus.system.watch_name(SWITCHEROO_BUS_NAME,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
Lang.bind(this, this._switcherooProxyAppeared),
|
||||
Lang.bind(this, function() {
|
||||
this._switcherooProxy = null;
|
||||
this._updateDiscreteGpuAvailable();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateDiscreteGpuAvailable: function() {
|
||||
if (!this._switcherooProxy)
|
||||
discreteGpuAvailable = false;
|
||||
else
|
||||
discreteGpuAvailable = this._switcherooProxy.HasDualGpu;
|
||||
},
|
||||
|
||||
_switcherooProxyAppeared: function() {
|
||||
this._switcherooProxy = new SwitcherooProxy(Gio.DBus.system, SWITCHEROO_BUS_NAME, SWITCHEROO_OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._updateDiscreteGpuAvailable();
|
||||
}));
|
||||
},
|
||||
|
||||
animate: function(animationDirection, onComplete) {
|
||||
let currentView = this._views[global.settings.get_uint('app-picker-view')].view;
|
||||
let currentView = this._views.filter(v => v.control.has_style_pseudo_class('checked')).pop().view;
|
||||
|
||||
// Animate controls opacity using iconGrid animation time, since
|
||||
// it will be the time the AllView or FrequentView takes to show
|
||||
@ -1861,6 +1900,19 @@ const AppIconMenu = new Lang.Class({
|
||||
this._appendSeparator();
|
||||
}
|
||||
|
||||
if (discreteGpuAvailable &&
|
||||
this._source.app.state == Shell.AppState.STOPPED &&
|
||||
actions.indexOf('activate-discrete-gpu') == -1) {
|
||||
this._onDiscreteGpuMenuItem = this._appendMenuItem(_("Launch using Dedicated Graphics Card"));
|
||||
this._onDiscreteGpuMenuItem.connect('activate', Lang.bind(this, function() {
|
||||
if (this._source.app.state == Shell.AppState.STOPPED)
|
||||
this._source.animateLaunch();
|
||||
|
||||
this._source.app.launch(0, -1, true);
|
||||
this.emit('activate-window', null);
|
||||
}));
|
||||
}
|
||||
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
let action = actions[i];
|
||||
let item = this._appendMenuItem(appInfo.get_action_name(action));
|
||||
|
@ -10,8 +10,10 @@ const RENAMED_DESKTOP_IDS = {
|
||||
'baobab.desktop': 'org.gnome.baobab.desktop',
|
||||
'cheese.desktop': 'org.gnome.Cheese.desktop',
|
||||
'dconf-editor.desktop': 'ca.desrt.dconf-editor.desktop',
|
||||
'epiphany.desktop': 'org.gnome.Epiphany.desktop',
|
||||
'file-roller.desktop': 'org.gnome.FileRoller.desktop',
|
||||
'gcalctool.desktop': 'gnome-calculator.desktop',
|
||||
'gcalctool.desktop': 'org.gnome.Calculator.desktop',
|
||||
'geary.desktop': 'org.gnome.Geary.desktop',
|
||||
'gedit.desktop': 'org.gnome.gedit.desktop',
|
||||
'glchess.desktop': 'gnome-chess.desktop',
|
||||
'glines.desktop': 'five-or-more.desktop',
|
||||
@ -19,6 +21,7 @@ const RENAMED_DESKTOP_IDS = {
|
||||
'gnibbles.desktop': 'org.gnome.Nibbles.desktop',
|
||||
'gnobots2.desktop': 'gnome-robots.desktop',
|
||||
'gnome-boxes.desktop': 'org.gnome.Boxes.desktop',
|
||||
'gnome-calculator.desktop': 'org.gnome.Calculator.desktop',
|
||||
'gnome-clocks.desktop': 'org.gnome.clocks.desktop',
|
||||
'gnome-contacts.desktop': 'org.gnome.Contacts.desktop',
|
||||
'gnome-documents.desktop': 'org.gnome.Documents.desktop',
|
||||
@ -56,12 +59,14 @@ const AppFavorites = new Lang.Class({
|
||||
|
||||
reload: function() {
|
||||
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
|
||||
// Map old desktop file names to the current ones
|
||||
let updated = false;
|
||||
ids = ids.map(function (id) {
|
||||
let newId = RENAMED_DESKTOP_IDS[id];
|
||||
if (newId !== undefined) {
|
||||
if (newId !== undefined &&
|
||||
appSys.lookup_app(newId) != null) {
|
||||
updated = true;
|
||||
return newId;
|
||||
}
|
||||
@ -71,7 +76,6 @@ const AppFavorites = new Lang.Class({
|
||||
if (updated)
|
||||
global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
|
||||
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let apps = ids.map(function (id) {
|
||||
return appSys.lookup_app(id);
|
||||
}).filter(function (app) {
|
||||
|
@ -102,6 +102,7 @@ const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -141,7 +142,6 @@ const BackgroundCache = new Lang.Class({
|
||||
Name: 'BackgroundCache',
|
||||
|
||||
_init: function() {
|
||||
this._pendingFileLoads = [];
|
||||
this._fileMonitors = {};
|
||||
this._backgroundSources = {};
|
||||
this._animations = {};
|
||||
@ -254,6 +254,14 @@ const Background = new Lang.Class({
|
||||
this._loadAnimation(this._animation.file);
|
||||
}));
|
||||
|
||||
let loginManager = LoginManager.getLoginManager();
|
||||
this._prepareForSleepId = loginManager.connect('prepare-for-sleep',
|
||||
(lm, aboutToSuspend) => {
|
||||
if (aboutToSuspend)
|
||||
return;
|
||||
this._refreshAnimation();
|
||||
});
|
||||
|
||||
this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
@ -276,16 +284,26 @@ const Background = new Lang.Class({
|
||||
this._clock.disconnect(this._timezoneChangedId);
|
||||
this._timezoneChangedId = 0;
|
||||
|
||||
if (this._prepareForSleepId != 0)
|
||||
LoginManager.getLoginManager().disconnect(this._prepareForSleepId);
|
||||
this._prepareForSleepId = 0;
|
||||
|
||||
if (this._settingsChangedSignalId != 0)
|
||||
this._settings.disconnect(this._settingsChangedSignalId);
|
||||
this._settingsChangedSignalId = 0;
|
||||
},
|
||||
|
||||
updateResolution: function() {
|
||||
if (this._animation) {
|
||||
this._removeAnimationTimeout();
|
||||
this._updateAnimation();
|
||||
}
|
||||
if (this._animation)
|
||||
this._refreshAnimation();
|
||||
},
|
||||
|
||||
_refreshAnimation: function() {
|
||||
if (!this._animation)
|
||||
return;
|
||||
|
||||
this._removeAnimationTimeout();
|
||||
this._updateAnimation();
|
||||
},
|
||||
|
||||
_setLoaded: function() {
|
||||
@ -363,11 +381,9 @@ const Background = new Lang.Class({
|
||||
|
||||
let cache = Meta.BackgroundImageCache.get_default();
|
||||
let numPendingImages = files.length;
|
||||
let images = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this._watchFile(files[i]);
|
||||
let image = cache.load(files[i]);
|
||||
images.push(image);
|
||||
if (image.is_loaded()) {
|
||||
numPendingImages--;
|
||||
if (numPendingImages == 0)
|
||||
|
@ -20,7 +20,7 @@ const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
const ELLIPSIS_CHAR = '\u2026';
|
||||
|
||||
const MESSAGE_ICON_SIZE = 32;
|
||||
const MESSAGE_ICON_SIZE = 16;
|
||||
|
||||
// alias to prevent xgettext from picking up strings translated in GTK+
|
||||
const gtk30_ = Gettext_gtk30.gettext;
|
||||
@ -706,6 +706,14 @@ const EventMessage = new Lang.Class({
|
||||
this._date = date;
|
||||
|
||||
this.parent(this._formatEventTime(), event.summary);
|
||||
|
||||
this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' });
|
||||
this.setIcon(this._icon);
|
||||
|
||||
this.actor.connect('style-changed', () => {
|
||||
let iconVisible = this.actor.get_parent().has_style_pseudo_class('first-child');
|
||||
this._icon.opacity = (iconVisible ? 255 : 0);
|
||||
});
|
||||
},
|
||||
|
||||
_formatEventTime: function() {
|
||||
@ -811,7 +819,16 @@ const EventsSection = new Lang.Class({
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._reloadEvents));
|
||||
this._eventSource = new EmptyEventSource();
|
||||
|
||||
this.parent('');
|
||||
this.parent();
|
||||
|
||||
this._title = new St.Button({ style_class: 'events-section-title',
|
||||
label: '',
|
||||
x_align: St.Align.START,
|
||||
can_focus: true });
|
||||
this.actor.insert_child_below(this._title, null);
|
||||
|
||||
this._title.connect('clicked', Lang.bind(this, this._onTitleClicked));
|
||||
this._title.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
Lang.bind(this, this._appInstalledChanged));
|
||||
@ -832,10 +849,10 @@ const EventsSection = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateTitle: function() {
|
||||
if (isToday(this._date)) {
|
||||
this._title.label = _("Events");
|
||||
this._title.visible = !isToday(this._date);
|
||||
|
||||
if (!this._title.visible)
|
||||
return;
|
||||
}
|
||||
|
||||
let dayFormat;
|
||||
let now = new Date();
|
||||
@ -897,12 +914,13 @@ const EventsSection = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTitleClicked: function() {
|
||||
this.parent();
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
|
||||
let app = this._getCalendarApp();
|
||||
if (app.get_id() == 'evolution.desktop')
|
||||
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||
app.launch([], global.create_app_launch_context(0, -1));
|
||||
app.launch([], global.create_app_launch_context(0, -1), false);
|
||||
},
|
||||
|
||||
setDate: function(date) {
|
||||
@ -928,7 +946,7 @@ const NotificationSection = new Lang.Class({
|
||||
Extends: MessageList.MessageListSection,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Notifications"));
|
||||
this.parent();
|
||||
|
||||
this._sources = new Map();
|
||||
this._nUrgent = 0;
|
||||
@ -946,10 +964,14 @@ const NotificationSection = new Lang.Class({
|
||||
!Main.sessionMode.isGreeter;
|
||||
},
|
||||
|
||||
_createTimeLabel: function() {
|
||||
let label = Util.createTimeLabel(new Date());
|
||||
label.style_class = 'event-time',
|
||||
label.x_align = Clutter.ActorAlign.END;
|
||||
_createTimeLabel: function(datetime) {
|
||||
let label = new St.Label({ style_class: 'event-time',
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
y_align: Clutter.ActorAlign.END });
|
||||
label.connect('notify::mapped', () => {
|
||||
if (label.mapped)
|
||||
label.text = Util.formatTimeSpan(datetime);
|
||||
});
|
||||
return label;
|
||||
},
|
||||
|
||||
@ -970,13 +992,13 @@ const NotificationSection = new Lang.Class({
|
||||
|
||||
_onNotificationAdded: function(source, notification) {
|
||||
let message = new NotificationMessage(notification);
|
||||
message.setSecondaryActor(this._createTimeLabel());
|
||||
message.setSecondaryActor(this._createTimeLabel(notification.datetime));
|
||||
|
||||
let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
|
||||
|
||||
let updatedId = notification.connect('updated', Lang.bind(this,
|
||||
function() {
|
||||
message.setSecondaryActor(this._createTimeLabel());
|
||||
message.setSecondaryActor(this._createTimeLabel(notification.datetime));
|
||||
this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.actor.mapped);
|
||||
}));
|
||||
let destroyId = notification.connect('destroy', Lang.bind(this,
|
||||
@ -1017,26 +1039,8 @@ const NotificationSection = new Lang.Class({
|
||||
message.notification.acknowledged = true;
|
||||
},
|
||||
|
||||
_onTitleClicked: function() {
|
||||
this.parent();
|
||||
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-notifications-panel.desktop');
|
||||
|
||||
if (!app) {
|
||||
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||
return;
|
||||
}
|
||||
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_shouldShow: function() {
|
||||
return !this.empty && isToday(this._date);
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this.parent();
|
||||
this._title.reactive = Main.sessionMode.allowSettings;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1098,12 +1102,26 @@ const CalendarMessageList = new Lang.Class({
|
||||
this._placeholder = new Placeholder();
|
||||
this.actor.add_actor(this._placeholder.actor);
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true,
|
||||
x_expand: true, y_expand: true });
|
||||
this.actor.add_actor(box);
|
||||
|
||||
this._scrollView = new St.ScrollView({ style_class: 'vfade',
|
||||
overlay_scrollbars: true,
|
||||
x_expand: true, y_expand: true,
|
||||
x_fill: true, y_fill: true });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor.add_actor(this._scrollView);
|
||||
box.add_actor(this._scrollView);
|
||||
|
||||
this._clearButton = new St.Button({ style_class: 'message-list-clear-button button',
|
||||
label: _("Clear All"),
|
||||
can_focus: true });
|
||||
this._clearButton.set_x_align(Clutter.ActorAlign.END);
|
||||
this._clearButton.connect('clicked', () => {
|
||||
let sections = [...this._sections.keys()];
|
||||
sections.forEach((s) => { s.clear(); });
|
||||
});
|
||||
box.add_actor(this._clearButton);
|
||||
|
||||
this._sectionList = new St.BoxLayout({ style_class: 'message-list-sections',
|
||||
vertical: true,
|
||||
@ -1129,6 +1147,7 @@ const CalendarMessageList = new Lang.Class({
|
||||
destroyId: 0,
|
||||
visibleId: 0,
|
||||
emptyChangedId: 0,
|
||||
canClearChangedId: 0,
|
||||
keyFocusId: 0
|
||||
};
|
||||
obj.destroyId = section.actor.connect('destroy', Lang.bind(this,
|
||||
@ -1139,6 +1158,8 @@ const CalendarMessageList = new Lang.Class({
|
||||
Lang.bind(this, this._sync));
|
||||
obj.emptyChangedId = section.connect('empty-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
obj.canClearChangedId = section.connect('can-clear-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
obj.keyFocusId = section.connect('key-focus-in',
|
||||
Lang.bind(this, this._onKeyFocusIn));
|
||||
|
||||
@ -1152,6 +1173,7 @@ const CalendarMessageList = new Lang.Class({
|
||||
section.actor.disconnect(obj.destroyId);
|
||||
section.actor.disconnect(obj.visibleId);
|
||||
section.disconnect(obj.emptyChangedId);
|
||||
section.disconnect(obj.canClearChangedId);
|
||||
section.disconnect(obj.keyFocusId);
|
||||
|
||||
this._sections.delete(section);
|
||||
@ -1172,10 +1194,16 @@ const CalendarMessageList = new Lang.Class({
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
let showPlaceholder = sections.every(function(s) {
|
||||
let empty = sections.every(function(s) {
|
||||
return s.empty || !s.actor.visible;
|
||||
});
|
||||
this._placeholder.actor.visible = showPlaceholder;
|
||||
this._placeholder.actor.visible = empty;
|
||||
this._clearButton.visible = !empty;
|
||||
|
||||
let canClear = sections.some(function(s) {
|
||||
return s.canClear && s.actor.visible;
|
||||
});
|
||||
this._clearButton.reactive = canClear;
|
||||
},
|
||||
|
||||
setEventSource: function(eventSource) {
|
||||
|
@ -64,7 +64,8 @@ function startAppForMount(app, mount) {
|
||||
|
||||
try {
|
||||
retval = app.launch(files,
|
||||
global.create_app_launch_context(0, -1))
|
||||
global.create_app_launch_context(0, -1),
|
||||
false)
|
||||
} catch (e) {
|
||||
log('Unable to launch the application ' + app.get_name()
|
||||
+ ': ' + e.toString());
|
||||
|
@ -615,6 +615,14 @@ const NetworkAgent = new Lang.Class({
|
||||
this._vpnRequests = { };
|
||||
this._notifications = { };
|
||||
|
||||
this._pluginDir = Gio.file_new_for_path(GLib.build_filenamev([Config.SYSCONFDIR, 'NetworkManager/VPN']));
|
||||
try {
|
||||
let monitor = this._pluginDir.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||
monitor.connect('changed', () => { this._vpnCacheBuilt = false; });
|
||||
} catch(e) {
|
||||
log('Failed to create monitor for VPN plugin dir: ' + e.message);
|
||||
}
|
||||
|
||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
|
||||
@ -765,9 +773,8 @@ const NetworkAgent = new Lang.Class({
|
||||
this._vpnCacheBuilt = true;
|
||||
this._vpnBinaries = { };
|
||||
|
||||
let dir = Gio.file_new_for_path(GLib.build_filenamev([Config.SYSCONFDIR, 'NetworkManager/VPN']));
|
||||
try {
|
||||
let fileEnum = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let fileEnum = this._pluginDir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let info;
|
||||
|
||||
while ((info = fileEnum.next_file(null))) {
|
||||
@ -777,7 +784,7 @@ const NetworkAgent = new Lang.Class({
|
||||
|
||||
try {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
keyfile.load_from_file(dir.get_child(name).get_path(), GLib.KeyFileFlags.NONE);
|
||||
keyfile.load_from_file(this._pluginDir.get_child(name).get_path(), GLib.KeyFileFlags.NONE);
|
||||
let service = keyfile.get_string('VPN Connection', 'service');
|
||||
let binary = keyfile.get_string('GNOME', 'auth-dialog');
|
||||
let externalUIMode = false;
|
||||
@ -810,7 +817,7 @@ const NetworkAgent = new Lang.Class({
|
||||
}
|
||||
} catch(e) {
|
||||
log('Error \'%s\' while processing VPN keyfile \'%s\''.
|
||||
format(e.message, dir.get_child(name).get_path()));
|
||||
format(e.message, this._pluginDir.get_child(name).get_path()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
* requested authentication was not gained; this can happen
|
||||
* because of an authentication error (like invalid password),
|
||||
* for instance. */
|
||||
this._errorMessageLabel.set_text(_("Sorry, that didn\'t work. Please try again."));
|
||||
this._errorMessageLabel.set_text(_("Sorry, that didn’t work. Please try again."));
|
||||
this._errorMessageLabel.show();
|
||||
this._infoMessageLabel.hide();
|
||||
this._nullMessageLabel.hide();
|
||||
|
@ -6,11 +6,17 @@ const GLib = imports.gi.GLib;
|
||||
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 Tpl = imports.gi.TelepathyLogger;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
|
||||
var Tpl = null;
|
||||
var Tp = null;
|
||||
try {
|
||||
Tpl = imports.gi.TelepathyLogger;
|
||||
Tp = imports.gi.TelepathyGLib;
|
||||
} catch(e) {
|
||||
log('Telepathy is not available, chat integration will be disabled.');
|
||||
}
|
||||
|
||||
const History = imports.misc.history;
|
||||
const Main = imports.ui.main;
|
||||
@ -20,6 +26,8 @@ const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const HAVE_TP = (Tp != null && Tpl != null);
|
||||
|
||||
// See Notification.appendMessage
|
||||
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
|
||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
||||
@ -71,8 +79,43 @@ function makeMessageFromTplEvent(event) {
|
||||
};
|
||||
}
|
||||
|
||||
const TelepathyClient = new Lang.Class({
|
||||
const TelepathyComponent = new Lang.Class({
|
||||
Name: 'TelepathyComponent',
|
||||
|
||||
_init: function() {
|
||||
this._client = null;
|
||||
|
||||
if (!HAVE_TP)
|
||||
return; // Telepathy isn't available
|
||||
|
||||
this._client = new TelepathyClient();
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
if (!this._client)
|
||||
return;
|
||||
|
||||
try {
|
||||
this._client.register();
|
||||
} catch (e) {
|
||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||
}
|
||||
|
||||
if (!this._client.account_manager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
|
||||
this._client.account_manager.prepare_async(null, null);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
if (!this._client)
|
||||
return;
|
||||
|
||||
this._client.unregister();
|
||||
}
|
||||
});
|
||||
|
||||
const TelepathyClient = HAVE_TP ? new Lang.Class({
|
||||
Name: 'TelepathyClient',
|
||||
Extends: Tp.BaseClient,
|
||||
|
||||
_init: function() {
|
||||
// channel path -> ChatSource
|
||||
@ -97,39 +140,28 @@ const TelepathyClient = new Lang.Class({
|
||||
// channel matching its filters is detected.
|
||||
// The second argument, recover, means _observeChannels will be run
|
||||
// for any existing channel as well.
|
||||
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
|
||||
account_manager: this._accountManager,
|
||||
uniquify_name: true });
|
||||
this._tpClient.set_observe_channels_func(
|
||||
Lang.bind(this, this._observeChannels));
|
||||
this._tpClient.set_approve_channels_func(
|
||||
Lang.bind(this, this._approveChannels));
|
||||
this._tpClient.set_handle_channels_func(
|
||||
Lang.bind(this, this._handleChannels));
|
||||
this.parent({ name: 'GnomeShell',
|
||||
account_manager: this._accountManager,
|
||||
uniquify_name: true });
|
||||
|
||||
// We only care about single-user text-based chats
|
||||
let filter = {};
|
||||
filter[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
|
||||
filter[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE] = Tp.HandleType.CONTACT;
|
||||
|
||||
this.set_observer_recover(true);
|
||||
this.add_observer_filter(filter);
|
||||
this.add_approver_filter(filter);
|
||||
this.add_handler_filter(filter);
|
||||
|
||||
// Allow other clients (such as Empathy) to pre-empt our channels if
|
||||
// needed
|
||||
this._tpClient.set_delegated_channels_callback(
|
||||
this.set_delegated_channels_callback(
|
||||
Lang.bind(this, this._delegatedChannelsCb));
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
try {
|
||||
this._tpClient.register();
|
||||
} catch (e) {
|
||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||
}
|
||||
|
||||
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
|
||||
this._accountManager.prepare_async(null, null);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._tpClient.unregister();
|
||||
},
|
||||
|
||||
_observeChannels: function(observer, account, conn, channels,
|
||||
dispatchOp, requests, context) {
|
||||
vfunc_observe_channels: function(account, conn, channels,
|
||||
dispatchOp, requests, context) {
|
||||
let len = channels.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let channel = channels[i];
|
||||
@ -153,7 +185,7 @@ const TelepathyClient = new Lang.Class({
|
||||
if (this._chatSources[channel.get_object_path()])
|
||||
return;
|
||||
|
||||
let source = new ChatSource(account, conn, channel, contact, this._tpClient);
|
||||
let source = new ChatSource(account, conn, channel, contact, this);
|
||||
|
||||
this._chatSources[channel.get_object_path()] = source;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
@ -162,8 +194,8 @@ const TelepathyClient = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_handleChannels: function(handler, account, conn, channels,
|
||||
requests, user_action_time, context) {
|
||||
vfunc_handle_channels: function(account, conn, channels, requests,
|
||||
user_action_time, context) {
|
||||
this._handlingChannels(account, conn, channels, true);
|
||||
context.accept();
|
||||
},
|
||||
@ -193,7 +225,7 @@ const TelepathyClient = new Lang.Class({
|
||||
// Telepathy spec states that handlers must foreground channels
|
||||
// in HandleChannels calls which are already being handled.
|
||||
|
||||
if (notify && this._tpClient.is_handling_channel(channel)) {
|
||||
if (notify && this.is_handling_channel(channel)) {
|
||||
// We are already handling the channel, display the source
|
||||
let source = this._chatSources[channel.get_object_path()];
|
||||
if (source)
|
||||
@ -202,8 +234,8 @@ const TelepathyClient = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_approveChannels: function(approver, account, conn, channels,
|
||||
dispatchOp, context) {
|
||||
vfunc_add_dispatch_operation: function(account, conn, channels,
|
||||
dispatchOp, context) {
|
||||
let channel = channels[0];
|
||||
let chanType = channel.get_channel_type();
|
||||
|
||||
@ -230,7 +262,7 @@ const TelepathyClient = new Lang.Class({
|
||||
}
|
||||
|
||||
// Approve private text channels right away as we are going to handle it
|
||||
dispatchOp.claim_with_async(this._tpClient, Lang.bind(this, function(dispatchOp, result) {
|
||||
dispatchOp.claim_with_async(this, Lang.bind(this, function(dispatchOp, result) {
|
||||
try {
|
||||
dispatchOp.claim_with_finish(result);
|
||||
this._handlingChannels(account, conn, [channel], false);
|
||||
@ -246,7 +278,7 @@ const TelepathyClient = new Lang.Class({
|
||||
// Nothing to do as we don't make a distinction between observed and
|
||||
// handled channels.
|
||||
},
|
||||
});
|
||||
}) : null;
|
||||
|
||||
const ChatSource = new Lang.Class({
|
||||
Name: 'ChatSource',
|
||||
@ -475,6 +507,11 @@ const ChatSource = new Lang.Class({
|
||||
this._channel.close_async(function(channel, result) {
|
||||
channel.close_finish(result);
|
||||
});
|
||||
} else {
|
||||
// Don't indicate any unread messages when the notification
|
||||
// that represents them has been destroyed.
|
||||
this._pendingMessages = [];
|
||||
this.countUpdated();
|
||||
}
|
||||
|
||||
// Keep source alive while the channel is open
|
||||
@ -655,7 +692,9 @@ const ChatNotification = new Lang.Class({
|
||||
}
|
||||
|
||||
if (message.direction == NotificationDirection.RECEIVED)
|
||||
this.update(this.source.title, messageBody, { bannerMarkup: true });
|
||||
this.update(this.source.title, messageBody,
|
||||
{ datetime: GLib.DateTime.new_from_unix_local (message.timestamp),
|
||||
bannerMarkup: true });
|
||||
|
||||
let group = (message.direction == NotificationDirection.RECEIVED ?
|
||||
'received' : 'sent');
|
||||
@ -957,4 +996,4 @@ const ChatNotificationBanner = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const Component = TelepathyClient;
|
||||
const Component = TelepathyComponent;
|
||||
|
@ -8,6 +8,7 @@ const Gtk = imports.gi.Gtk;
|
||||
const GWeather = imports.gi.GWeather;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -20,6 +21,7 @@ const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Weather = imports.misc.weather;
|
||||
|
||||
function _isToday(date) {
|
||||
let now = new Date();
|
||||
@ -36,7 +38,7 @@ const TodayButton = new Lang.Class({
|
||||
// on the current date can be confusing. So don't make the button reactive
|
||||
// until the selected date changes.
|
||||
this.actor = new St.Button({ style_class: 'datemenu-today-button',
|
||||
x_align: St.Align.START,
|
||||
x_expand: true, x_align: St.Align.START,
|
||||
can_focus: true,
|
||||
reactive: false
|
||||
});
|
||||
@ -77,7 +79,7 @@ const TodayButton = new Lang.Class({
|
||||
* below the time in the shell; it should combine the weekday and the
|
||||
* date, e.g. "Tuesday February 17 2015".
|
||||
*/
|
||||
let dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
|
||||
dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
|
||||
this.actor.accessible_name = date.toLocaleFormat(dateFormat);
|
||||
}
|
||||
});
|
||||
@ -87,9 +89,7 @@ const WorldClocksSection = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._settings = null;
|
||||
this._clockNotifyId = 0;
|
||||
this._changedId = 0;
|
||||
|
||||
this._locations = [];
|
||||
|
||||
@ -98,8 +98,7 @@ const WorldClocksSection = new Lang.Class({
|
||||
can_focus: true });
|
||||
this.actor.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
let app = this._getClockApp();
|
||||
app.activate();
|
||||
this._clockAppMon.activateApp();
|
||||
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
@ -112,40 +111,25 @@ const WorldClocksSection = new Lang.Class({
|
||||
|
||||
this.actor.child = this._grid;
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._clockAppMon = new Util.AppSettingsMonitor('org.gnome.clocks.desktop',
|
||||
'org.gnome.clocks');
|
||||
this._clockAppMon.connect('available-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._clockAppMon.watchSetting('world-clocks',
|
||||
Lang.bind(this, this._clocksChanged));
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_getClockApp: function() {
|
||||
return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this.actor.visible = (this._getClockApp() != null);
|
||||
|
||||
if (this.actor.visible) {
|
||||
if (!this._settings) {
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.clocks' });
|
||||
this._changedId =
|
||||
this._settings.connect('changed::world-clocks',
|
||||
Lang.bind(this, this._clocksChanged));
|
||||
this._clocksChanged();
|
||||
}
|
||||
} else {
|
||||
if (this._settings)
|
||||
this._settings.disconnect(this._changedId);
|
||||
this._settings = null;
|
||||
this._changedId = 0;
|
||||
}
|
||||
this.actor.visible = this._clockAppMon.available;
|
||||
},
|
||||
|
||||
_clocksChanged: function() {
|
||||
_clocksChanged: function(settings) {
|
||||
this._grid.destroy_all_children();
|
||||
this._locations = [];
|
||||
|
||||
let world = GWeather.Location.get_world();
|
||||
let clocks = this._settings.get_value('world-clocks').deep_unpack();
|
||||
let clocks = settings.get_value('world-clocks').deep_unpack();
|
||||
for (let i = 0; i < clocks.length; i++) {
|
||||
let l = world.deserialize(clocks[i].location);
|
||||
this._locations.push({ location: l });
|
||||
@ -210,6 +194,133 @@ const WorldClocksSection = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const WeatherSection = new Lang.Class({
|
||||
Name: 'WeatherSection',
|
||||
|
||||
_init: function() {
|
||||
this._weatherClient = new Weather.WeatherClient();
|
||||
|
||||
this.actor = new St.Button({ style_class: 'weather-button',
|
||||
x_fill: true,
|
||||
can_focus: true });
|
||||
this.actor.connect('clicked', () => {
|
||||
this._weatherClient.activateApp();
|
||||
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
});
|
||||
this.actor.connect('notify::mapped', () => {
|
||||
if (this.actor.mapped)
|
||||
this._weatherClient.update();
|
||||
});
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'weather-box',
|
||||
vertical: true });
|
||||
|
||||
this.actor.child = box;
|
||||
|
||||
box.add_child(new St.Label({ style_class: 'weather-header',
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
text: _("Weather") }));
|
||||
|
||||
this._conditionsLabel = new St.Label({ style_class: 'weather-conditions',
|
||||
x_align: Clutter.ActorAlign.START });
|
||||
this._conditionsLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._conditionsLabel.clutter_text.line_wrap = true;
|
||||
box.add_child(this._conditionsLabel);
|
||||
|
||||
this._weatherClient.connect('changed', Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_getSummary: function(info) {
|
||||
let summary = info.get_conditions();
|
||||
if (summary == '-')
|
||||
return info.get_sky();
|
||||
return summary;
|
||||
},
|
||||
|
||||
_sameSummary: function(info1, info2) {
|
||||
let [ok1, phenom1, qualifier1] = info1.get_value_conditions();
|
||||
let [ok2, phenom2, qualifier2] = info2.get_value_conditions();
|
||||
if (ok1 || ok2)
|
||||
return ok1 == ok2 && phenom1 == phenom2 && qualifier1 == qualifier2;
|
||||
|
||||
let [, sky1] = info1.get_value_sky();
|
||||
let [, sky2] = info2.get_value_sky();
|
||||
return sky1 == sky2;
|
||||
},
|
||||
|
||||
_getSummaryText: function() {
|
||||
let info = this._weatherClient.info;
|
||||
let forecasts = info.get_forecast_list();
|
||||
if (forecasts.length == 0) // No forecasts, just current conditions
|
||||
return '%s.'.format(this._getSummary(info));
|
||||
|
||||
let current = info;
|
||||
let summaries = [this._getSummary(info)];
|
||||
for (let i = 0; i < forecasts.length; i++) {
|
||||
let [ok, timestamp] = forecasts[i].get_value_update();
|
||||
if (!_isToday(new Date(timestamp * 1000)))
|
||||
continue; // Ignore forecasts from other days
|
||||
|
||||
if (this._sameSummary(current, forecasts[i]))
|
||||
continue; // Ignore consecutive runs of equal summaries
|
||||
|
||||
current = forecasts[i];
|
||||
if (summaries.push(this._getSummary(current)) == 3)
|
||||
break; // Use a maximum of three summaries
|
||||
}
|
||||
|
||||
let fmt;
|
||||
switch(summaries.length) {
|
||||
/* Translators: %s is a weather condition like "Clear sky"; see
|
||||
libgweather for the possible condition strings. If at all
|
||||
possible, the sentence should match the grammatical case etc. of
|
||||
the inserted conditions. */
|
||||
case 1: fmt = _("%s all day."); break;
|
||||
|
||||
/* Translators: %s is a weather condition like "Clear sky"; see
|
||||
libgweather for the possible condition strings. If at all
|
||||
possible, the sentence should match the grammatical case etc. of
|
||||
the inserted conditions. */
|
||||
case 2: fmt = _("%s, then %s later."); break;
|
||||
|
||||
/* Translators: %s is a weather condition like "Clear sky"; see
|
||||
libgweather for the possible condition strings. If at all
|
||||
possible, the sentence should match the grammatical case etc. of
|
||||
the inserted conditions. */
|
||||
case 3: fmt = _("%s, then %s, followed by %s later."); break;
|
||||
}
|
||||
return String.prototype.format.apply(fmt, summaries);
|
||||
},
|
||||
|
||||
_getLabelText: function() {
|
||||
if (this._weatherClient.loading)
|
||||
return _("Loading…");
|
||||
|
||||
let info = this._weatherClient.info;
|
||||
if (info.is_valid())
|
||||
return this._getSummaryText() + ' ' +
|
||||
/* Translators: %s is a temperature with unit, e.g. "23℃" */
|
||||
_("Feels like %s.").format(info.get_apparent());
|
||||
|
||||
if (info.network_error())
|
||||
return _("Go online for weather information");
|
||||
|
||||
return _("Weather information is currently unavailable");
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
this.actor.visible = this._weatherClient.available;
|
||||
|
||||
if (!this.actor.visible)
|
||||
return;
|
||||
|
||||
this._conditionsLabel.text = this._getLabelText();
|
||||
}
|
||||
});
|
||||
|
||||
const MessagesIndicator = new Lang.Class({
|
||||
Name: 'MessagesIndicator',
|
||||
|
||||
@ -297,14 +408,38 @@ const FreezableBinLayout = new Lang.Class({
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
if (!this._frozen || this._savedWidth.some(isNaN))
|
||||
this._savedWidth = this.parent(container, forHeight);
|
||||
return this.parent(container, forHeight);
|
||||
return this._savedWidth;
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
if (!this._frozen || this._savedHeight.some(isNaN))
|
||||
this._savedHeight = this.parent(container, forWidth);
|
||||
return this.parent(container, forWidth);
|
||||
return this._savedHeight;
|
||||
},
|
||||
|
||||
vfunc_allocate: function(container, allocation, flags) {
|
||||
this.parent(container, allocation, flags);
|
||||
|
||||
let [width, height] = allocation.get_size();
|
||||
this._savedWidth = [width, width];
|
||||
this._savedHeight = [height, height];
|
||||
}
|
||||
});
|
||||
|
||||
const CalendarColumnLayout = new Lang.Class({
|
||||
Name: 'CalendarColumnLayout',
|
||||
Extends: Clutter.BoxLayout,
|
||||
|
||||
_init: function(actor) {
|
||||
this.parent({ orientation: Clutter.Orientation.VERTICAL });
|
||||
this._calActor = actor;
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
if (!this._calActor || this._calActor.get_parent() != container)
|
||||
return this.parent(container, forHeight);
|
||||
return this._calActor.get_preferred_width(forHeight);
|
||||
}
|
||||
});
|
||||
|
||||
@ -364,14 +499,16 @@ const DateMenuButton = new Lang.Class({
|
||||
hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({ style_class: 'datemenu-calendar-column',
|
||||
vertical: true });
|
||||
let boxLayout = new CalendarColumnLayout(this._calendar.actor);
|
||||
vbox = new St.Widget({ style_class: 'datemenu-calendar-column',
|
||||
layout_manager: boxLayout });
|
||||
boxLayout.hookup_style(vbox);
|
||||
hbox.add(vbox);
|
||||
|
||||
this._date = new TodayButton(this._calendar);
|
||||
vbox.add_actor(this._date.actor);
|
||||
|
||||
vbox.add(this._calendar.actor);
|
||||
vbox.add_actor(this._calendar.actor);
|
||||
|
||||
this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade',
|
||||
x_expand: true, x_fill: true,
|
||||
@ -386,6 +523,8 @@ const DateMenuButton = new Lang.Class({
|
||||
this._clocksItem = new WorldClocksSection();
|
||||
displaysBox.add(this._clocksItem.actor, { x_fill: true });
|
||||
|
||||
this._weatherItem = new WeatherSection();
|
||||
displaysBox.add(this._weatherItem.actor, { x_fill: true });
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
|
@ -14,6 +14,7 @@ const DRAG_DISTANCE = 80;
|
||||
const EdgeDragAction = new Lang.Class({
|
||||
Name: 'EdgeDragAction',
|
||||
Extends: Clutter.GestureAction,
|
||||
Signals: { 'activated': {} },
|
||||
|
||||
_init : function(side, allowedModes) {
|
||||
this.parent();
|
||||
@ -81,4 +82,3 @@ const EdgeDragAction = new Lang.Class({
|
||||
this.emit('activated');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(EdgeDragAction.prototype);
|
||||
|
@ -457,7 +457,6 @@ const EndSessionDialog = new Lang.Class({
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
_setLabelText(this._subjectLabel, subject);
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
if (dialogContent.iconName) {
|
||||
this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName,
|
||||
icon_size: _DIALOG_ICON_SIZE,
|
||||
|
@ -1,6 +1,8 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
imports.gi.versions.Clutter = '1.0';
|
||||
const Config = imports.misc.config;
|
||||
|
||||
imports.gi.versions.Clutter = Config.LIBMUTTER_API_VERSION;
|
||||
imports.gi.versions.Gio = '2.0';
|
||||
imports.gi.versions.Gdk = '3.0';
|
||||
imports.gi.versions.GdkPixbuf = '2.0';
|
||||
@ -61,10 +63,19 @@ function _patchLayoutClass(layoutClass, styleProps) {
|
||||
};
|
||||
}
|
||||
|
||||
function _makeLoggingFunc(func) {
|
||||
return function() {
|
||||
return func([].join.call(arguments, ', '));
|
||||
};
|
||||
function _loggingFunc() {
|
||||
let fields = {'MESSAGE': [].join.call(arguments, ', ')};
|
||||
let domain = "GNOME Shell";
|
||||
|
||||
// If the caller is an extension, add it as metadata
|
||||
let extension = imports.misc.extensionUtils.getCurrentExtension();
|
||||
if (extension != null) {
|
||||
domain = extension.metadata.name;
|
||||
fields['GNOME_SHELL_EXTENSION_UUID'] = extension.uuid;
|
||||
fields['GNOME_SHELL_EXTENSION_NAME'] = extension.metadata.name;
|
||||
}
|
||||
|
||||
GLib.log_structured(domain, GLib.LogLevelFlags.LEVEL_MESSAGE, fields);
|
||||
}
|
||||
|
||||
function init() {
|
||||
@ -72,7 +83,7 @@ function init() {
|
||||
// browser convention of having that namespace be called 'window'.)
|
||||
window.global = Shell.Global.get();
|
||||
|
||||
window.log = _makeLoggingFunc(window.log);
|
||||
window.log = _loggingFunc;
|
||||
|
||||
window._ = Gettext.gettext;
|
||||
window.C_ = Gettext.pgettext;
|
||||
|
@ -252,7 +252,7 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
invocation.return_value(GLib.Variant.new('(s)', 'successful'));
|
||||
invocation.return_value(GLib.Variant.new('(s)', ['successful']));
|
||||
}
|
||||
|
||||
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
|
||||
|
@ -38,6 +38,7 @@ const connect = Lang.bind(_signals, _signals.connect);
|
||||
const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
const DISABLE_USER_EXTENSIONS_KEY = 'disable-user-extensions';
|
||||
const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
|
||||
|
||||
var initted = false;
|
||||
@ -238,11 +239,16 @@ function initExtension(uuid) {
|
||||
}
|
||||
|
||||
function getEnabledExtensions() {
|
||||
let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
if (!Array.isArray(Main.sessionMode.enabledExtensions))
|
||||
let extensions;
|
||||
if (Array.isArray(Main.sessionMode.enabledExtensions))
|
||||
extensions = Main.sessionMode.enabledExtensions;
|
||||
else
|
||||
extensions = [];
|
||||
|
||||
if (global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY))
|
||||
return extensions;
|
||||
|
||||
return Main.sessionMode.enabledExtensions.concat(extensions);
|
||||
return extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY));
|
||||
}
|
||||
|
||||
function onEnabledExtensionsChanged() {
|
||||
@ -288,6 +294,7 @@ function _onVersionValidationChanged() {
|
||||
|
||||
function _loadExtensions() {
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
global.settings.connect('changed::' + DISABLE_USER_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged);
|
||||
|
||||
enabledExtensions = getEnabledExtensions();
|
||||
|
@ -19,6 +19,7 @@ const CandidateArea = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true,
|
||||
reactive: true,
|
||||
visible: false });
|
||||
this._candidateBoxes = [];
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
@ -39,6 +40,19 @@ const CandidateArea = new Lang.Class({
|
||||
}));
|
||||
}
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, function(actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
switch(direction) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
this.emit('cursor-up');
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
this.emit('cursor-down');
|
||||
break;
|
||||
};
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
|
||||
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
|
||||
|
||||
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous button' });
|
||||
@ -144,6 +158,14 @@ const CandidatePopup = new Lang.Class({
|
||||
this._candidateArea.connect('next-page', Lang.bind(this, function() {
|
||||
this._panelService.page_down();
|
||||
}));
|
||||
|
||||
this._candidateArea.connect('cursor-up', Lang.bind(this, function() {
|
||||
this._panelService.cursor_up();
|
||||
}));
|
||||
this._candidateArea.connect('cursor-down', Lang.bind(this, function() {
|
||||
this._panelService.cursor_down();
|
||||
}));
|
||||
|
||||
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
|
||||
this._panelService.candidate_clicked(index, button, state);
|
||||
}));
|
||||
|
@ -10,6 +10,7 @@ const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const InputSourceManager = imports.ui.status.keyboard;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Layout = imports.ui.layout;
|
||||
@ -757,19 +758,48 @@ const ShellWaylandAdapter = new Lang.Class({
|
||||
Name: 'ShellWaylandAdapter',
|
||||
Extends: Caribou.XAdapter,
|
||||
|
||||
_init: function () {
|
||||
this.parent();
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
this._virtualDevice = deviceManager.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE);
|
||||
|
||||
this._inputSourceManager = InputSourceManager.getInputSourceManager();
|
||||
this._sourceChangedId = this._inputSourceManager.connect('current-source-changed',
|
||||
Lang.bind(this, this._onSourceChanged));
|
||||
this._sourcesModifiedId = this._inputSourceManager.connect ('sources-changed',
|
||||
Lang.bind(this, this._onSourcesModified));
|
||||
},
|
||||
|
||||
_onSourcesModified: function () {
|
||||
this.emit('config-changed');
|
||||
},
|
||||
|
||||
_onSourceChanged: function (inputSourceManager, oldSource) {
|
||||
let source = inputSourceManager.currentSource;
|
||||
this.emit('group-changed', source.index, source.id, '');
|
||||
},
|
||||
|
||||
vfunc_get_groups: function () {
|
||||
let inputSources = this._inputSourceManager.inputSources;
|
||||
let groups = []
|
||||
let variants = [];
|
||||
|
||||
for (let i in inputSources) {
|
||||
let is = inputSources[i];
|
||||
groups[is.index] = is.id;
|
||||
variants[is.index] = '';
|
||||
}
|
||||
|
||||
return [groups, groups.length, variants, variants.length];
|
||||
},
|
||||
|
||||
vfunc_keyval_press: function(keyval) {
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (focus instanceof Clutter.Text)
|
||||
Shell.util_text_insert_keyval(focus, keyval);
|
||||
else
|
||||
this.parent(keyval);
|
||||
this._virtualDevice.notify_keyval(Clutter.get_current_event_time(),
|
||||
keyval, Clutter.KeyState.PRESSED);
|
||||
},
|
||||
|
||||
vfunc_keyval_release: function(keyval) {
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (focus instanceof Clutter.Text)
|
||||
return; // do nothing
|
||||
else
|
||||
this.parent(keyval);
|
||||
this._virtualDevice.notify_keyval(Clutter.get_current_event_time(),
|
||||
keyval, Clutter.KeyState.RELEASED);
|
||||
},
|
||||
});
|
||||
|
@ -33,7 +33,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const Mainloop = imports.mainloop; ' +
|
||||
'const Meta = imports.gi.Meta; ' +
|
||||
'const Shell = imports.gi.Shell; ' +
|
||||
'const Tp = imports.gi.TelepathyGLib; ' +
|
||||
'const Main = imports.ui.main; ' +
|
||||
'const Lang = imports.lang; ' +
|
||||
'const Tweener = imports.ui.tweener; ' +
|
||||
|
@ -26,6 +26,7 @@ const ModalDialog = imports.ui.modalDialog;
|
||||
const OsdWindow = imports.ui.osdWindow;
|
||||
const OsdMonitorLabeler = imports.ui.osdMonitorLabeler;
|
||||
const Overview = imports.ui.overview;
|
||||
const PadOsd = imports.ui.padOsd;
|
||||
const Panel = imports.ui.panel;
|
||||
const Params = imports.misc.params;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
@ -61,6 +62,7 @@ let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let padOsdService = null;
|
||||
let osdWindowManager = null;
|
||||
let osdMonitorLabeler = null;
|
||||
let sessionMode = null;
|
||||
@ -155,6 +157,7 @@ function _initializeUI() {
|
||||
// working until it's updated.
|
||||
uiGroup = layoutManager.uiGroup;
|
||||
|
||||
padOsdService = new PadOsd.PadOsdService();
|
||||
screencastService = new Screencast.ScreencastService();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
|
@ -318,6 +318,7 @@ const Message = new Lang.Class({
|
||||
|
||||
this._iconBin = new St.Bin({ style_class: 'message-icon-bin',
|
||||
y_expand: true,
|
||||
y_align: St.Align.START,
|
||||
visible: false });
|
||||
hbox.add_actor(this._iconBin);
|
||||
|
||||
@ -331,18 +332,18 @@ const Message = new Lang.Class({
|
||||
let titleBox = new St.BoxLayout();
|
||||
contentBox.add_actor(titleBox);
|
||||
|
||||
this.titleLabel = new St.Label({ style_class: 'message-title',
|
||||
x_expand: true,
|
||||
x_align: Clutter.ActorAlign.START });
|
||||
this.titleLabel = new St.Label({ style_class: 'message-title' });
|
||||
this.setTitle(title);
|
||||
titleBox.add_actor(this.titleLabel);
|
||||
|
||||
this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin' });
|
||||
this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin',
|
||||
x_expand: true, y_expand: true,
|
||||
x_fill: true, y_fill: true });
|
||||
titleBox.add_actor(this._secondaryBin);
|
||||
|
||||
let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic',
|
||||
icon_size: 16 });
|
||||
this._closeButton = new St.Button({ child: closeIcon, visible: false });
|
||||
this._closeButton = new St.Button({ child: closeIcon, opacity: 0 });
|
||||
titleBox.add_actor(this._closeButton);
|
||||
|
||||
this._bodyStack = new St.Widget({ x_expand: true });
|
||||
@ -493,9 +494,8 @@ const Message = new Lang.Class({
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let hovered = this.actor.hover;
|
||||
this._closeButton.visible = hovered && this.canClose();
|
||||
this._secondaryBin.visible = !hovered;
|
||||
let visible = this.actor.hover && this.canClose();
|
||||
this._closeButton.opacity = visible ? 255 : 0;
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
@ -520,32 +520,10 @@ Signals.addSignalMethods(Message.prototype);
|
||||
const MessageListSection = new Lang.Class({
|
||||
Name: 'MessageListSection',
|
||||
|
||||
_init: function(title) {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'message-list-section',
|
||||
clip_to_allocation: true,
|
||||
x_expand: true, vertical: true });
|
||||
let titleBox = new St.BoxLayout({ style_class: 'message-list-section-title-box' });
|
||||
this.actor.add_actor(titleBox);
|
||||
|
||||
this._title = new St.Button({ style_class: 'message-list-section-title',
|
||||
label: title,
|
||||
can_focus: true,
|
||||
x_expand: true,
|
||||
x_align: St.Align.START });
|
||||
titleBox.add_actor(this._title);
|
||||
|
||||
this._title.connect('clicked', Lang.bind(this, this._onTitleClicked));
|
||||
this._title.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
|
||||
let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic' });
|
||||
this._closeButton = new St.Button({ style_class: 'message-list-section-close',
|
||||
child: closeIcon,
|
||||
accessible_name: _("Clear section"),
|
||||
can_focus: true });
|
||||
this._closeButton.set_x_align(Clutter.ActorAlign.END);
|
||||
titleBox.add_actor(this._closeButton);
|
||||
|
||||
this._closeButton.connect('clicked', Lang.bind(this, this.clear));
|
||||
|
||||
this._list = new St.BoxLayout({ style_class: 'message-list-section-list',
|
||||
vertical: true });
|
||||
@ -563,14 +541,10 @@ const MessageListSection = new Lang.Class({
|
||||
this._messages = new Map();
|
||||
this._date = new Date();
|
||||
this.empty = true;
|
||||
this.canClear = false;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_onTitleClicked: function() {
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
_onKeyFocusIn: function(actor) {
|
||||
this.emit('key-focus-in', actor);
|
||||
},
|
||||
@ -719,7 +693,13 @@ const MessageListSection = new Lang.Class({
|
||||
if (changed)
|
||||
this.emit('empty-changed');
|
||||
|
||||
this._closeButton.visible = this._canClear();
|
||||
let canClear = this._canClear();
|
||||
changed = this.canClear !== canClear;
|
||||
this.canClear = canClear;
|
||||
|
||||
if (changed)
|
||||
this.emit('can-clear-changed');
|
||||
|
||||
this.actor.visible = this.allowed && this._shouldShow();
|
||||
}
|
||||
});
|
||||
|
@ -368,6 +368,7 @@ const Notification = new Lang.Class({
|
||||
secondaryGIcon: null,
|
||||
bannerMarkup: false,
|
||||
clear: false,
|
||||
datetime: null,
|
||||
soundName: null,
|
||||
soundFile: null });
|
||||
|
||||
@ -375,6 +376,11 @@ const Notification = new Lang.Class({
|
||||
this.bannerBodyText = banner;
|
||||
this.bannerBodyMarkup = params.bannerMarkup;
|
||||
|
||||
if (params.datetime)
|
||||
this.datetime = params.datetime;
|
||||
else
|
||||
this.datetime = GLib.DateTime.new_now_local();
|
||||
|
||||
if (params.gicon || params.clear)
|
||||
this.gicon = params.gicon;
|
||||
|
||||
@ -535,7 +541,8 @@ const NotificationBanner = new Lang.Class({
|
||||
|
||||
_addSecondaryIcon: function() {
|
||||
if (this.notification.secondaryGIcon) {
|
||||
let icon = new St.Icon({ gicon: this.notification.secondaryGIcon });
|
||||
let icon = new St.Icon({ gicon: this.notification.secondaryGIcon,
|
||||
x_align: Clutter.ActorAlign.END });
|
||||
this.setSecondaryActor(icon);
|
||||
}
|
||||
},
|
||||
@ -796,7 +803,7 @@ const Source = new Lang.Class({
|
||||
notification.acknowledged = false;
|
||||
this.pushNotification(notification);
|
||||
|
||||
if (this.policy.showBanners) {
|
||||
if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL) {
|
||||
this.emit('notify', notification);
|
||||
} else {
|
||||
notification.playSound();
|
||||
@ -1224,7 +1231,7 @@ const MessageTray = new Lang.Class({
|
||||
if (this._notificationState == State.HIDDEN) {
|
||||
let nextNotification = this._notificationQueue[0] || null;
|
||||
if (hasNotifications && nextNotification) {
|
||||
let limited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
|
||||
let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen;
|
||||
let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL);
|
||||
if (showNextNotification)
|
||||
this._showNotification();
|
||||
|
@ -36,6 +36,8 @@ const MprisPlayerIface = '<node> \
|
||||
<method name="PlayPause" /> \
|
||||
<method name="Next" /> \
|
||||
<method name="Previous" /> \
|
||||
<property name="CanGoNext" type="b" access="read" /> \
|
||||
<property name="CanGoPrevious" type="b" access="read" /> \
|
||||
<property name="CanPlay" type="b" access="read" /> \
|
||||
<property name="Metadata" type="a{sv}" access="read" /> \
|
||||
<property name="PlaybackStatus" type="s" access="read" /> \
|
||||
@ -57,7 +59,7 @@ const MediaMessage = new Lang.Class({
|
||||
this._icon = new St.Icon({ style_class: 'media-message-cover-icon' });
|
||||
this.setIcon(this._icon);
|
||||
|
||||
this.addMediaControl('media-skip-backward-symbolic',
|
||||
this._prevButton = this.addMediaControl('media-skip-backward-symbolic',
|
||||
Lang.bind(this, function() {
|
||||
this._player.previous();
|
||||
}));
|
||||
@ -67,7 +69,7 @@ const MediaMessage = new Lang.Class({
|
||||
this._player.playPause();
|
||||
}));
|
||||
|
||||
this.addMediaControl('media-skip-forward-symbolic',
|
||||
this._nextButton = this.addMediaControl('media-skip-forward-symbolic',
|
||||
Lang.bind(this, function() {
|
||||
this._player.next();
|
||||
}));
|
||||
@ -82,6 +84,10 @@ const MediaMessage = new Lang.Class({
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
_updateNavButton: function(button, sensitive) {
|
||||
button.reactive = sensitive;
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this.setTitle(this._player.trackArtists.join(', '));
|
||||
this.setBody(this._player.trackTitle);
|
||||
@ -99,6 +105,9 @@ const MediaMessage = new Lang.Class({
|
||||
let iconName = isPlaying ? 'media-playback-pause-symbolic'
|
||||
: 'media-playback-start-symbolic';
|
||||
this._playPauseButton.child.icon_name = iconName;
|
||||
|
||||
this._updateNavButton(this._prevButton, this._player.canGoPrevious);
|
||||
this._updateNavButton(this._nextButton, this._player.canGoNext);
|
||||
}
|
||||
});
|
||||
|
||||
@ -139,10 +148,18 @@ const MprisPlayer = new Lang.Class({
|
||||
this._playerProxy.PlayPauseRemote();
|
||||
},
|
||||
|
||||
get canGoNext() {
|
||||
return this._playerProxy.CanGoNext;
|
||||
},
|
||||
|
||||
next: function() {
|
||||
this._playerProxy.NextRemote();
|
||||
},
|
||||
|
||||
get canGoPrevious() {
|
||||
return this._playerProxy.CanGoPrevious;
|
||||
},
|
||||
|
||||
previous: function() {
|
||||
this._playerProxy.PreviousRemote();
|
||||
},
|
||||
@ -214,7 +231,7 @@ const MediaSection = new Lang.Class({
|
||||
Extends: MessageList.MessageListSection,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Media"));
|
||||
this.parent();
|
||||
|
||||
this._players = new Map();
|
||||
|
||||
|
@ -186,7 +186,7 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
return source;
|
||||
}
|
||||
|
||||
let source = new FdoNotificationDaemonSource(title, pid, sender, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
source = new FdoNotificationDaemonSource(title, pid, sender, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
|
||||
this._sources.push(source);
|
||||
source.connect('destroy', Lang.bind(this, function() {
|
||||
@ -600,7 +600,8 @@ const GtkNotificationDaemonNotification = new Lang.Class({
|
||||
"priority": priority,
|
||||
"buttons": buttons,
|
||||
"default-action": defaultAction,
|
||||
"default-action-target": defaultActionTarget } = notification;
|
||||
"default-action-target": defaultActionTarget,
|
||||
"timestamp": time } = notification;
|
||||
|
||||
if (priority) {
|
||||
let urgency = PRIORITY_URGENCY_MAP[priority.unpack()];
|
||||
@ -623,7 +624,8 @@ const GtkNotificationDaemonNotification = new Lang.Class({
|
||||
this._defaultActionTarget = defaultActionTarget;
|
||||
|
||||
this.update(title.unpack(), body ? body.unpack() : null,
|
||||
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null });
|
||||
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null,
|
||||
datetime : time ? GLib.DateTime.new_from_unix_local(time.unpack()) : null });
|
||||
},
|
||||
|
||||
_activateAction: function(namespacedActionId, target) {
|
||||
@ -690,6 +692,7 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||
throw new InvalidAppError();
|
||||
|
||||
this._notifications = {};
|
||||
this._notificationPending = false;
|
||||
|
||||
this.parent(this._app.get_name());
|
||||
},
|
||||
@ -702,27 +705,35 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||
return new MessageTray.NotificationApplicationPolicy(this._appId);
|
||||
},
|
||||
|
||||
_createApp: function() {
|
||||
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
|
||||
_createApp: function(callback) {
|
||||
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath, callback);
|
||||
},
|
||||
|
||||
activateAction: function(actionId, target) {
|
||||
let app = this._createApp();
|
||||
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
|
||||
|
||||
this._createApp(function (app, error) {
|
||||
if (error == null)
|
||||
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
|
||||
else
|
||||
logError(error, 'Failed to activate application proxy');
|
||||
});
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
open: function() {
|
||||
let app = this._createApp();
|
||||
app.ActivateRemote(getPlatformData());
|
||||
|
||||
this._createApp(function (app, error) {
|
||||
if (error == null)
|
||||
app.ActivateRemote(getPlatformData());
|
||||
else
|
||||
logError(error, 'Failed to open application proxy');
|
||||
});
|
||||
Main.overview.hide();
|
||||
Main.panel.closeCalendar();
|
||||
},
|
||||
|
||||
addNotification: function(notificationId, notificationParams, showBanner) {
|
||||
this._notificationPending = true;
|
||||
|
||||
if (this._notifications[notificationId])
|
||||
this._notifications[notificationId].destroy();
|
||||
|
||||
@ -736,6 +747,14 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||
this.notify(notification);
|
||||
else
|
||||
this.pushNotification(notification);
|
||||
|
||||
this._notificationPending = false;
|
||||
},
|
||||
|
||||
destroy: function(reason) {
|
||||
if (this._notificationPending)
|
||||
return;
|
||||
this.parent(reason);
|
||||
},
|
||||
|
||||
removeNotification: function(notificationId) {
|
||||
@ -847,6 +866,9 @@ const GtkNotificationDaemon = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
let timestamp = GLib.DateTime.new_now_local().to_unix();
|
||||
notification['timestamp'] = new GLib.Variant('x', timestamp);
|
||||
|
||||
source.addNotification(notificationId, notification, true);
|
||||
|
||||
invocation.return_value(null);
|
||||
|
@ -72,8 +72,8 @@ const OsdWindowConstraint = new Lang.Class({
|
||||
|
||||
// Recenter
|
||||
let [x, y] = actorBox.get_origin();
|
||||
actorBox.set_origin(Math.floor(x + width / 2 - size / 2),
|
||||
Math.floor(y + height / 2 - size / 2));
|
||||
actorBox.set_origin(Math.ceil(x + width / 2 - size / 2),
|
||||
Math.ceil(y + height / 2 - size / 2));
|
||||
}
|
||||
});
|
||||
|
||||
@ -109,8 +109,11 @@ const OsdWindow = new Lang.Class({
|
||||
this._reset();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
Lang.bind(this, this._relayout));
|
||||
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||
themeContext.connect('notify::scale-factor',
|
||||
Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
Main.uiGroup.add_child(this.actor);
|
||||
},
|
||||
|
||||
@ -188,7 +191,7 @@ const OsdWindow = new Lang.Class({
|
||||
this.setLevel(null);
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
_relayout: function() {
|
||||
/* assume 110x110 on a 640x480 display and scale from there */
|
||||
let monitor = Main.layoutManager.monitors[this._monitorIndex];
|
||||
if (!monitor)
|
||||
|
@ -107,9 +107,9 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._overviewCreated = true;
|
||||
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
accessible_name: _("Overview"),
|
||||
vertical: true });
|
||||
this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||
@ -378,7 +378,7 @@ const Overview = new Lang.Class({
|
||||
return null;
|
||||
|
||||
let window = windows[0];
|
||||
let clone = new Clutter.Clone({ source: window.get_texture(),
|
||||
let clone = new Clutter.Clone({ source: window,
|
||||
x: window.x, y: window.y });
|
||||
clone.source.connect('destroy', Lang.bind(this, function() {
|
||||
clone.destroy();
|
||||
|
900
js/ui/padOsd.js
Normal file
900
js/ui/padOsd.js
Normal file
@ -0,0 +1,900 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Rsvg = imports.gi.Rsvg;
|
||||
const GObject = imports.gi.GObject;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||
const Atk = imports.gi.Atk;
|
||||
const Cairo = imports.cairo;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Layout = imports.ui.layout;
|
||||
|
||||
const ACTIVE_COLOR = "#729fcf";
|
||||
|
||||
const LTR = 0;
|
||||
const RTL = 1;
|
||||
|
||||
const CW = 0;
|
||||
const CCW = 1;
|
||||
|
||||
const UP = 0;
|
||||
const DOWN = 1;
|
||||
|
||||
const PadChooser = new Lang.Class({
|
||||
Name: 'PadChooser',
|
||||
|
||||
_init: function (device, groupDevices) {
|
||||
this.actor = new St.Button({ style_class: 'pad-chooser-button',
|
||||
toggle_mode: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.currentDevice = device;
|
||||
this._padChooserMenu = null;
|
||||
|
||||
let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
|
||||
icon_name: 'pan-down-symbolic',
|
||||
accessible_role: Atk.Role.ARROW });
|
||||
this.actor.set_child(arrow);
|
||||
this._ensureMenu(groupDevices);
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('clicked', Lang.bind(this, function (actor) {
|
||||
if (actor.get_checked()) {
|
||||
if (this._padChooserMenu != null)
|
||||
this._padChooserMenu.open(true);
|
||||
else
|
||||
this.set_checked(false);
|
||||
} else {
|
||||
this._padChooserMenu.close(true);
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_ensureMenu: function (devices) {
|
||||
this._padChooserMenu = new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.TOP);
|
||||
this._padChooserMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); }));
|
||||
this._padChooserMenu.actor.hide();
|
||||
Main.uiGroup.add_actor(this._padChooserMenu.actor);
|
||||
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
let device = devices[i];
|
||||
if (device == this.currentDevice)
|
||||
continue;
|
||||
|
||||
this._padChooserMenu.addAction(device.get_device_name(), () => {
|
||||
this.emit('pad-selected', device);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function () {
|
||||
this._padChooserMenu.destroy();
|
||||
},
|
||||
|
||||
update: function (devices) {
|
||||
if (this._padChooserMenu)
|
||||
this._padChooserMenu.actor.destroy();
|
||||
this.actor.set_checked(false);
|
||||
this._ensureMenu(devices);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(PadChooser.prototype);
|
||||
|
||||
const KeybindingEntry = new Lang.Class({
|
||||
Name: 'KeybindingEntry',
|
||||
|
||||
_init: function () {
|
||||
this.actor = new St.Entry({ hint_text: _("New shortcut…"),
|
||||
style: 'width: 10em' });
|
||||
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
},
|
||||
|
||||
_onCapturedEvent: function (actor, event) {
|
||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let str = Gtk.accelerator_name_with_keycode(null,
|
||||
event.get_key_symbol(),
|
||||
event.get_key_code(),
|
||||
event.get_state());
|
||||
this.actor.set_text(str);
|
||||
this.emit('keybinding-edited', str);
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(KeybindingEntry.prototype);
|
||||
|
||||
const ActionComboBox = new Lang.Class({
|
||||
Name: 'ActionComboBox',
|
||||
|
||||
_init: function () {
|
||||
this.actor = new St.Button({ style_class: 'button' });
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onButtonClicked));
|
||||
this.actor.set_toggle_mode(true);
|
||||
|
||||
let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
|
||||
spacing: 6 });
|
||||
let box = new St.Widget({ layout_manager: boxLayout });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this._label = new St.Label({ style_class: 'combo-box-label' });
|
||||
box.add_child(this._label)
|
||||
|
||||
let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
|
||||
icon_name: 'pan-down-symbolic',
|
||||
accessible_role: Atk.Role.ARROW,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
box.add_child(arrow);
|
||||
|
||||
this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP);
|
||||
this._editMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); }));
|
||||
this._editMenu.actor.hide();
|
||||
Main.uiGroup.add_actor(this._editMenu.actor);
|
||||
|
||||
this._actionLabels = new Map();
|
||||
this._actionLabels.set(GDesktopEnums.PadButtonAction.NONE, _("Application defined"));
|
||||
this._actionLabels.set(GDesktopEnums.PadButtonAction.HELP, _("Show on-screen help"));
|
||||
this._actionLabels.set(GDesktopEnums.PadButtonAction.SWITCH_MONITOR, _("Switch monitor"));
|
||||
this._actionLabels.set(GDesktopEnums.PadButtonAction.KEYBINDING, _("Assign keystroke"));
|
||||
|
||||
for (let [action, label] of this._actionLabels.entries()) {
|
||||
let selectedAction = action;
|
||||
this._editMenu.addAction(label, Lang.bind(this, function() { this._onActionSelected(selectedAction) }));
|
||||
}
|
||||
|
||||
this.setAction(GDesktopEnums.PadButtonAction.NONE);
|
||||
},
|
||||
|
||||
_onActionSelected: function (action) {
|
||||
this.setAction(action);
|
||||
this.popdown();
|
||||
this.emit('action-selected', action);
|
||||
},
|
||||
|
||||
setAction: function (action) {
|
||||
this._label.set_text(this._actionLabels.get(action));
|
||||
},
|
||||
|
||||
popup: function () {
|
||||
this._editMenu.open(true);
|
||||
},
|
||||
|
||||
popdown: function () {
|
||||
this._editMenu.close(true);
|
||||
},
|
||||
|
||||
_onButtonClicked: function () {
|
||||
if (this.actor.get_checked())
|
||||
this.popup();
|
||||
else
|
||||
this.popdown();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ActionComboBox.prototype);
|
||||
|
||||
const ActionEditor = new Lang.Class({
|
||||
Name: 'ActionEditor',
|
||||
|
||||
_init: function () {
|
||||
let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
|
||||
spacing: 12 });
|
||||
|
||||
this.actor = new St.Widget({ layout_manager: boxLayout });
|
||||
|
||||
this._actionComboBox = new ActionComboBox();
|
||||
this._actionComboBox.connect('action-selected', Lang.bind(this, this._onActionSelected));
|
||||
this.actor.add_actor(this._actionComboBox.actor);
|
||||
|
||||
this._keybindingEdit = new KeybindingEntry();
|
||||
this._keybindingEdit.connect('keybinding-edited', Lang.bind(this, this._onKeybindingEdited));
|
||||
this.actor.add_actor(this._keybindingEdit.actor);
|
||||
|
||||
this._doneButton = new St.Button({ label: _("Done"),
|
||||
style_class: 'button',
|
||||
x_expand: false});
|
||||
this._doneButton.connect('clicked', Lang.bind(this, this._onEditingDone));
|
||||
this.actor.add_actor(this._doneButton);
|
||||
},
|
||||
|
||||
_updateKeybindingEntryState: function () {
|
||||
if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) {
|
||||
this._keybindingEdit.actor.set_text(this._currentKeybinding);
|
||||
this._keybindingEdit.actor.show();
|
||||
this._keybindingEdit.actor.grab_key_focus();
|
||||
} else {
|
||||
this._keybindingEdit.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
setSettings: function (settings) {
|
||||
this._buttonSettings = settings;
|
||||
|
||||
this._currentAction = this._buttonSettings.get_enum('action');
|
||||
this._currentKeybinding = this._buttonSettings.get_string('keybinding');
|
||||
this._actionComboBox.setAction(this._currentAction);
|
||||
this._updateKeybindingEntryState();
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this._actionComboBox.popdown();
|
||||
this.actor.hide();
|
||||
},
|
||||
|
||||
_onKeybindingEdited: function (entry, keybinding) {
|
||||
this._currentKeybinding = keybinding;
|
||||
},
|
||||
|
||||
_onActionSelected: function (menu, action) {
|
||||
this._currentAction = action;
|
||||
this._updateKeybindingEntryState();
|
||||
},
|
||||
|
||||
_storeSettings: function () {
|
||||
if (!this._buttonSettings)
|
||||
return;
|
||||
|
||||
let keybinding = null;
|
||||
|
||||
if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING)
|
||||
keybinding = this._currentKeybinding;
|
||||
|
||||
this._buttonSettings.set_enum('action', this._currentAction);
|
||||
|
||||
if (keybinding)
|
||||
this._buttonSettings.set_string('keybinding', keybinding);
|
||||
else
|
||||
this._buttonSettings.reset('keybinding');
|
||||
},
|
||||
|
||||
_onEditingDone: function () {
|
||||
this._storeSettings();
|
||||
this.close();
|
||||
this.emit('done');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ActionEditor.prototype);
|
||||
|
||||
const PadDiagram = new Lang.Class({
|
||||
Name: 'PadDiagram',
|
||||
Extends: St.DrawingArea,
|
||||
Properties: { 'left-handed': GObject.ParamSpec.boolean('left-handed',
|
||||
'left-handed', 'Left handed',
|
||||
GObject.ParamFlags.READWRITE |
|
||||
GObject.ParamFlags.CONSTRUCT_ONLY,
|
||||
false),
|
||||
'image': GObject.ParamSpec.string('image', 'image', 'Image',
|
||||
GObject.ParamFlags.READWRITE |
|
||||
GObject.ParamFlags.CONSTRUCT_ONLY,
|
||||
null),
|
||||
'editor-actor': GObject.ParamSpec.object('editor-actor',
|
||||
'editor-actor',
|
||||
'Editor actor',
|
||||
GObject.ParamFlags.READWRITE |
|
||||
GObject.ParamFlags.CONSTRUCT_ONLY,
|
||||
Clutter.Actor.$gtype) },
|
||||
|
||||
_init: function (params) {
|
||||
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/pad-osd.css');
|
||||
let [success, css, etag] = file.load_contents(null);
|
||||
this._css = css;
|
||||
this._labels = [];
|
||||
this._activeButtons = [];
|
||||
this.parent(params);
|
||||
},
|
||||
|
||||
get left_handed() {
|
||||
return this._leftHanded;
|
||||
},
|
||||
|
||||
set left_handed(leftHanded) {
|
||||
this._leftHanded = leftHanded;
|
||||
},
|
||||
|
||||
get image() {
|
||||
return this._imagePath;
|
||||
},
|
||||
|
||||
set image(imagePath) {
|
||||
let originalHandle = Rsvg.Handle.new_from_file(imagePath);
|
||||
let dimensions = originalHandle.get_dimensions();
|
||||
this._imageWidth = dimensions.width;
|
||||
this._imageHeight = dimensions.height;
|
||||
|
||||
this._imagePath = imagePath;
|
||||
this._handle = this._composeStyledDiagram();
|
||||
},
|
||||
|
||||
get editor_actor() {
|
||||
return this._editorActor;
|
||||
},
|
||||
|
||||
set editor_actor(actor) {
|
||||
actor.hide();
|
||||
this._editorActor = actor;
|
||||
this.add_actor(actor);
|
||||
},
|
||||
|
||||
_wrappingSvgHeader: function () {
|
||||
return ('<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
|
||||
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
|
||||
'xmlns:xi="http://www.w3.org/2001/XInclude" ' +
|
||||
'width="' + this._imageWidth + '" height="' + this._imageHeight + '"> ' +
|
||||
'<style type="text/css">');
|
||||
},
|
||||
|
||||
_wrappingSvgFooter: function () {
|
||||
return ('</style>' +
|
||||
'<xi:include href="' + this._imagePath + '" />' +
|
||||
'</svg>');
|
||||
},
|
||||
|
||||
_cssString: function () {
|
||||
let css = this._css;
|
||||
|
||||
for (let i = 0; i < this._activeButtons.length; i++) {
|
||||
let ch = String.fromCharCode('A'.charCodeAt() + this._activeButtons[i]);
|
||||
css += ('.' + ch + ' { ' +
|
||||
' stroke: ' + ACTIVE_COLOR + ' !important; ' +
|
||||
' fill: ' + ACTIVE_COLOR + ' !important; ' +
|
||||
'} ');
|
||||
}
|
||||
|
||||
return css;
|
||||
},
|
||||
|
||||
_composeStyledDiagram: function () {
|
||||
let svgData = '';
|
||||
|
||||
if (!GLib.file_test(this._imagePath, GLib.FileTest.EXISTS))
|
||||
return null;
|
||||
|
||||
svgData += this._wrappingSvgHeader();
|
||||
svgData += this._cssString();
|
||||
svgData += this._wrappingSvgFooter();
|
||||
|
||||
let handle = new Rsvg.Handle();
|
||||
handle.set_base_uri(GLib.path_get_dirname(this._imagePath));
|
||||
handle.write(svgData);
|
||||
handle.close();
|
||||
|
||||
return handle;
|
||||
},
|
||||
|
||||
_updateDiagramScale: function () {
|
||||
if (this._handle == null)
|
||||
return;
|
||||
|
||||
[this._actorWidth, this._actorHeight] = this.get_size();
|
||||
let dimensions = this._handle.get_dimensions();
|
||||
let scaleX = this._actorWidth / dimensions.width;
|
||||
let scaleY = this._actorHeight / dimensions.height;
|
||||
this._scale = Math.min(scaleX, scaleY);
|
||||
},
|
||||
|
||||
_allocateChild: function (child, x, y, direction) {
|
||||
let [prefHeight, natHeight] = child.get_preferred_height(-1);
|
||||
let [prefWidth, natWidth] = child.get_preferred_width(natHeight);
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
if (direction == LTR) {
|
||||
childBox.x1 = x;
|
||||
childBox.x2 = x + natWidth;
|
||||
} else {
|
||||
childBox.x1 = x - natWidth;
|
||||
childBox.x2 = x;
|
||||
}
|
||||
|
||||
childBox.y1 = y - natHeight / 2;
|
||||
childBox.y2 = y + natHeight / 2;
|
||||
child.allocate(childBox, 0);
|
||||
},
|
||||
|
||||
vfunc_allocate: function (box, flags) {
|
||||
this.parent(box, flags);
|
||||
this._updateDiagramScale();
|
||||
|
||||
for (let i = 0; i < this._labels.length; i++) {
|
||||
let [label, action, idx, dir] = this._labels[i];
|
||||
let [found, x, y, arrangement] = this.getLabelCoords(action, idx, dir);
|
||||
this._allocateChild(label, x, y, arrangement);
|
||||
}
|
||||
|
||||
if (this._editorActor && this._curEdited) {
|
||||
let [label, action, idx, dir] = this._curEdited;
|
||||
let [found, x, y, arrangement] = this.getLabelCoords(action, idx, dir);
|
||||
this._allocateChild(this._editorActor, x, y, arrangement);
|
||||
}
|
||||
},
|
||||
|
||||
vfunc_repaint: function () {
|
||||
if (this._handle == null)
|
||||
return;
|
||||
|
||||
if (this._scale == null)
|
||||
this._updateDiagramScale();
|
||||
|
||||
let [width, height] = this.get_surface_size();
|
||||
let dimensions = this._handle.get_dimensions();
|
||||
let cr = this.get_context();
|
||||
|
||||
cr.save();
|
||||
cr.translate(width/2, height/2);
|
||||
cr.scale(this._scale, this._scale);
|
||||
if (this._leftHanded)
|
||||
cr.rotate(Math.PI);
|
||||
cr.translate(-dimensions.width/2, -dimensions.height/2);
|
||||
this._handle.render_cairo(cr);
|
||||
cr.restore();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_transformPoint: function (x, y) {
|
||||
if (this._handle == null || this._scale == null)
|
||||
return [x, y];
|
||||
|
||||
// I miss Cairo.Matrix
|
||||
let dimensions = this._handle.get_dimensions();
|
||||
x = x * this._scale + this._actorWidth / 2 - dimensions.width / 2 * this._scale;
|
||||
y = y * this._scale + this._actorHeight / 2 - dimensions.height / 2 * this._scale;;
|
||||
return [Math.round(x), Math.round(y)];
|
||||
},
|
||||
|
||||
_getItemLabelCoords: function (labelName, leaderName) {
|
||||
if (this._handle == null)
|
||||
return [false];
|
||||
|
||||
let leaderPos, leaderSize, pos;
|
||||
let found, direction;
|
||||
|
||||
[found, pos] = this._handle.get_position_sub('#' + labelName);
|
||||
if (!found)
|
||||
return [false];
|
||||
|
||||
[found, leaderPos] = this._handle.get_position_sub('#' + leaderName);
|
||||
[found, leaderSize] = this._handle.get_dimensions_sub('#' + leaderName);
|
||||
if (!found)
|
||||
return [false];
|
||||
|
||||
if (pos.x > leaderPos.x + leaderSize.width)
|
||||
direction = LTR;
|
||||
else
|
||||
direction = RTL;
|
||||
|
||||
if (this._leftHanded) {
|
||||
direction = 1 - direction;
|
||||
pos.x = this._imageWidth - pos.x;
|
||||
pos.y = this._imageHeight - pos.y;
|
||||
}
|
||||
|
||||
let [x, y] = this._transformPoint(pos.x, pos.y)
|
||||
|
||||
return [true, x, y, direction];
|
||||
},
|
||||
|
||||
getButtonLabelCoords: function (button) {
|
||||
let ch = String.fromCharCode('A'.charCodeAt() + button);
|
||||
let labelName = 'Label' + ch;
|
||||
let leaderName = 'Leader' + ch;
|
||||
|
||||
return this._getItemLabelCoords(labelName, leaderName);
|
||||
},
|
||||
|
||||
getRingLabelCoords: function (number, dir) {
|
||||
let numStr = number > 0 ? number.toString() : '';
|
||||
let dirStr = dir == CW ? 'CW' : 'CCW';
|
||||
let labelName = 'LabelRing' + numStr + dirStr;
|
||||
let leaderName = 'LeaderRing' + numStr + dirStr;
|
||||
|
||||
return this._getItemLabelCoords(labelName, leaderName);
|
||||
},
|
||||
|
||||
getStripLabelCoords: function (number, dir) {
|
||||
let numStr = number > 0 ? (number + 1).toString() : '';
|
||||
let dirStr = dir == UP ? 'Up' : 'Down';
|
||||
let labelName = 'LabelStrip' + numStr + dirStr;
|
||||
let leaderName = 'LeaderStrip' + numStr + dirStr;
|
||||
|
||||
return this._getItemLabelCoords(labelName, leaderName);
|
||||
},
|
||||
|
||||
getLabelCoords: function (action, idx, dir) {
|
||||
if (action == Meta.PadActionType.BUTTON)
|
||||
return this.getButtonLabelCoords(idx);
|
||||
else if (action == Meta.PadActionType.RING)
|
||||
return this.getRingLabelCoords(idx, dir);
|
||||
else if (action == Meta.PadActionType.STRIP)
|
||||
return this.getStripLabelCoords(idx, dir);
|
||||
|
||||
return [false];
|
||||
},
|
||||
|
||||
_invalidateSvg: function () {
|
||||
if (this._handle == null)
|
||||
return;
|
||||
this._handle = this._composeStyledDiagram();
|
||||
this.queue_repaint();
|
||||
},
|
||||
|
||||
activateButton: function (button) {
|
||||
this._activeButtons.push(button);
|
||||
this._invalidateSvg();
|
||||
},
|
||||
|
||||
deactivateButton: function (button) {
|
||||
for (let i = 0; i < this._activeButtons.length; i++) {
|
||||
if (this._activeButtons[i] == button)
|
||||
this._activeButtons.splice(i, 1);
|
||||
}
|
||||
this._invalidateSvg();
|
||||
},
|
||||
|
||||
addLabel: function (label, type, idx, dir) {
|
||||
this._labels.push([label, type, idx, dir]);
|
||||
this.add_actor(label);
|
||||
},
|
||||
|
||||
stopEdition: function (str) {
|
||||
this._editorActor.hide();
|
||||
|
||||
if (this._curEdited) {
|
||||
let [label, action, idx, dir] = this._curEdited;
|
||||
if (str != null) {
|
||||
label.set_text(str);
|
||||
|
||||
let [found, x, y, arrangement] = this.getLabelCoords(action, idx, dir);
|
||||
this._allocateChild(label, x, y, arrangement);
|
||||
}
|
||||
label.show();
|
||||
this._curEdited = null;
|
||||
}
|
||||
},
|
||||
|
||||
startEdition: function(action, idx, dir) {
|
||||
let editedLabel;
|
||||
this.stopEdition();
|
||||
|
||||
for (let i = 0; i < this._labels.length; i++) {
|
||||
let [label, itemAction, itemIdx, itemDir] = this._labels[i];
|
||||
if (action == itemAction && idx == itemIdx && dir == itemDir) {
|
||||
this._curEdited = this._labels[i];
|
||||
editedLabel = label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._curEdited == null)
|
||||
return;
|
||||
let [found] = this.getLabelCoords(action, idx, dir);
|
||||
if (!found)
|
||||
return;
|
||||
this._editorActor.show();
|
||||
editedLabel.hide();
|
||||
}
|
||||
});
|
||||
|
||||
const PadOsd = new Lang.Class({
|
||||
Name: 'PadOsd',
|
||||
|
||||
_init: function (padDevice, settings, imagePath, editionMode, monitorIndex) {
|
||||
this.padDevice = padDevice;
|
||||
this._groupPads = [ padDevice ];
|
||||
this._settings = settings;
|
||||
this._imagePath = imagePath;
|
||||
this._editionMode = editionMode;
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
this._padChooser = null;
|
||||
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
this._deviceAddedId = deviceManager.connect('device-added', Lang.bind(this, function (manager, device) {
|
||||
if (device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE &&
|
||||
this.padDevice.is_grouped(device)) {
|
||||
this._groupPads.push(device);
|
||||
this._updatePadChooser();
|
||||
}
|
||||
}));
|
||||
this._deviceRemovedId = deviceManager.connect('device-removed', Lang.bind(this, function (manager, device) {
|
||||
// If the device is being removed, destroy the padOsd.
|
||||
if (device == this.padDevice) {
|
||||
this.destroy();
|
||||
} else if (this._groupPads.indexOf(device) != -1) {
|
||||
// Or update the pad chooser if the device belongs to
|
||||
// the same group.
|
||||
this._groupPads.splice(this._groupPads.indexOf(device), 1);
|
||||
this._updatePadChooser();
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
deviceManager.list_devices().forEach(Lang.bind(this, function(device) {
|
||||
if (device != this.padDevice &&
|
||||
device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE &&
|
||||
this.padDevice.is_grouped(device))
|
||||
this._groupPads.push(device);
|
||||
}));
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'pad-osd-window',
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
vertical: true,
|
||||
reactive: true });
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
|
||||
this._monitorIndex = monitorIndex;
|
||||
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this._titleBox = new St.BoxLayout({ style_class: 'pad-osd-title-box',
|
||||
vertical: false,
|
||||
x_expand: false,
|
||||
x_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_actor(this._titleBox);
|
||||
|
||||
let labelBox = new St.BoxLayout({ style_class: 'pad-osd-title-menu-box',
|
||||
vertical: true });
|
||||
this._titleBox.add_actor(labelBox);
|
||||
|
||||
this._titleLabel = new St.Label({ style: 'font-side: larger; font-weight: bold;',
|
||||
x_align: Clutter.ActorAlign.CENTER });
|
||||
this._titleLabel.clutter_text.set_text(padDevice.get_device_name());
|
||||
labelBox.add_actor(this._titleLabel);
|
||||
|
||||
this._tipLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER });
|
||||
labelBox.add_actor(this._tipLabel);
|
||||
|
||||
this._updatePadChooser();
|
||||
|
||||
this._actionEditor = new ActionEditor();
|
||||
this._actionEditor.connect('done', Lang.bind(this, this._endButtonActionEdition));
|
||||
|
||||
this._padDiagram = new PadDiagram({ image: this._imagePath,
|
||||
left_handed: settings.get_boolean('left-handed'),
|
||||
editor_actor: this._actionEditor.actor,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
this.actor.add_actor(this._padDiagram);
|
||||
|
||||
// FIXME: Fix num buttons.
|
||||
let i = 0;
|
||||
for (i = 0; i < 50; i++) {
|
||||
let [found] = this._padDiagram.getButtonLabelCoords(i);
|
||||
if (!found)
|
||||
break;
|
||||
this._createLabel(Meta.PadActionType.BUTTON, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < padDevice.get_n_rings(); i++) {
|
||||
this._createLabel(Meta.PadActionType.RING, i, CW);
|
||||
this._createLabel(Meta.PadActionType.RING, i, CCW);
|
||||
}
|
||||
|
||||
for (i = 0; i < padDevice.get_n_strips(); i++) {
|
||||
this._createLabel(Meta.PadActionType.STRIP, i, UP);
|
||||
this._createLabel(Meta.PadActionType.STRIP, i, DOWN);
|
||||
}
|
||||
|
||||
let buttonBox = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_actor(buttonBox);
|
||||
this._editButton = new St.Button({ label: _("Edit…"),
|
||||
style_class: 'button',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
can_focus: true });
|
||||
this._editButton.connect('clicked', Lang.bind(this, function () { this.setEditionMode(true) }));
|
||||
buttonBox.add_actor(this._editButton);
|
||||
|
||||
this._syncEditionMode();
|
||||
Main.pushModal(this.actor);
|
||||
},
|
||||
|
||||
_updatePadChooser: function () {
|
||||
if (this._groupPads.length > 1) {
|
||||
if (this._padChooser == null) {
|
||||
this._padChooser = new PadChooser(this.padDevice, this._groupPads)
|
||||
this._padChooser.connect('pad-selected', Lang.bind(this, function (chooser, pad) {
|
||||
this._requestForOtherPad(pad);
|
||||
}));
|
||||
this._titleBox.add_child(this._padChooser.actor);
|
||||
} else {
|
||||
this._padChooser.update(this._groupPads);
|
||||
}
|
||||
} else if (this._padChooser != null) {
|
||||
this._padChooser.destroy();
|
||||
this._padChooser = null;
|
||||
}
|
||||
},
|
||||
|
||||
_requestForOtherPad: function (pad) {
|
||||
if (pad == this.padDevice ||
|
||||
this._groupPads.indexOf(pad) == -1)
|
||||
return;
|
||||
|
||||
let editionMode = this._editionMode;
|
||||
this.destroy();
|
||||
global.display.request_pad_osd(pad, editionMode);
|
||||
},
|
||||
|
||||
_createLabel: function (type, number, dir) {
|
||||
let str = global.display.get_pad_action_label(this.padDevice, type, number);
|
||||
let label = new St.Label({ text: str ? str : _("None") });
|
||||
this._padDiagram.addLabel(label, type, number, dir);
|
||||
},
|
||||
|
||||
_onCapturedEvent : function (actor, event) {
|
||||
if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS &&
|
||||
event.get_source_device() == this.padDevice) {
|
||||
this._padDiagram.activateButton(event.get_button());
|
||||
|
||||
if (this._editionMode)
|
||||
this._startButtonActionEdition(event.get_button());
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (event.type() == Clutter.EventType.PAD_BUTTON_RELEASE &&
|
||||
event.get_source_device() == this.padDevice) {
|
||||
this._padDiagram.deactivateButton(event.get_button());
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (event.type() == Clutter.EventType.KEY_PRESS &&
|
||||
(!this._editionMode || event.get_key_symbol() == Clutter.Escape)) {
|
||||
if (this._editingButtonAction != null)
|
||||
this._endButtonActionEdition();
|
||||
else
|
||||
this.destroy();
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
// If the event comes from another pad in the same group,
|
||||
// show the OSD for it.
|
||||
if (this._groupPads.indexOf(event.get_source_device()) != -1) {
|
||||
this._requestForOtherPad(event.get_source_device());
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_syncEditionMode: function () {
|
||||
this._editButton.set_reactive(!this._editionMode);
|
||||
this._editButton.save_easing_state();
|
||||
this._editButton.set_easing_duration(200);
|
||||
this._editButton.set_opacity(this._editionMode ? 128 : 255);
|
||||
this._editButton.restore_easing_state();
|
||||
|
||||
let title;
|
||||
|
||||
if (this._editionMode) {
|
||||
title = _("Press a button to configure");
|
||||
this._tipLabel.set_text(_("Press Esc to exit"));
|
||||
} else {
|
||||
title = this.padDevice.get_device_name();
|
||||
this._tipLabel.set_text(_("Press any key to exit"));
|
||||
}
|
||||
|
||||
this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + title + '</b></span>');
|
||||
},
|
||||
|
||||
_endButtonActionEdition: function () {
|
||||
this._actionEditor.close();
|
||||
|
||||
if (this._editingButtonAction != null) {
|
||||
let str = global.display.get_pad_action_label(this.padDevice,
|
||||
Meta.PadActionType.BUTTON,
|
||||
this._editingButtonAction);
|
||||
this._padDiagram.stopEdition(str ? str : _("None"))
|
||||
this._editingButtonAction = null;
|
||||
}
|
||||
|
||||
this._editedButtonSettings = null;
|
||||
},
|
||||
|
||||
_startButtonActionEdition: function (button) {
|
||||
if (this._editingButtonAction == button)
|
||||
return;
|
||||
|
||||
this._endButtonActionEdition();
|
||||
this._editingButtonAction = button;
|
||||
|
||||
let ch = String.fromCharCode('A'.charCodeAt() + button);
|
||||
let settingsPath = this._settings.path + "button" + ch + '/';
|
||||
this._editedButtonSettings = Gio.Settings.new_with_path('org.gnome.desktop.peripherals.tablet.pad-button',
|
||||
settingsPath);
|
||||
this._actionEditor.setSettings(this._editedButtonSettings);
|
||||
this._padDiagram.startEdition(Meta.PadActionType.BUTTON, button);
|
||||
},
|
||||
|
||||
setEditionMode: function (editionMode) {
|
||||
if (this._editionMode == editionMode)
|
||||
return;
|
||||
|
||||
this._editionMode = editionMode;
|
||||
this._syncEditionMode();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy: function () {
|
||||
Main.popModal(this.actor);
|
||||
this._actionEditor.close();
|
||||
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
if (this._deviceRemovedId != 0) {
|
||||
deviceManager.disconnect(this._deviceRemovedId);
|
||||
this._deviceRemovedId = 0;
|
||||
}
|
||||
if (this._deviceAddedId != 0) {
|
||||
deviceManager.disconnect(this._deviceAddedId);
|
||||
this._deviceAddedId = 0;
|
||||
}
|
||||
|
||||
if (this._capturedEventId != 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
this.actor = null;
|
||||
this.emit('closed');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PadOsd.prototype);
|
||||
|
||||
const PadOsdIface = '<node> \
|
||||
<interface name="org.gnome.Shell.Wacom.PadOsd"> \
|
||||
<method name="Show"> \
|
||||
<arg name="device_node" direction="in" type="o"/> \
|
||||
<arg name="edition_mode" direction="in" type="b"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PadOsdService = new Lang.Class({
|
||||
Name: 'PadOsdService',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(PadOsdIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Wacom');
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.Wacom.PadOsd', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
ShowAsync: function(params, invocation) {
|
||||
let [deviceNode, editionMode] = params;
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
let devices = deviceManager.list_devices();
|
||||
let padDevice = null;
|
||||
|
||||
devices.forEach(Lang.bind(this, function(device) {
|
||||
if (deviceNode == device.get_device_node())
|
||||
padDevice = device;
|
||||
}));
|
||||
|
||||
if (padDevice == null ||
|
||||
padDevice.get_device_type() != Clutter.InputDeviceType.PAD_DEVICE) {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum,
|
||||
Gio.IOErrorEnum.CANCELLED,
|
||||
"Invalid params");
|
||||
return;
|
||||
}
|
||||
|
||||
global.display.request_pad_osd(padDevice, editionMode);
|
||||
invocation.return_value(null);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PadOsdService.prototype);
|
@ -719,9 +719,11 @@ const AggregateMenu = new Lang.Class({
|
||||
this._system = new imports.ui.status.system.Indicator();
|
||||
this._screencast = new imports.ui.status.screencast.Indicator();
|
||||
this._location = new imports.ui.status.location.Indicator();
|
||||
this._nightLight = new imports.ui.status.nightLight.Indicator();
|
||||
|
||||
this._indicators.add_child(this._screencast.indicators);
|
||||
this._indicators.add_child(this._location.indicators);
|
||||
this._indicators.add_child(this._nightLight.indicators);
|
||||
if (this._network) {
|
||||
this._indicators.add_child(this._network.indicators);
|
||||
}
|
||||
@ -745,6 +747,7 @@ const AggregateMenu = new Lang.Class({
|
||||
this.menu.addMenuItem(this._location.menu);
|
||||
this.menu.addMenuItem(this._rfkill.menu);
|
||||
this.menu.addMenuItem(this._power.menu);
|
||||
this.menu.addMenuItem(this._nightLight.menu);
|
||||
this.menu.addMenuItem(this._system.menu);
|
||||
|
||||
menuLayout.addSizeChild(this._location.menu.actor);
|
||||
|
@ -605,6 +605,24 @@ const PopupMenuBase = new Lang.Class({
|
||||
menuItem.actor.show();
|
||||
},
|
||||
|
||||
moveMenuItem: function(menuItem, position) {
|
||||
let items = this._getMenuItems();
|
||||
let i = 0;
|
||||
|
||||
while (i < items.length && position > 0) {
|
||||
if (items[i] != menuItem)
|
||||
position--;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < items.length) {
|
||||
if (items[i] != menuItem)
|
||||
this.box.set_child_below_sibling(menuItem.actor, items[i].actor);
|
||||
} else {
|
||||
this.box.set_child_above_sibling(menuItem.actor, null);
|
||||
}
|
||||
},
|
||||
|
||||
addMenuItem: function(menuItem, position) {
|
||||
let before_item = null;
|
||||
if (position == undefined) {
|
||||
@ -783,6 +801,16 @@ const PopupMenu = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
|
||||
let state = event.get_state();
|
||||
|
||||
// if user has a modifier down (except capslock)
|
||||
// then don't handle the key press here
|
||||
state &= ~Clutter.ModifierType.LOCK_MASK;
|
||||
state &= Clutter.ModifierType.MODIFIER_MASK;
|
||||
|
||||
if (state)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
this.toggle();
|
||||
|
@ -297,7 +297,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
// the provider is not compatible with the new version of the interface, launch
|
||||
// the app itself but warn so we can catch the error in logs
|
||||
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
|
||||
this.appInfo.launch([], global.create_app_launch_context(0, -1));
|
||||
this.appInfo.launch([], global.create_app_launch_context(0, -1), false);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -274,7 +274,7 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
_restart: function() {
|
||||
if (Meta.is_wayland_compositor()) {
|
||||
this._showError('Restart is not available on Wayland');
|
||||
this._showError(_("Restart is not available on Wayland"));
|
||||
return;
|
||||
}
|
||||
this._shouldFadeOut = false;
|
||||
|
@ -665,7 +665,10 @@ const ScreenShield = new Lang.Class({
|
||||
let isEnter = (symbol == Clutter.KEY_Return ||
|
||||
symbol == Clutter.KEY_KP_Enter ||
|
||||
symbol == Clutter.KEY_ISO_Enter);
|
||||
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
|
||||
let isEscape = (symbol == Clutter.KEY_Escape);
|
||||
let isLiftChar = (GLib.unichar_isprint(unichar) &&
|
||||
(this._isLocked || !GLib.unichar_isgraph(unichar)));
|
||||
if (!isEnter && !isEscape && !isLiftChar)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._isLocked &&
|
||||
@ -847,6 +850,7 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_activateFade: function(lightbox, time) {
|
||||
Main.uiGroup.set_child_above_sibling(lightbox.actor, null);
|
||||
lightbox.show(time);
|
||||
|
||||
if (this._becameActiveId == 0)
|
||||
|
@ -380,7 +380,7 @@ const ShellMountPasswordDialog = new Lang.Class({
|
||||
this.setInitialKeyFocus(this._passwordEntry);
|
||||
|
||||
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
|
||||
text: _("Sorry, that didn\'t work. Please try again.") });
|
||||
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();
|
||||
|
@ -137,7 +137,7 @@ const Indicator = new Lang.Class({
|
||||
else if (nConnectedDevices == -1)
|
||||
this._item.label.text = _("Off");
|
||||
else
|
||||
this._item.label.text = _("Not In Use");
|
||||
this._item.label.text = _("On");
|
||||
|
||||
this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _("Turn On") : _("Turn Off");
|
||||
},
|
||||
|
@ -284,6 +284,7 @@ const AppAuthorizer = new Lang.Class({
|
||||
this.reqAccuracyLevel = reqAccuracyLevel;
|
||||
this._permStoreProxy = permStoreProxy;
|
||||
this._maxAccuracyLevel = maxAccuracyLevel;
|
||||
this._permissions = {};
|
||||
|
||||
this._accuracyLevel = GeoclueAccuracyLevel.NONE;
|
||||
},
|
||||
|
@ -5,6 +5,7 @@ const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
const NMGtk = imports.gi.NMGtk;
|
||||
@ -39,6 +40,8 @@ const NMAccessPointSecurity = {
|
||||
WPA2_ENT: 6
|
||||
};
|
||||
|
||||
const MAX_DEVICE_ITEMS = 4;
|
||||
|
||||
// small optimization, to avoid using [] all the time
|
||||
const NM80211Mode = NetworkManager['80211Mode'];
|
||||
const NM80211ApFlags = NetworkManager['80211ApFlags'];
|
||||
@ -296,11 +299,22 @@ const NMConnectionSection = new Lang.Class({
|
||||
|
||||
let item = this._connectionItems.get(connection.get_uuid());
|
||||
if (item)
|
||||
item.updateForConnection(connection);
|
||||
this._updateForConnection(item, connection);
|
||||
else
|
||||
this._addConnection(connection);
|
||||
},
|
||||
|
||||
_updateForConnection: function(item, connection) {
|
||||
let pos = this._connections.indexOf(connection);
|
||||
|
||||
this._connections.splice(pos, 1);
|
||||
pos = Util.insertSorted(this._connections, connection, Lang.bind(this, this._connectionSortFunction));
|
||||
this._labelSection.moveMenuItem(item.labelItem, pos);
|
||||
this._radioSection.moveMenuItem(item.radioItem, pos);
|
||||
|
||||
item.updateForConnection(connection);
|
||||
},
|
||||
|
||||
_addConnection: function(connection) {
|
||||
let item = this._makeConnectionItem(connection);
|
||||
if (!item)
|
||||
@ -752,10 +766,9 @@ const NMWirelessDialog = new Lang.Class({
|
||||
this._updateSensitivity();
|
||||
this._syncView();
|
||||
|
||||
if (accessPoints.length == 0) {
|
||||
/* If there are no visible access points, request a scan */
|
||||
this._device.request_scan_simple(null);
|
||||
}
|
||||
this._scanTimeoutId = Mainloop.timeout_add_seconds(15, Lang.bind(this, this._onScanTimeout));
|
||||
GLib.Source.set_name_by_id(this._scanTimeoutId, '[gnome-shell] this._onScanTimeout');
|
||||
this._onScanTimeout();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -780,9 +793,19 @@ const NMWirelessDialog = new Lang.Class({
|
||||
this._airplaneModeChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._scanTimeoutId) {
|
||||
Mainloop.source_remove(this._scanTimeoutId);
|
||||
this._scanTimeoutId = 0;
|
||||
}
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_onScanTimeout: function() {
|
||||
this._device.request_scan_simple(null);
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
},
|
||||
|
||||
_activeApChanged: function() {
|
||||
if (this._activeNetwork)
|
||||
this._activeNetwork.item.setActive(false);
|
||||
@ -1408,7 +1431,7 @@ const NMVPNConnectionItem = new Lang.Class({
|
||||
case NetworkManager.VPNConnectionState.PREPARE:
|
||||
case NetworkManager.VPNConnectionState.CONNECT:
|
||||
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
|
||||
return _("connecting...");
|
||||
return _("connecting…");
|
||||
case NetworkManager.VPNConnectionState.NEED_AUTH:
|
||||
/* Translators: this is for network connections that require some kind of key or password */
|
||||
return _("authentication required");
|
||||
@ -1491,7 +1514,7 @@ const NMVPNSection = new Lang.Class({
|
||||
if (nItems > 1) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let app = appSys.lookup_app('gnome-network-panel.desktop');
|
||||
app.launch(0, -1);
|
||||
app.launch(0, -1, false);
|
||||
} else {
|
||||
let connection = this._connections[0];
|
||||
Util.spawnApp(['gnome-control-center', 'network', 'show-device',
|
||||
@ -1531,8 +1554,10 @@ const NMVPNSection = new Lang.Class({
|
||||
item.setActiveConnection(null);
|
||||
}
|
||||
vpnConnections.forEach(Lang.bind(this, function(a) {
|
||||
let item = this._connectionItems.get(a._connection.get_uuid());
|
||||
item.setActiveConnection(a);
|
||||
if (a._connection) {
|
||||
let item = this._connectionItems.get(a._connection.get_uuid());
|
||||
item.setActiveConnection(a);
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
@ -1552,6 +1577,73 @@ const NMVPNSection = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(NMVPNSection.prototype);
|
||||
|
||||
const DeviceCategory = new Lang.Class({
|
||||
Name: 'DeviceCategory',
|
||||
Extends: PopupMenu.PopupMenuSection,
|
||||
|
||||
_init: function(category) {
|
||||
this.parent();
|
||||
|
||||
this._category = category;
|
||||
|
||||
this.devices = [];
|
||||
|
||||
this.section = new PopupMenu.PopupMenuSection();
|
||||
this.section.box.connect('actor-added', Lang.bind(this, this._sync));
|
||||
this.section.box.connect('actor-removed', Lang.bind(this, this._sync));
|
||||
this.addMenuItem(this.section);
|
||||
|
||||
this._summaryItem = new PopupMenu.PopupSubMenuMenuItem('', true);
|
||||
this._summaryItem.icon.icon_name = this._getSummaryIcon();
|
||||
this.addMenuItem(this._summaryItem);
|
||||
|
||||
this._summaryItem.menu.addSettingsAction(_('Network Settings'),
|
||||
'gnome-network-panel.desktop');
|
||||
this._summaryItem.actor.hide();
|
||||
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let nDevices = this.section.box.get_children().reduce(
|
||||
function(prev, child) {
|
||||
return prev + (child.visible ? 1 : 0);
|
||||
}, 0);
|
||||
this._summaryItem.label.text = this._getSummaryLabel(nDevices);
|
||||
let shouldSummarize = nDevices > MAX_DEVICE_ITEMS;
|
||||
this._summaryItem.actor.visible = shouldSummarize;
|
||||
this.section.actor.visible = !shouldSummarize;
|
||||
},
|
||||
|
||||
_getSummaryIcon: function() {
|
||||
switch(this._category) {
|
||||
case NMConnectionCategory.WIRED:
|
||||
return 'network-wired-symbolic';
|
||||
case NMConnectionCategory.WIRELESS:
|
||||
case NMConnectionCategory.WWAN:
|
||||
return 'network-wireless-symbolic';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
_getSummaryLabel: function(nDevices) {
|
||||
switch(this._category) {
|
||||
case NMConnectionCategory.WIRED:
|
||||
return ngettext("%s Wired Connection",
|
||||
"%s Wired Connections",
|
||||
nDevices).format(nDevices);
|
||||
case NMConnectionCategory.WIRELESS:
|
||||
return ngettext("%s Wi-Fi Connection",
|
||||
"%s Wi-Fi Connections",
|
||||
nDevices).format(nDevices);
|
||||
case NMConnectionCategory.WWAN:
|
||||
return ngettext("%s Modem Connection",
|
||||
"%s Modem Connections",
|
||||
nDevices).format(nDevices);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
const NMApplet = new Lang.Class({
|
||||
Name: 'NMApplet',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
@ -1595,15 +1687,6 @@ const NMApplet = new Lang.Class({
|
||||
this._tryLateInit();
|
||||
},
|
||||
|
||||
_createDeviceCategory: function() {
|
||||
let category = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
};
|
||||
this.menu.addMenuItem(category.section);
|
||||
return category;
|
||||
},
|
||||
|
||||
_tryLateInit: function() {
|
||||
if (!this._client || !this._settings)
|
||||
return;
|
||||
@ -1620,9 +1703,13 @@ const NMApplet = new Lang.Class({
|
||||
this._nmDevices = [];
|
||||
this._devices = { };
|
||||
|
||||
this._devices.wired = this._createDeviceCategory();
|
||||
this._devices.wireless = this._createDeviceCategory();
|
||||
this._devices.wwan = this._createDeviceCategory();
|
||||
let categories = [NMConnectionCategory.WIRED,
|
||||
NMConnectionCategory.WIRELESS,
|
||||
NMConnectionCategory.WWAN];
|
||||
for (let category of categories) {
|
||||
this._devices[category] = new DeviceCategory(category);
|
||||
this.menu.addMenuItem(this._devices[category]);
|
||||
}
|
||||
|
||||
this._vpnSection = new NMVPNSection(this._client);
|
||||
this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
|
||||
@ -1632,6 +1719,8 @@ const NMApplet = new Lang.Class({
|
||||
this._readConnections();
|
||||
this._readDevices();
|
||||
this._syncNMState();
|
||||
this._syncMainConnection();
|
||||
this._syncVPNConnections();
|
||||
|
||||
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
|
||||
@ -1977,7 +2066,7 @@ const NMApplet = new Lang.Class({
|
||||
// (but in general we should only prompt a portal if we know there is a portal)
|
||||
if (GLib.getenv('GNOME_SHELL_CONNECTIVITY_TEST') != null)
|
||||
isPortal = isPortal || this._client.connectivity < NetworkManager.ConnectivityState.FULL;
|
||||
if (!isPortal)
|
||||
if (!isPortal || Main.sessionMode.isGreeter)
|
||||
return;
|
||||
|
||||
let path = this._mainConnection.get_path();
|
||||
@ -2008,13 +2097,24 @@ const NMApplet = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateIcon: function() {
|
||||
if (!this._client.networking_enabled || !this._mainConnection) {
|
||||
if (!this._client.networking_enabled) {
|
||||
this._primaryIndicator.visible = false;
|
||||
} else {
|
||||
let dev = this._mainConnection._primaryDevice;
|
||||
this._primaryIndicator.visible = (dev != null);
|
||||
if (dev)
|
||||
let dev = null;
|
||||
if (this._mainConnection)
|
||||
dev = this._mainConnection._primaryDevice;
|
||||
|
||||
let state = this._client.get_state();
|
||||
let connected = state == NetworkManager.State.CONNECTED_GLOBAL;
|
||||
this._primaryIndicator.visible = (dev != null) || connected;
|
||||
if (dev) {
|
||||
this._primaryIndicator.icon_name = dev.getIndicatorIcon();
|
||||
} else if (connected) {
|
||||
if (this._client.connectivity == NetworkManager.ConnectivityState.FULL)
|
||||
this._primaryIndicator.icon_name = 'network-wired-symbolic';
|
||||
else
|
||||
this._primaryIndicator.icon_name = 'network-wired-no-route-symbolic';
|
||||
}
|
||||
}
|
||||
|
||||
this._vpnIndicator.icon_name = this._vpnSection.getIndicatorIcon();
|
||||
|
74
js/ui/status/nightLight.js
Normal file
74
js/ui/status/nightLight.js
Normal file
@ -0,0 +1,74 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Color';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Color';
|
||||
|
||||
const ColorInterface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Color"> \
|
||||
<property name="DisabledUntilTomorrow" type="b" access="readwrite"/> \
|
||||
<property name="NightLightActive" type="b" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ColorProxy = Gio.DBusProxy.makeProxyWrapper(ColorInterface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'NightLightIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'night-light-symbolic';
|
||||
this._proxy = new ColorProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
(proxy, error) => {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
});
|
||||
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem("", true);
|
||||
this._item.icon.icon_name = 'night-light-symbolic';
|
||||
this._disableItem = this._item.menu.addAction('', () => {
|
||||
this._proxy.DisabledUntilTomorrow = !this._proxy.DisabledUntilTomorrow;
|
||||
});
|
||||
this._item.menu.addAction(_("Turn Off"), () => {
|
||||
let settings = new Gio.Settings({ schema_id: 'org.gnome.settings-daemon.plugins.color' });
|
||||
settings.set_boolean('night-light-enabled', false);
|
||||
});
|
||||
this._item.menu.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
this.menu.setSensitive(sensitive);
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let visible = this._proxy.NightLightActive;
|
||||
let disabled = this._proxy.DisabledUntilTomorrow;
|
||||
|
||||
this._item.label.text = disabled ? _("Night Light Disabled")
|
||||
: _("Night Light On");
|
||||
this._disableItem.label.text = disabled ? _("Resume")
|
||||
: _("Disable Until Tomorrow");
|
||||
this._item.actor.visible = this._indicator.visible = visible;
|
||||
}
|
||||
});
|
@ -48,8 +48,14 @@ const AltSwitcher = new Lang.Class({
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
|
||||
this._flipped = false;
|
||||
|
||||
this._clickAction = new Clutter.ClickAction();
|
||||
this._clickAction.connect('long-press', Lang.bind(this, this._onLongPress));
|
||||
|
||||
this.actor = new St.Bin();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('notify::mapped', () => { this._flipped = false; });
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
@ -58,14 +64,25 @@ const AltSwitcher = new Lang.Class({
|
||||
if (this._standard.visible && this._alternate.visible) {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
let altPressed = (mods & Clutter.ModifierType.MOD1_MASK) != 0;
|
||||
childToShow = altPressed ? this._alternate : this._standard;
|
||||
if (this._flipped)
|
||||
childToShow = altPressed ? this._standard : this._alternate;
|
||||
else
|
||||
childToShow = altPressed ? this._alternate : this._standard;
|
||||
} else if (this._standard.visible) {
|
||||
childToShow = this._standard;
|
||||
} else if (this._alternate.visible) {
|
||||
childToShow = this._alternate;
|
||||
}
|
||||
|
||||
if (this.actor.get_child() != childToShow) {
|
||||
let childShown = this.actor.get_child();
|
||||
if (childShown != childToShow) {
|
||||
if (childShown) {
|
||||
if (childShown.fake_release)
|
||||
childShown.fake_release();
|
||||
childShown.remove_action(this._clickAction);
|
||||
}
|
||||
childToShow.add_action(this._clickAction);
|
||||
|
||||
let hasFocus = this.actor.contains(global.stage.get_key_focus());
|
||||
this.actor.set_child(childToShow);
|
||||
if (hasFocus)
|
||||
@ -96,6 +113,16 @@ const AltSwitcher = new Lang.Class({
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onLongPress: function(action, actor, state) {
|
||||
if (state == Clutter.LongPressState.QUERY ||
|
||||
state == Clutter.LongPressState.CANCEL)
|
||||
return true;
|
||||
|
||||
this._flipped = !this._flipped;
|
||||
this._sync();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
|
@ -23,6 +23,7 @@ const EdgeDragAction = imports.ui.edgeDragAction;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
const PINCH_GESTURE_THRESHOLD = 0.7;
|
||||
|
||||
const ViewPage = {
|
||||
WINDOWS: 1,
|
||||
@ -51,9 +52,32 @@ function getTermsForSearchString(searchString) {
|
||||
return terms;
|
||||
}
|
||||
|
||||
const TouchpadShowOverviewAction = new Lang.Class({
|
||||
Name: 'TouchpadShowOverviewAction',
|
||||
|
||||
_init: function(actor) {
|
||||
actor.connect('captured-event', Lang.bind(this, this._handleEvent));
|
||||
},
|
||||
|
||||
_handleEvent: function(actor, event) {
|
||||
if (event.type() != Clutter.EventType.TOUCHPAD_PINCH)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_touchpad_gesture_finger_count() != 3)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END)
|
||||
this.emit('activated', event.get_gesture_pinch_scale ());
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(TouchpadShowOverviewAction.prototype);
|
||||
|
||||
const ShowOverviewAction = new Lang.Class({
|
||||
Name: 'ShowOverviewAction',
|
||||
Extends: Clutter.GestureAction,
|
||||
Signals: { 'activated': {} },
|
||||
|
||||
_init : function() {
|
||||
this.parent();
|
||||
@ -112,7 +136,6 @@ const ShowOverviewAction = new Lang.Class({
|
||||
this.emit('activated', areaDiff);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ShowOverviewAction.prototype);
|
||||
|
||||
const ViewSelector = new Lang.Class({
|
||||
Name: 'ViewSelector',
|
||||
@ -230,11 +253,16 @@ const ViewSelector = new Lang.Class({
|
||||
global.stage.add_action(gesture);
|
||||
|
||||
gesture = new ShowOverviewAction();
|
||||
gesture.connect('activated', Lang.bind(this, function(action, areaDiff) {
|
||||
if (areaDiff < 0.7)
|
||||
Main.overview.show();
|
||||
}));
|
||||
gesture.connect('activated', Lang.bind(this, this._pinchGestureActivated));
|
||||
global.stage.add_action(gesture);
|
||||
|
||||
gesture = new TouchpadShowOverviewAction(global.stage);
|
||||
gesture.connect('activated', Lang.bind(this, this._pinchGestureActivated));
|
||||
},
|
||||
|
||||
_pinchGestureActivated: function(action, scale) {
|
||||
if (scale < PINCH_GESTURE_THRESHOLD)
|
||||
Main.overview.show();
|
||||
},
|
||||
|
||||
_toggleAppsPage: function() {
|
||||
|
@ -17,6 +17,8 @@ const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const WindowMenu = imports.ui.windowMenu;
|
||||
const PadOsd = imports.ui.padOsd;
|
||||
const EdgeDragAction = imports.ui.edgeDragAction;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2;
|
||||
@ -32,6 +34,25 @@ const UNDIM_TIME = 0.250;
|
||||
const DISPLAY_REVERT_TIMEOUT = 20; // in seconds - keep in sync with mutter
|
||||
const ONE_SECOND = 1000; // in ms
|
||||
|
||||
const GSD_WACOM_BUS_NAME = 'org.gnome.SettingsDaemon.Wacom';
|
||||
const GSD_WACOM_OBJECT_PATH = '/org/gnome/SettingsDaemon/Wacom';
|
||||
|
||||
const GsdWacomIface = '<node name="/org/gnome/SettingsDaemon/Wacom"> \
|
||||
<interface name="org.gnome.SettingsDaemon.Wacom"> \
|
||||
<method name="SetGroupModeLED"> \
|
||||
<arg name="device_path" direction="in" type="s"/> \
|
||||
<arg name="group" direction="in" type="u"/> \
|
||||
<arg name="mode" direction="in" type="u"/> \
|
||||
</method> \
|
||||
<method name="SetOLEDLabels"> \
|
||||
<arg name="device_path" direction="in" type="s"/> \
|
||||
<arg name="labels" direction="in" type="as"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const GsdWacomProxy = Gio.DBusProxy.makeProxyWrapper(GsdWacomIface);
|
||||
|
||||
const DisplayChangeDialog = new Lang.Class({
|
||||
Name: 'DisplayChangeDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -510,7 +531,7 @@ const TouchpadWorkspaceSwitchAction = new Lang.Class({
|
||||
if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_gesture_swipe_finger_count() != 4)
|
||||
if (event.get_touchpad_gesture_finger_count() != 4)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) {
|
||||
@ -534,6 +555,7 @@ Signals.addSignalMethods(TouchpadWorkspaceSwitchAction.prototype);
|
||||
const WorkspaceSwitchAction = new Lang.Class({
|
||||
Name: 'WorkspaceSwitchAction',
|
||||
Extends: Clutter.SwipeAction,
|
||||
Signals: { 'activated': { param_types: [Meta.MotionDirection.$gtype] } },
|
||||
|
||||
_init : function() {
|
||||
const MOTION_THRESHOLD = 50;
|
||||
@ -571,11 +593,11 @@ const WorkspaceSwitchAction = new Lang.Class({
|
||||
this.emit('activated', dir);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(WorkspaceSwitchAction.prototype);
|
||||
|
||||
const AppSwitchAction = new Lang.Class({
|
||||
Name: 'AppSwitchAction',
|
||||
Extends: Clutter.GestureAction,
|
||||
Signals: { 'activated': {} },
|
||||
|
||||
_init : function() {
|
||||
this.parent();
|
||||
@ -637,7 +659,6 @@ const AppSwitchAction = new Lang.Class({
|
||||
return true;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AppSwitchAction.prototype);
|
||||
|
||||
const ResizePopup = new Lang.Class({
|
||||
Name: 'ResizePopup',
|
||||
@ -655,7 +676,7 @@ const ResizePopup = new Lang.Class({
|
||||
set: function(rect, displayW, displayH) {
|
||||
/* Translators: This represents the size of a window. The first number is
|
||||
* the width of the window and the second is the height. */
|
||||
let text = _("%d x %d").format(displayW, displayH);
|
||||
let text = _("%d × %d").format(displayW, displayH);
|
||||
this._label.set_text(text);
|
||||
|
||||
this._widget.set_position(rect.x, rect.y);
|
||||
@ -683,6 +704,8 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
this._dimmedWindows = [];
|
||||
|
||||
this._skippedActors = [];
|
||||
|
||||
this._allowedKeybindings = {};
|
||||
|
||||
this._isWorkspacePrepended = false;
|
||||
@ -703,6 +726,7 @@ const WindowManager = new Lang.Class({
|
||||
this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
|
||||
this._shellwm.connect('unminimize', Lang.bind(this, this._unminimizeWindow));
|
||||
this._shellwm.connect('size-change', Lang.bind(this, this._sizeChangeWindow));
|
||||
this._shellwm.connect('size-changed', Lang.bind(this, this._sizeChangedWindow));
|
||||
this._shellwm.connect('map', Lang.bind(this, this._mapWindow));
|
||||
this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow));
|
||||
this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding));
|
||||
@ -915,6 +939,35 @@ const WindowManager = new Lang.Class({
|
||||
Lang.bind(this, this._toggleCalendar));
|
||||
|
||||
global.display.connect('show-resize-popup', Lang.bind(this, this._showResizePopup));
|
||||
global.display.connect('show-pad-osd', Lang.bind(this, this._showPadOsd));
|
||||
global.display.connect('show-osd', Lang.bind(this, function (display, monitorIndex, iconName, label) {
|
||||
let icon = Gio.Icon.new_for_string(iconName);
|
||||
Main.osdWindowManager.show(monitorIndex, icon, label, null);
|
||||
}));
|
||||
|
||||
this._gsdWacomProxy = new GsdWacomProxy(Gio.DBus.session, GSD_WACOM_BUS_NAME,
|
||||
GSD_WACOM_OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
}));
|
||||
|
||||
global.display.connect('pad-mode-switch', Lang.bind(this, function (display, pad, group, mode) {
|
||||
let labels = [];
|
||||
|
||||
//FIXME: Fix num buttons
|
||||
for (let i = 0; i < 50; i++) {
|
||||
let str = display.get_pad_action_label(pad, Meta.PadActionType.BUTTON, i);
|
||||
labels.push(str ? str: '');
|
||||
}
|
||||
|
||||
if (this._gsdWacomProxy) {
|
||||
this._gsdWacomProxy.SetOLEDLabelsRemote(pad.get_device_node(), labels);
|
||||
this._gsdWacomProxy.SetGroupModeLEDRemote(pad.get_device_node(), group, mode);
|
||||
}
|
||||
}));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
@ -945,6 +998,18 @@ const WindowManager = new Lang.Class({
|
||||
gesture.connect('activated', Lang.bind(this, this._switchApp));
|
||||
global.stage.add_action(gesture);
|
||||
|
||||
gesture = new EdgeDragAction.EdgeDragAction(St.Side.BOTTOM, Shell.ActionMode.ALL);
|
||||
gesture.connect('activated', Lang.bind(this, function() {
|
||||
Main.keyboard.show(Main.layoutManager.bottomIndex);
|
||||
}));
|
||||
global.stage.add_action(gesture);
|
||||
},
|
||||
|
||||
_showPadOsd: function (display, device, settings, imagePath, editionMode, monitorIndex) {
|
||||
this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex);
|
||||
this._currentPadOsd.connect('closed', Lang.bind(this, function() { this._currentPadOsd = null }));
|
||||
|
||||
return this._currentPadOsd.actor;
|
||||
},
|
||||
|
||||
_actionSwitchWorkspace: function(action, direction) {
|
||||
@ -1035,6 +1100,10 @@ const WindowManager = new Lang.Class({
|
||||
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
|
||||
},
|
||||
|
||||
skipNextEffect: function(actor) {
|
||||
this._skippedActors.push(actor);
|
||||
},
|
||||
|
||||
setCustomKeybindingHandler: function(name, modes, handler) {
|
||||
if (Meta.keybindings_set_custom_handler(name, handler))
|
||||
this.allowKeybinding(name, modes);
|
||||
@ -1061,9 +1130,15 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_shouldAnimateActor: function(actor, types) {
|
||||
if (this._removeEffect(this._skippedActors, actor))
|
||||
return false;
|
||||
|
||||
if (!this._shouldAnimate())
|
||||
return false;
|
||||
|
||||
if (!actor.get_texture())
|
||||
return false;
|
||||
|
||||
let type = actor.meta_window.get_window_type();
|
||||
return types.indexOf(type) >= 0;
|
||||
},
|
||||
@ -1238,37 +1313,17 @@ const WindowManager = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
if (whichChange == Meta.SizeChange.FULLSCREEN)
|
||||
this._fullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect);
|
||||
else if (whichChange == Meta.SizeChange.UNFULLSCREEN)
|
||||
this._unfullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect);
|
||||
if ((whichChange == Meta.SizeChange.FULLSCREEN ||
|
||||
whichChange == Meta.SizeChange.UNFULLSCREEN) &&
|
||||
oldFrameRect.width > 0 && oldFrameRect.height > 0)
|
||||
this._fullscreenAnimation(shellwm, actor, oldFrameRect, whichChange);
|
||||
else
|
||||
shellwm.completed_size_change(actor);
|
||||
},
|
||||
|
||||
_fullscreenWindow: function(shellwm, actor, oldFrameRect, oldBufferRect) {
|
||||
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
||||
actor.translation_x = oldFrameRect.x - monitor.x;
|
||||
actor.translation_y = oldFrameRect.y - monitor.y;
|
||||
this._fullscreenAnimation(shellwm, actor, oldFrameRect);
|
||||
},
|
||||
|
||||
_unfullscreenWindow: function(shellwm, actor, oldFrameRect, oldBufferRect) {
|
||||
let targetRect = actor.meta_window.get_frame_rect();
|
||||
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
||||
actor.translation_x = -(targetRect.x - monitor.x);
|
||||
actor.translation_y = -(targetRect.y - monitor.y);
|
||||
this._fullscreenAnimation(shellwm, actor, oldFrameRect);
|
||||
},
|
||||
|
||||
_fullscreenAnimation: function(shellwm, actor, oldFrameRect) {
|
||||
this._resizing.push(actor);
|
||||
|
||||
_fullscreenAnimation: function(shellwm, actor, oldFrameRect, change) {
|
||||
// Position a clone of the window on top of the old position,
|
||||
// while actor updates are frozen.
|
||||
// Note that the MetaWindow has up to date sizing information for
|
||||
// the new geometry already.
|
||||
let targetRect = actor.meta_window.get_frame_rect();
|
||||
let actorContent = Shell.util_get_content_for_window_actor(actor, oldFrameRect);
|
||||
let actorClone = new St.Widget({ content: actorContent });
|
||||
actorClone.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
@ -1276,10 +1331,28 @@ const WindowManager = new Lang.Class({
|
||||
actorClone.set_size(oldFrameRect.width, oldFrameRect.height);
|
||||
Main.uiGroup.add_actor(actorClone);
|
||||
|
||||
actor.__fullscreenClone = actorClone;
|
||||
let rect = change == Meta.SizeChange.FULLSCREEN ? oldFrameRect : null;
|
||||
|
||||
let scaleX = targetRect.width / oldFrameRect.width;
|
||||
let scaleY = targetRect.height / oldFrameRect.height;
|
||||
if (this._clearFullscreenInfo(actor))
|
||||
this._shellwm.completed_size_change(actor);
|
||||
|
||||
actor.__fullscreenInfo = { clone: actorClone,
|
||||
oldRect: rect };
|
||||
},
|
||||
|
||||
_sizeChangedWindow: function(shellwm, actor) {
|
||||
if (!actor.__fullscreenInfo)
|
||||
return;
|
||||
if (this._resizing.indexOf(actor) != -1)
|
||||
return;
|
||||
|
||||
let actorClone = actor.__fullscreenInfo.clone;
|
||||
let targetRect = actor.meta_window.get_frame_rect();
|
||||
|
||||
let scaleX = targetRect.width / actorClone.width;
|
||||
let scaleY = targetRect.height / actorClone.height;
|
||||
|
||||
this._resizing.push(actor);
|
||||
|
||||
// Now scale and fade out the clone
|
||||
Tweener.addTween(actorClone,
|
||||
@ -1292,9 +1365,17 @@ const WindowManager = new Lang.Class({
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
||||
let oldRect = actor.__fullscreenInfo.oldRect;
|
||||
if (oldRect) {
|
||||
actor.translation_x = oldRect.x - monitor.x;
|
||||
actor.translation_y = oldRect.y - monitor.y;
|
||||
} else {
|
||||
actor.translation_x = -(targetRect.x - monitor.x);
|
||||
actor.translation_y = -(targetRect.y - monitor.y);
|
||||
}
|
||||
|
||||
// Now set scale the actor to size it as the clone.
|
||||
// Note that the caller of this function already set a translation
|
||||
// on the actor.
|
||||
actor.scale_x = 1 / scaleX;
|
||||
actor.scale_y = 1 / scaleY;
|
||||
|
||||
@ -1320,6 +1401,15 @@ const WindowManager = new Lang.Class({
|
||||
shellwm.completed_size_change(actor);
|
||||
},
|
||||
|
||||
_clearFullscreenInfo: function(actor) {
|
||||
if (actor.__fullscreenInfo) {
|
||||
actor.__fullscreenInfo.clone.destroy();
|
||||
delete actor.__fullscreenInfo;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_sizeChangeWindowDone: function(shellwm, actor) {
|
||||
if (this._removeEffect(this._resizing, actor)) {
|
||||
Tweener.removeTweens(actor);
|
||||
@ -1327,31 +1417,25 @@ const WindowManager = new Lang.Class({
|
||||
actor.scale_y = 1.0;
|
||||
actor.translation_x = 0;
|
||||
actor.translation_y = 0;
|
||||
|
||||
let actorClone = actor.__fullscreenClone;
|
||||
if (actorClone) {
|
||||
actorClone.destroy();
|
||||
delete actor.__fullscreenClone;
|
||||
}
|
||||
this._clearFullscreenInfo(actor);
|
||||
}
|
||||
},
|
||||
|
||||
_sizeChangeWindowOverwritten: function(shellwm, actor) {
|
||||
if (this._removeEffect(this._resizing, actor)) {
|
||||
let actorClone = actor.__fullscreenClone;
|
||||
if (actorClone) {
|
||||
actorClone.destroy();
|
||||
delete actor.__fullscreenClone;
|
||||
}
|
||||
}
|
||||
if (this._removeEffect(this._resizing, actor))
|
||||
this._clearFullscreenInfo(actor);
|
||||
},
|
||||
|
||||
_hasAttachedDialogs: function(window, ignoreWindow) {
|
||||
var count = 0;
|
||||
window.foreach_transient(function(win) {
|
||||
if (win != ignoreWindow && win.is_attached_dialog())
|
||||
if (win != ignoreWindow &&
|
||||
win.is_attached_dialog() &&
|
||||
win.get_transient_for() == window) {
|
||||
count++;
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return count != 0;
|
||||
},
|
||||
@ -1420,6 +1504,11 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
actor._windowType = type;
|
||||
}));
|
||||
actor.meta_window.connect('unmanaged', Lang.bind(this, function(window) {
|
||||
let parent = window.get_transient_for();
|
||||
if (parent)
|
||||
this._checkDimming(parent);
|
||||
}));
|
||||
|
||||
if (actor.meta_window.is_attached_dialog())
|
||||
this._checkDimming(actor.get_meta_window().get_transient_for());
|
||||
|
@ -97,8 +97,6 @@ const WindowMenu = new Lang.Class({
|
||||
if (window.is_always_on_all_workspaces())
|
||||
item.setSensitive(false);
|
||||
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
|
||||
if (!isSticky) {
|
||||
let workspace = window.get_workspace();
|
||||
if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) {
|
||||
|
@ -111,7 +111,7 @@ const WindowClone = new Lang.Class({
|
||||
this.metaWindow._delegate = this;
|
||||
this._workspace = workspace;
|
||||
|
||||
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture() });
|
||||
this._windowClone = new Clutter.Clone({ source: realWindow });
|
||||
// We expect this.actor to be used for all interaction rather than
|
||||
// this._windowClone; as the former is reactive and the latter
|
||||
// is not, this just works for most cases. However, for DND all
|
||||
|
@ -19,8 +19,7 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
Name: 'WorkspaceSwitcherPopup',
|
||||
|
||||
_init : function() {
|
||||
this.actor = new St.Widget({ reactive: true,
|
||||
x: 0,
|
||||
this.actor = new St.Widget({ x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height,
|
||||
|
@ -24,9 +24,10 @@ const XdndHandler = new Lang.Class({
|
||||
if (!Meta.is_wayland_compositor())
|
||||
global.init_xdnd();
|
||||
|
||||
global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
|
||||
global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
|
||||
global.connect('xdnd-leave', Lang.bind(this, this._onLeave));
|
||||
var dnd = Meta.get_backend().get_dnd();
|
||||
dnd.connect('dnd-enter', Lang.bind(this, this._onEnter));
|
||||
dnd.connect('dnd-position-change', Lang.bind(this, this._onPositionChanged));
|
||||
dnd.connect('dnd-leave', Lang.bind(this, this._onLeave));
|
||||
|
||||
this._windowGroupVisibilityHandlerId = 0;
|
||||
},
|
||||
|
@ -29,6 +29,7 @@ gl
|
||||
gu
|
||||
he
|
||||
hi
|
||||
hr
|
||||
hu
|
||||
ia
|
||||
id
|
||||
|
68
po/Makevars
Normal file
68
po/Makevars
Normal file
@ -0,0 +1,68 @@
|
||||
# Makefile variables for PO directory in any package using GNU gettext.
|
||||
|
||||
# Usually the message domain is the same as the package name.
|
||||
DOMAIN = $(PACKAGE)
|
||||
|
||||
# These two variables depend on the location of this directory.
|
||||
subdir = po
|
||||
top_builddir = ..
|
||||
|
||||
# These options get passed to xgettext.
|
||||
XGETTEXT_OPTIONS = --from-code=UTF-8 --keyword=_ --keyword=N_ \
|
||||
--keyword=C_:1c,2 --keyword=NC_:1c,2 \
|
||||
--keyword=g_dngettext:2,3 --add-comments \
|
||||
--flag=g_dngettext:2:pass-c-format \
|
||||
--flag=g_strdup_printf:1:c-format \
|
||||
--flag=g_string_printf:2:c-format \
|
||||
--flag=g_string_append_printf:2:c-format \
|
||||
--flag=g_error_new:3:c-format \
|
||||
--flag=g_set_error:4:c-format \
|
||||
--flag=g_markup_printf_escaped:1:c-format \
|
||||
--flag=g_log:3:c-format \
|
||||
--flag=g_print:1:c-format \
|
||||
--flag=g_printerr:1:c-format \
|
||||
--flag=g_printf:1:c-format \
|
||||
--flag=g_fprintf:2:c-format \
|
||||
--flag=g_sprintf:2:c-format \
|
||||
--flag=g_snprintf:3:c-format
|
||||
|
||||
|
||||
# This is the copyright holder that gets inserted into the header of the
|
||||
# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
|
||||
# package. (Note that the msgstr strings, extracted from the package's
|
||||
# sources, belong to the copyright holder of the package.) Translators are
|
||||
# expected to transfer the copyright for their translations to this person
|
||||
# or entity, or to disclaim their copyright. The empty string stands for
|
||||
# the public domain; in this case the translators are expected to disclaim
|
||||
# their copyright.
|
||||
COPYRIGHT_HOLDER = Translation copyright holder
|
||||
# This is the email address or URL to which the translators shall report
|
||||
# bugs in the untranslated strings:
|
||||
# - Strings which are not entire sentences, see the maintainer guidelines
|
||||
# in the GNU gettext documentation, section 'Preparing Strings'.
|
||||
# - Strings which use unclear terms or require additional context to be
|
||||
# understood.
|
||||
# - Strings which make invalid assumptions about notation of date, time or
|
||||
# money.
|
||||
# - Pluralisation problems.
|
||||
# - Incorrect English spelling.
|
||||
# - Incorrect formatting.
|
||||
# It can be your email address, or a mailing list address where translators
|
||||
# can write to without being subscribed, or the URL of a web page through
|
||||
# which the translators can contact you.
|
||||
MSGID_BUGS_ADDRESS = http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=general
|
||||
|
||||
# This is the list of locale categories, beyond LC_MESSAGES, for which the
|
||||
# message catalogs shall be used. It is usually empty.
|
||||
EXTRA_LOCALE_CATEGORIES =
|
||||
|
||||
# Ignore the timestamp of the .pot file, as git clones do not have
|
||||
# deterministic timestamps, and .po files are updated by translators
|
||||
# (only) in GNOME projects.
|
||||
PO_DEPENDS_ON_POT = no
|
||||
|
||||
# This tells whether or not to forcibly update $(DOMAIN).pot and
|
||||
# regenerate PO files on "make dist". Possible values are "yes" and
|
||||
# "no". Set this to no if the POT file and PO files are maintained
|
||||
# externally.
|
||||
DIST_DEPENDS_ON_UPDATE_PO = no
|
@ -1,17 +1,17 @@
|
||||
# List of source files containing translatable strings.
|
||||
# Please keep this file sorted alphabetically.
|
||||
[encoding: UTF-8]
|
||||
data/50-gnome-shell-system.xml.in
|
||||
data/50-gnome-shell-system.xml
|
||||
data/gnome-shell-extension-prefs.desktop.in.in
|
||||
data/org.gnome.Shell.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in.in
|
||||
data/org.gnome.Shell.PortalHelper.desktop.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
data/org.gnome.Shell.PortalHelper.desktop.in.in
|
||||
js/extensionPrefs/main.js
|
||||
js/gdm/authPrompt.js
|
||||
js/gdm/loginDialog.js
|
||||
js/gdm/util.js
|
||||
js/misc/util.js
|
||||
js/portalHelper/main.js
|
||||
js/ui/accessDialog.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/audioDeviceSelection.js
|
||||
@ -39,6 +39,7 @@ js/ui/mpris.js
|
||||
js/ui/notificationDaemon.js
|
||||
js/ui/overviewControls.js
|
||||
js/ui/overview.js
|
||||
js/ui/padOsd.js
|
||||
js/ui/panel.js
|
||||
js/ui/popupMenu.js
|
||||
js/ui/runDialog.js
|
||||
@ -52,6 +53,7 @@ js/ui/status/brightness.js
|
||||
js/ui/status/keyboard.js
|
||||
js/ui/status/location.js
|
||||
js/ui/status/network.js
|
||||
js/ui/status/nightLight.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/rfkill.js
|
||||
js/ui/status/system.js
|
||||
@ -61,7 +63,7 @@ js/ui/viewSelector.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
js/ui/windowManager.js
|
||||
js/ui/windowMenu.js
|
||||
src/calendar-server/evolution-calendar.desktop.in.in
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
# Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get it.
|
||||
src/gvc/gvc-mixer-control.c
|
||||
src/main.c
|
||||
|
@ -1,4 +0,0 @@
|
||||
data/org.gnome.shell.evolution.calendar.gschema.xml.in
|
||||
src/calendar-server/evolution-calendar.desktop.in
|
||||
# Meh, autofools :-(
|
||||
sub/src/calendar-server/evolution-calendar.desktop.in
|
2
po/as.po
2
po/as.po
@ -13,7 +13,7 @@ msgstr ""
|
||||
"PO-Revision-Date: 2014-09-15 14:59+0530\n"
|
||||
"Last-Translator: Nilamdyuti Goswami <ngoswami@redhat.com>\n"
|
||||
"Language-Team: Assamese <kde-i18n-doc@kde.org>\n"
|
||||
"Language: as_IN\n"
|
||||
"Language: as\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
1
po/bn.po
1
po/bn.po
@ -7,6 +7,7 @@ msgstr ""
|
||||
"PO-Revision-Date: 2011-04-04 11:04+0600\n"
|
||||
"Last-Translator: Israt Jahan <israt@ankur.org.bd>\n"
|
||||
"Language-Team: Bengali <ankur-bd-l10n@googlegroups.com>\n"
|
||||
"Language: bn\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
@ -11,7 +11,7 @@ msgstr ""
|
||||
"PO-Revision-Date: 2014-12-30 16:45+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: American English <kde-i18n-doc@kde.org>\n"
|
||||
"Language: en_US\n"
|
||||
"Language: bn_IN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"PO-Revision-Date: 2014-09-14 23:32+0200\n"
|
||||
"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
|
||||
"Language-Team: Catalan <tradgnome@softcatala.org>\n"
|
||||
"Language: ca-XV\n"
|
||||
"Language: ca@valencia\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bits\n"
|
||||
|
847
po/en_GB.po
847
po/en_GB.po
File diff suppressed because it is too large
Load Diff
2
po/gu.po
2
po/gu.po
@ -13,7 +13,7 @@ msgstr ""
|
||||
"PO-Revision-Date: 2014-10-01 15:51+0530\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: American English <kde-i18n-doc@kde.org>\n"
|
||||
"Language: \n"
|
||||
"Language: gu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user