Compare commits
348 Commits
wip/rtcm/m
...
3.23.90
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
4fc0c513af | |||
f5c058a036 | |||
0dac0ad516 | |||
5d4d1e26fd | |||
2f0039e7dd | |||
9202309bed | |||
7e0e224e0e | |||
35825cf0c7 | |||
b2d79b6362 | |||
f5e1dc86cd | |||
8c51f0076f | |||
8347152730 | |||
3d77dd8f51 | |||
82e6935281 | |||
61070e6ec0 | |||
128697d6a7 | |||
424fa01eca | |||
775187b2e4 | |||
99b5e10acf | |||
208ed6e90d | |||
de8d4c4cd8 | |||
3ae591434d | |||
61fb62f969 | |||
c4584c20dc | |||
c6eb95ff3a | |||
ab68360d53 | |||
58a733dc93 | |||
db8f6b4848 | |||
e72d388a67 | |||
b5dd4d1456 | |||
a7562b4148 | |||
358f64d66b | |||
2a7f9f70b8 | |||
c91085caf7 | |||
21ddbf0b8f | |||
2ad2853278 | |||
e16f63a8e4 | |||
c39ffa111f | |||
5182129196 | |||
02bad8e92b | |||
500ea13155 | |||
ce8e7481e7 | |||
528f2e3cdc | |||
408211ba73 | |||
e02467eada | |||
3e93a1bdd6 | |||
a69a9ba51a | |||
1c0e0191e0 | |||
f233122d4d | |||
72bfa91259 | |||
ef195f0185 | |||
47da6b139e | |||
2705434955 | |||
a7e030d0f9 | |||
8a44170f83 | |||
2ea6ae05e5 | |||
5c0eba7d3b | |||
f81887772a | |||
9aa3d864dc | |||
2425b11df6 | |||
1cb644529f | |||
6cff0fd994 | |||
437256e0f1 | |||
841e0132e9 | |||
1545596c7e | |||
38406e070c | |||
9b07ce1d0d | |||
bf8d30603e | |||
bd6e7f14d1 | |||
5226d8b086 | |||
bd92331780 | |||
387f5143f0 | |||
b7867fe44c | |||
ddea54a539 | |||
84da49c715 | |||
513dff45c1 | |||
3d697baa26 | |||
9eda10d091 | |||
0f82da8735 | |||
296b61b61c | |||
974a896dbc | |||
093fd54e2b | |||
5ae3e5aeb7 | |||
af28a219be | |||
1883df2927 | |||
3274f270e3 | |||
c97626e516 | |||
9c483dd9a6 | |||
8fd6e93fbe | |||
8416ba25de | |||
63f6ff9151 | |||
59c2ace98c | |||
cf4465027a | |||
a23b293fe2 | |||
c039a3ddda | |||
ebe071bd50 |
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
|
||||
|
246
NEWS
246
NEWS
@ -1,3 +1,249 @@
|
||||
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]
|
||||
* Remember input sources MRU list [Cosimo; #766826]
|
||||
* networkAgent: Handle VPN service aliases [David; #658484]
|
||||
* Plug a memory leak [Hans; #710230]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Florian Müllner, Hans Petter Jansson, David Woodhouse
|
||||
|
||||
Translations:
|
||||
Tiago Santos [pt], Cédric Valmary [oc], Muhammet Kara [tr],
|
||||
Daniel Mustieles [es], Rafael Fontenelle [pt_BR]
|
||||
|
||||
3.21.2
|
||||
======
|
||||
* Fix sorting of hidden apps in app switcher [Florian; #766238]
|
||||
* Set logind's LockedHint property when locked [Victor; #764773]
|
||||
* Allocate framebuffers early to fix a crash on NVIDIA [Martin; #764898]
|
||||
* Fix cycle-windows/cycle-group keybindings [Florian; #730739]
|
||||
* Switch to shared desktop schema for calendar settings [Iain; #766318]
|
||||
* Misc. bug fixes [Florian, Cosimo, Michele; #766325, #758471, #757556,
|
||||
#757019, #766598]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Michele Gaio, Iain Lane, Florian Müllner, Martin Szulecki,
|
||||
Victor Toso
|
||||
|
||||
Translations:
|
||||
Tiago Santos [pt], Kjartan Maraas [nb], Jiro Matsuzawa [ja],
|
||||
Cédric Valmary [oc], Sveinn í Felli [is]
|
||||
|
||||
3.21.1
|
||||
======
|
||||
* Save screencasts in HOME if XDG_VIDEO_DIR doesn't exist [Florian; #765015]
|
||||
* Don't show orientation lock when g-s-d won't rotate [Florian; #765267]
|
||||
* Misc. bug fixes [Heiher, Florian, Marek, Rui; #722752, #765061, #763068,
|
||||
#765607, #757676, #760439]
|
||||
|
||||
Contributors:
|
||||
Heiher, Marek Chalupa, Rui Matos, Florian Müllner
|
||||
|
||||
Translations:
|
||||
Arash Mousavi [fa], Kristjan SCHMIDT [eo], GNOME Translation Robot [gd]
|
||||
|
||||
3.20.1
|
||||
======
|
||||
* Plug a memory leak [Aaron; #735705]
|
||||
|
||||
Contributors:
|
||||
Aaron Plattner
|
||||
|
||||
Translations:
|
||||
Daniel Korostil [uk], Matej Urbančič [sl], Inaki Larranaga Murgoitio [eu],
|
||||
Cheng-Chia Tseng [zh_TW], Fabio Tomat [fur], Trần Ngọc Quân [vi],
|
||||
YunQiang Su [zh_CN], Marek Černocký [cs], Arash Mousavi [fa],
|
||||
Alexander Shopov [bg], Khaled Hosny [ar]
|
||||
|
||||
3.20.0
|
||||
======
|
||||
|
||||
|
@ -11,6 +11,8 @@ test -z "$srcdir" && srcdir=.
|
||||
exit 1
|
||||
}
|
||||
|
||||
pushd $srcdir
|
||||
|
||||
# Fetch submodules if needed
|
||||
if test ! -f src/gvc/Makefile.am || test ! -f data/theme/gnome-shell-sass/COPYING;
|
||||
then
|
||||
@ -19,6 +21,8 @@ then
|
||||
fi
|
||||
git submodule update
|
||||
|
||||
popd
|
||||
|
||||
which gnome-autogen.sh || {
|
||||
echo "You need to install gnome-common from GNOME Git (or from"
|
||||
echo "your OS vendor's package manager)."
|
||||
|
@ -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.20.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.23.90],[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 clutter-1.0)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules $LIBMUTTER_CLUTTER)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
@ -74,10 +83,9 @@ 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.20.0
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=1.49.1
|
||||
GJS_MIN_VERSION=1.47.0
|
||||
MUTTER_MIN_VERSION=3.23.90
|
||||
GTK_MIN_VERSION=3.15.0
|
||||
GIO_MIN_VERSION=2.45.3
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
@ -95,11 +103,11 @@ 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
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
$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
|
||||
@ -111,15 +119,17 @@ 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, 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, 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.19.2)
|
||||
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],
|
||||
@ -145,10 +155,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 +264,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>
|
||||
|
@ -7,12 +7,16 @@ desktop_DATA = org.gnome.Shell.desktop gnome-shell-extension-prefs.desktop
|
||||
if HAVE_NETWORKMANAGER
|
||||
desktop_DATA += org.gnome.Shell.PortalHelper.desktop
|
||||
|
||||
portaldir = $(datadir)/xdg-desktop-portal/portals
|
||||
portal_DATA = gnome-shell.portal
|
||||
|
||||
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
|
||||
@ -28,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 \
|
||||
@ -57,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)
|
||||
|
||||
@ -80,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 $@
|
||||
|
||||
@ -106,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)
|
||||
@ -123,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>
|
||||
|
4
data/gnome-shell.portal
Normal file
4
data/gnome-shell.portal
Normal file
@ -0,0 +1,4 @@
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.desktop.gnome
|
||||
Interfaces=org.freedesktop.impl.portal.Access
|
||||
UseIn=gnome
|
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,143 +3,139 @@
|
||||
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://live.gnome.org/GnomeShell/LookingGlass -->
|
||||
<_summary>History for the looking glass dialog</_summary>
|
||||
<!-- 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>
|
||||
</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>
|
||||
<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.
|
||||
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="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-weekdate" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show the week date in the calendar</_summary>
|
||||
<_description>
|
||||
If true, display the ISO week date in the calendar.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="open-application-menu" type="as">
|
||||
<default>["<Super>F10"]</default>
|
||||
<_summary>Keybinding to open the application menu</_summary>
|
||||
<_description>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@ -147,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>
|
||||
|
||||
@ -159,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>
|
||||
|
||||
@ -177,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>
|
||||
</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>
|
||||
|
||||
@ -198,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>
|
@ -427,6 +427,29 @@ StScrollBar {
|
||||
.audio-selection-device-icon {
|
||||
icon-size: 64px; }
|
||||
|
||||
/* Access Dialog */
|
||||
.access-dialog {
|
||||
spacing: 30px; }
|
||||
|
||||
.access-dialog-main-layout {
|
||||
padding: 12px 20px 0;
|
||||
spacing: 12px; }
|
||||
|
||||
.access-dialog-content {
|
||||
max-width: 28em;
|
||||
spacing: 20px; }
|
||||
|
||||
.access-dialog-icon {
|
||||
min-width: 48px;
|
||||
icon-size: 48px; }
|
||||
|
||||
.access-dialog-title {
|
||||
font-weight: bold; }
|
||||
|
||||
.access-dialog-subtitle {
|
||||
color: #999999;
|
||||
font-weight: bold; }
|
||||
|
||||
/* Geolocation Dialog */
|
||||
.geolocation-dialog {
|
||||
spacing: 30px; }
|
||||
@ -535,6 +558,17 @@ StScrollBar {
|
||||
border-radius: 0.3em;
|
||||
background-color: rgba(11, 12, 13, 0.5);
|
||||
color: #eeeeec; }
|
||||
.osd-window .level-bar {
|
||||
background-color: #eeeeec;
|
||||
border-radius: 0.3em; }
|
||||
|
||||
/* Pad OSD */
|
||||
.pad-osd-window {
|
||||
padding: 32px;
|
||||
background-color: rgba(0, 0, 0, 0.8); }
|
||||
|
||||
.combo-box-label {
|
||||
width: 15em; }
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-popup {
|
||||
@ -578,6 +612,10 @@ StScrollBar {
|
||||
width: 96px;
|
||||
height: 96px; }
|
||||
|
||||
/* Window Cycler */
|
||||
.cycler-highlight {
|
||||
border: 5px solid #215d9c; }
|
||||
|
||||
/* Workspace Switcher */
|
||||
.workspace-switcher-group {
|
||||
padding: 12px; }
|
||||
@ -766,7 +804,7 @@ StScrollBar {
|
||||
border-radius: 1.4em; }
|
||||
.calendar-day-base:hover, .calendar-day-base:focus {
|
||||
background-color: #0d0d0d; }
|
||||
.calendar-day-base:active {
|
||||
.calendar-day-base:active, .calendar-day-base:selected {
|
||||
color: white;
|
||||
background-color: #215d9c;
|
||||
border-color: transparent; }
|
||||
|
Submodule data/theme/gnome-shell-sass updated: 6ccc180811...50bbd0b50f
@ -427,6 +427,29 @@ StScrollBar {
|
||||
.audio-selection-device-icon {
|
||||
icon-size: 64px; }
|
||||
|
||||
/* Access Dialog */
|
||||
.access-dialog {
|
||||
spacing: 30px; }
|
||||
|
||||
.access-dialog-main-layout {
|
||||
padding: 12px 20px 0;
|
||||
spacing: 12px; }
|
||||
|
||||
.access-dialog-content {
|
||||
max-width: 28em;
|
||||
spacing: 20px; }
|
||||
|
||||
.access-dialog-icon {
|
||||
min-width: 48px;
|
||||
icon-size: 48px; }
|
||||
|
||||
.access-dialog-title {
|
||||
font-weight: bold; }
|
||||
|
||||
.access-dialog-subtitle {
|
||||
color: #8e8e80;
|
||||
font-weight: bold; }
|
||||
|
||||
/* Geolocation Dialog */
|
||||
.geolocation-dialog {
|
||||
spacing: 30px; }
|
||||
@ -535,6 +558,17 @@ StScrollBar {
|
||||
border-radius: 0.3em;
|
||||
background-color: rgba(11, 12, 13, 0.5);
|
||||
color: #eeeeec; }
|
||||
.osd-window .level-bar {
|
||||
background-color: #eeeeec;
|
||||
border-radius: 0.3em; }
|
||||
|
||||
/* Pad OSD */
|
||||
.pad-osd-window {
|
||||
padding: 32px;
|
||||
background-color: rgba(0, 0, 0, 0.8); }
|
||||
|
||||
.combo-box-label {
|
||||
width: 15em; }
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-popup {
|
||||
@ -578,6 +612,10 @@ StScrollBar {
|
||||
width: 96px;
|
||||
height: 96px; }
|
||||
|
||||
/* Window Cycler */
|
||||
.cycler-highlight {
|
||||
border: 5px solid #215d9c; }
|
||||
|
||||
/* Workspace Switcher */
|
||||
.workspace-switcher-group {
|
||||
padding: 12px; }
|
||||
@ -766,7 +804,7 @@ StScrollBar {
|
||||
border-radius: 1.4em; }
|
||||
.calendar-day-base:hover, .calendar-day-base:focus {
|
||||
background-color: #454c4c; }
|
||||
.calendar-day-base:active {
|
||||
.calendar-day-base:active, .calendar-day-base:selected {
|
||||
color: white;
|
||||
background-color: #215d9c;
|
||||
border-color: transparent; }
|
||||
|
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;
|
||||
}
|
@ -113,7 +113,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la -rpath $(MUTTER_TYPELIB_DIR)
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -78,7 +78,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=
|
||||
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la
|
||||
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la -rpath $(MUTTER_TYPELIB_DIR)
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -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) {
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
<file>portalHelper/main.js</file>
|
||||
|
||||
<file>ui/accessDialog.js</file>
|
||||
<file>ui/altTab.js</file>
|
||||
<file>ui/animation.js</file>
|
||||
<file>ui/appDisplay.js</file>
|
||||
@ -71,6 +72,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>
|
||||
@ -115,6 +117,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
|
||||
|
@ -40,6 +40,9 @@ const SystemdLoginSessionIface = '<node> \
|
||||
<signal name="Lock" /> \
|
||||
<signal name="Unlock" /> \
|
||||
<property name="Active" type="b" access="read" /> \
|
||||
<method name="SetLockedHint"> \
|
||||
<arg type="b" direction="in"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
@ -131,10 +134,13 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
this._proxy.CanSuspendRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no' && result[0] != 'na');
|
||||
if (error) {
|
||||
asyncCallback(false, false);
|
||||
} else {
|
||||
let needsAuth = result[0] == 'challenge';
|
||||
let canSuspend = needsAuth || result[0] == 'yes';
|
||||
asyncCallback(canSuspend, needsAuth);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -187,7 +193,7 @@ const LoginManagerDummy = new Lang.Class({
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
asyncCallback(false);
|
||||
asyncCallback(false, false);
|
||||
},
|
||||
|
||||
listSessions: function(asyncCallback) {
|
||||
|
@ -94,7 +94,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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
202
js/ui/accessDialog.js
Normal file
202
js/ui/accessDialog.js
Normal file
@ -0,0 +1,202 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
const RequestIface = '<node> \
|
||||
<interface name="org.freedesktop.impl.portal.Request"> \
|
||||
<method name="Close"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const AccessIface = '<node> \
|
||||
<interface name="org.freedesktop.impl.portal.Access"> \
|
||||
<method name="AccessDialog"> \
|
||||
<arg type="o" name="handle" direction="in"/> \
|
||||
<arg type="s" name="app_id" direction="in"/> \
|
||||
<arg type="s" name="parent_window" direction="in"/> \
|
||||
<arg type="s" name="title" direction="in"/> \
|
||||
<arg type="s" name="subtitle" direction="in"/> \
|
||||
<arg type="s" name="body" direction="in"/> \
|
||||
<arg type="a{sv}" name="options" direction="in"/> \
|
||||
<arg type="u" name="response" direction="out"/> \
|
||||
<arg type="a{sv}" name="results" direction="out"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const DialogResponse = {
|
||||
OK: 0,
|
||||
CANCEL: 1,
|
||||
CLOSED: 2
|
||||
};
|
||||
|
||||
const AccessDialog = new Lang.Class({
|
||||
Name: 'AccessDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function(invocation, handle, title, subtitle, body, options) {
|
||||
this.parent({ styleClass: 'access-dialog' });
|
||||
|
||||
this._invocation = invocation;
|
||||
this._handle = handle;
|
||||
|
||||
this._requestExported = false;
|
||||
this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this);
|
||||
|
||||
for (let option in options)
|
||||
options[option] = options[option].deep_unpack();
|
||||
|
||||
this._buildLayout(title, subtitle, body, options);
|
||||
},
|
||||
|
||||
_buildLayout: function(title, subtitle, body, options) {
|
||||
// No support for non-modal system dialogs, so ignore the option
|
||||
//let modal = options['modal'] || true;
|
||||
let denyLabel = options['deny_label'] || _("Deny Access");
|
||||
let grantLabel = options['grant_label'] || _("Grant Access");
|
||||
let iconName = options['icon'] || null;
|
||||
let choices = options['choices'] || [];
|
||||
|
||||
let mainContentBox = new St.BoxLayout();
|
||||
mainContentBox.style_class = 'access-dialog-main-layout';
|
||||
this.contentLayout.add_actor(mainContentBox);
|
||||
|
||||
let icon = new St.Icon({ style_class: 'access-dialog-icon',
|
||||
icon_name: iconName,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
mainContentBox.add_actor(icon);
|
||||
|
||||
let messageBox = new St.BoxLayout({ vertical: true });
|
||||
messageBox.style_class = 'access-dialog-content',
|
||||
mainContentBox.add_actor(messageBox);
|
||||
|
||||
let label;
|
||||
label = new St.Label({ style_class: 'access-dialog-title headline',
|
||||
text: title });
|
||||
messageBox.add_actor(label);
|
||||
|
||||
label = new St.Label({ style_class: 'access-dialog-subtitle',
|
||||
text: subtitle });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
label.clutter_text.line_wrap = true;
|
||||
messageBox.add_actor(label);
|
||||
|
||||
this._choices = new Map();
|
||||
|
||||
for (let i = 0; i < choices.length; i++) {
|
||||
let [id, name, opts, selected] = choices[i];
|
||||
if (opts.length > 0)
|
||||
continue; // radio buttons, not implemented
|
||||
|
||||
let check = new CheckBox.CheckBox();
|
||||
check.getLabelActor().text = name;
|
||||
check.actor.checked = selected == "true";
|
||||
messageBox.add_actor(check.actor);
|
||||
|
||||
this._choices.set(id, check);
|
||||
}
|
||||
|
||||
label = new St.Label({ text: body });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
label.clutter_text.line_wrap = true;
|
||||
messageBox.add_actor(label);
|
||||
|
||||
this.addButton({ label: denyLabel,
|
||||
action: () => {
|
||||
this._sendResponse(DialogResponse.CANCEL);
|
||||
},
|
||||
key: Clutter.KEY_Escape });
|
||||
this.addButton({ label: grantLabel,
|
||||
action: () => {
|
||||
this._sendResponse(DialogResponse.OK);
|
||||
}});
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this.parent();
|
||||
|
||||
let connection = this._invocation.get_connection();
|
||||
this._requestExported = this._request.export(connection, this._handle);
|
||||
},
|
||||
|
||||
CloseAsync: function(invocation, params) {
|
||||
if (this._invocation.get_sender() != invocation.get_sender()) {
|
||||
invocation.return_error_literal(Gio.DBusError,
|
||||
Gio.DBusError.ACCESS_DENIED,
|
||||
'');
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendResponse(DialogResponse.CLOSED);
|
||||
},
|
||||
|
||||
_sendResponse: function(response) {
|
||||
if (this._requestExported)
|
||||
this._request.unexport();
|
||||
this._requestExported = false;
|
||||
|
||||
let results = {};
|
||||
if (response == DialogResponse.OK) {
|
||||
for (let [id, check] of this._choices) {
|
||||
let checked = check.actor.checked ? 'true' : 'false';
|
||||
results[id] = new GLib.Variant('s', checked);
|
||||
}
|
||||
}
|
||||
|
||||
// Delay actual response until the end of the close animation (if any)
|
||||
this.connect('closed', () => {
|
||||
this._invocation.return_value(new GLib.Variant('(ua{sv})',
|
||||
[response, results]));
|
||||
});
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
const AccessDialogDBus = new Lang.Class({
|
||||
Name: 'AccessDialogDBus',
|
||||
|
||||
_init: function() {
|
||||
this._accessDialog = null;
|
||||
|
||||
this._windowTracker = Shell.WindowTracker.get_default();
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop');
|
||||
|
||||
Gio.DBus.session.own_name('org.freedesktop.impl.portal.desktop.gnome', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
AccessDialogAsync: function(params, invocation) {
|
||||
if (this._accessDialog) {
|
||||
invocation.return_error_literal(Gio.DBusError,
|
||||
Gio.DBusError.LIMITS_EXCEEDED,
|
||||
'Already showing a system access dialog');
|
||||
return;
|
||||
}
|
||||
|
||||
let [handle, appId, parentWindow, title, subtitle, body, options] = params;
|
||||
// We probably want to use parentWindow and global.display.focus_window
|
||||
// for this check in the future
|
||||
if (appId && appId + '.desktop' != this._windowTracker.focus_app.id) {
|
||||
invocation.return_error_literal(Gio.DBusError,
|
||||
Gio.DBusError.ACCESS_DENIED,
|
||||
'Only the focused app is allowed to show a system access dialog');
|
||||
return;
|
||||
}
|
||||
|
||||
let dialog = new AccessDialog(invocation, handle, title,
|
||||
subtitle, body, options);
|
||||
dialog.open();
|
||||
|
||||
dialog.connect('closed', () => { this._accessDialog = null; });
|
||||
|
||||
this._accessDialog = dialog;
|
||||
}
|
||||
});
|
189
js/ui/altTab.js
189
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,149 @@ 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,
|
||||
Abstract: true,
|
||||
|
||||
_init : function() {
|
||||
this.parent();
|
||||
|
||||
this._items = this._getWindows();
|
||||
|
||||
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(),
|
||||
highlight: Lang.bind(this, this._highlightItem),
|
||||
connect: function() {} };
|
||||
},
|
||||
|
||||
_highlightItem: function(index, justOutline) {
|
||||
this._highlight.window = this._items[index];
|
||||
global.window_group.set_child_above_sibling(this._highlight.actor, null);
|
||||
},
|
||||
|
||||
_finish: function() {
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const GroupCyclerPopup = new Lang.Class({
|
||||
Name: 'GroupCyclerPopup',
|
||||
Extends: CyclerPopup,
|
||||
|
||||
_getWindows: function() {
|
||||
let app = Shell.WindowTracker.get_default().focus_app;
|
||||
return app ? app.get_windows() : [];
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.CYCLE_GROUP)
|
||||
this._select(this._next());
|
||||
else if (action == Meta.KeyBindingAction.CYCLE_GROUP_BACKWARD)
|
||||
this._select(this._previous());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
});
|
||||
|
||||
const WindowSwitcherPopup = new Lang.Class({
|
||||
Name: 'WindowSwitcherPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
@ -374,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) {
|
||||
@ -401,6 +556,32 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const WindowCyclerPopup = new Lang.Class({
|
||||
Name: 'WindowCyclerPopup',
|
||||
Extends: CyclerPopup,
|
||||
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' });
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_getWindows: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return getWindows(workspace);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.CYCLE_WINDOWS)
|
||||
this._select(this._next());
|
||||
else if (action == Meta.KeyBindingAction.CYCLE_WINDOWS_BACKWARD)
|
||||
this._select(this._previous());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
});
|
||||
|
||||
const AppIcon = new Lang.Class({
|
||||
Name: 'AppIcon',
|
||||
|
||||
|
@ -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
|
||||
@ -1814,7 +1853,7 @@ const AppIconMenu = new Lang.Class({
|
||||
if (!source.actor.mapped)
|
||||
this.close();
|
||||
}));
|
||||
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
|
||||
source.actor.connect('destroy', Lang.bind(this, this.destroy));
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
},
|
||||
@ -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)
|
||||
@ -696,6 +712,7 @@ const BackgroundManager = new Lang.Class({
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
oldBackgroundActor.background.run_dispose();
|
||||
oldBackgroundActor.destroy();
|
||||
}
|
||||
});
|
||||
|
@ -120,6 +120,9 @@ const EmptyEventSource = new Lang.Class({
|
||||
destroy: function() {
|
||||
},
|
||||
|
||||
ignoreEvent: function(event) {
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
},
|
||||
|
||||
@ -184,6 +187,15 @@ const DBusEventSource = new Lang.Class({
|
||||
this.isLoading = false;
|
||||
this.isDummy = false;
|
||||
|
||||
this._ignoredEvents = new Map();
|
||||
|
||||
let savedState = global.get_persistent_state('as', 'ignored_events');
|
||||
if (savedState)
|
||||
savedState.deep_unpack().forEach(Lang.bind(this,
|
||||
function(eventId) {
|
||||
this._ignoredEvents.set(eventId, true);
|
||||
}));
|
||||
|
||||
this._initialized = false;
|
||||
this._dbusProxy = new CalendarServer();
|
||||
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
|
||||
@ -298,6 +310,16 @@ const DBusEventSource = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
ignoreEvent: function(event) {
|
||||
if (this._ignoredEvents.get(event.id))
|
||||
return;
|
||||
|
||||
this._ignoredEvents.set(event.id, true);
|
||||
let savedState = new GLib.Variant('as', [...this._ignoredEvents.keys()]);
|
||||
global.set_persistent_state('ignored_events', savedState);
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this.isLoading = true;
|
||||
@ -313,6 +335,10 @@ const DBusEventSource = new Lang.Class({
|
||||
let result = [];
|
||||
for(let n = 0; n < this._events.length; n++) {
|
||||
let event = this._events[n];
|
||||
|
||||
if (this._ignoredEvents.has(event.id))
|
||||
continue;
|
||||
|
||||
if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
|
||||
result.push(event);
|
||||
}
|
||||
@ -345,7 +371,7 @@ const Calendar = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.calendar' });
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' });
|
||||
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
@ -660,12 +686,12 @@ const Calendar = new Lang.Class({
|
||||
|
||||
this._buttons.forEach(Lang.bind(this, function(button) {
|
||||
if (sameDay(button._date, this._selectedDate)) {
|
||||
button.add_style_pseudo_class('active');
|
||||
button.add_style_pseudo_class('selected');
|
||||
if (this._shouldDateGrabFocus)
|
||||
button.grab_key_focus();
|
||||
}
|
||||
else
|
||||
button.remove_style_pseudo_class('active');
|
||||
button.remove_style_pseudo_class('selected');
|
||||
}));
|
||||
}
|
||||
});
|
||||
@ -785,15 +811,6 @@ const EventsSection = new Lang.Class({
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._reloadEvents));
|
||||
this._eventSource = new EmptyEventSource();
|
||||
|
||||
this._ignoredEvents = new Map();
|
||||
|
||||
let savedState = global.get_persistent_state('as', 'ignored_events');
|
||||
if (savedState)
|
||||
savedState.deep_unpack().forEach(Lang.bind(this,
|
||||
function(eventId) {
|
||||
this._ignoredEvents.set(eventId, true);
|
||||
}));
|
||||
|
||||
this.parent('');
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
@ -802,9 +819,7 @@ const EventsSection = new Lang.Class({
|
||||
},
|
||||
|
||||
_ignoreEvent: function(event) {
|
||||
this._ignoredEvents.set(event.id, true);
|
||||
let savedState = new GLib.Variant('as', [...this._ignoredEvents.keys()]);
|
||||
global.set_persistent_state('ignored_events', savedState);
|
||||
this._eventSource.ignoreEvent(event);
|
||||
},
|
||||
|
||||
setEventSource: function(eventSource) {
|
||||
@ -850,9 +865,6 @@ const EventsSection = new Lang.Class({
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
let event = events[i];
|
||||
|
||||
if (this._ignoredEvents.has(event.id))
|
||||
continue;
|
||||
|
||||
let message = new EventMessage(event, this._date);
|
||||
message.connect('close', Lang.bind(this, function() {
|
||||
this._ignoreEvent(event);
|
||||
@ -890,7 +902,7 @@ const EventsSection = new Lang.Class({
|
||||
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) {
|
||||
|
@ -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;
|
||||
@ -796,13 +803,21 @@ const NetworkAgent = new Lang.Class({
|
||||
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
|
||||
}
|
||||
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) {
|
||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints };
|
||||
else
|
||||
try {
|
||||
let aliases = keyfile.get_string_list('VPN Connection', 'aliases');
|
||||
|
||||
for (let alias of aliases) {
|
||||
this._vpnBinaries[alias] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints };
|
||||
}
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
} else {
|
||||
throw new Error('VPN plugin at %s is not executable'.format(path));
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
@ -475,6 +475,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
|
||||
|
@ -77,7 +77,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);
|
||||
}
|
||||
});
|
||||
|
70
js/ui/dnd.js
70
js/ui/dnd.js
@ -571,20 +571,13 @@ const _Draggable = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this._animationInProgress = true;
|
||||
// No target, so snap back
|
||||
Tweener.addTween(this._dragActor,
|
||||
{ x: snapBackX,
|
||||
y: snapBackY,
|
||||
scale_x: snapBackScale,
|
||||
scale_y: snapBackScale,
|
||||
opacity: this._dragOrigOpacity,
|
||||
time: SNAP_BACK_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._onAnimationComplete,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [this._dragActor, eventTime]
|
||||
});
|
||||
this._animateDragEnd(eventTime,
|
||||
{ x: snapBackX,
|
||||
y: snapBackY,
|
||||
scale_x: snapBackScale,
|
||||
scale_y: snapBackScale,
|
||||
time: SNAP_BACK_ANIMATION_TIME,
|
||||
});
|
||||
},
|
||||
|
||||
_restoreDragActor: function(eventTime) {
|
||||
@ -596,18 +589,44 @@ const _Draggable = new Lang.Class({
|
||||
this._dragActor.set_scale(restoreScale, restoreScale);
|
||||
this._dragActor.opacity = 0;
|
||||
|
||||
this._animateDragEnd(eventTime,
|
||||
{ time: REVERT_ANIMATION_TIME });
|
||||
},
|
||||
|
||||
_animateDragEnd: function (eventTime, params) {
|
||||
this._animationInProgress = true;
|
||||
Tweener.addTween(this._dragActor,
|
||||
{ opacity: this._dragOrigOpacity,
|
||||
time: REVERT_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._onAnimationComplete,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [this._dragActor, eventTime]
|
||||
});
|
||||
|
||||
// finish animation if the actor gets destroyed
|
||||
// during it
|
||||
this._dragActorDestroyId =
|
||||
this._dragActor.connect('destroy',
|
||||
Lang.bind(this, this._finishAnimation));
|
||||
|
||||
params['opacity'] = this._dragOrigOpacity;
|
||||
params['transition'] = 'easeOutQuad';
|
||||
params['onComplete'] = this._onAnimationComplete;
|
||||
params['onCompleteScope'] = this;
|
||||
params['onCompleteParams'] = [this._dragActor, eventTime];
|
||||
|
||||
// start the animation
|
||||
Tweener.addTween(this._dragActor, params)
|
||||
},
|
||||
|
||||
_finishAnimation : function () {
|
||||
if (!this._animationInProgress)
|
||||
return
|
||||
|
||||
this._animationInProgress = false;
|
||||
if (!this._buttonDown)
|
||||
this._dragComplete();
|
||||
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
},
|
||||
|
||||
_onAnimationComplete : function (dragActor, eventTime) {
|
||||
dragActor.disconnect(this._dragActorDestroyId);
|
||||
this._dragActorDestroyId = 0;
|
||||
|
||||
if (this._dragOrigParent) {
|
||||
Main.uiGroup.remove_child(this._dragActor);
|
||||
this._dragOrigParent.add_actor(this._dragActor);
|
||||
@ -616,12 +635,9 @@ const _Draggable = new Lang.Class({
|
||||
} else {
|
||||
dragActor.destroy();
|
||||
}
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this.emit('drag-end', eventTime, false);
|
||||
|
||||
this._animationInProgress = false;
|
||||
if (!this._buttonDown)
|
||||
this._dragComplete();
|
||||
this.emit('drag-end', eventTime, false);
|
||||
this._finishAnimation();
|
||||
},
|
||||
|
||||
_dragComplete: function() {
|
||||
|
@ -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);
|
||||
|
@ -1,6 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2010 Red Hat, Inc
|
||||
* Copyright 2010-2016 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -114,7 +114,7 @@ const restartDialogContent = {
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const restartInstallDialogContent = {
|
||||
const restartUpdateDialogContent = {
|
||||
|
||||
subject: C_("title", "Restart & Install Updates"),
|
||||
description: function(seconds) {
|
||||
@ -132,18 +132,38 @@ const restartInstallDialogContent = {
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const restartUpgradeDialogContent = {
|
||||
|
||||
subject: C_("title", "Restart & Install Upgrade"),
|
||||
upgradeDescription: function(distroName, distroVersion) {
|
||||
/* Translators: This is the text displayed for system upgrades in the
|
||||
shut down dialog. First %s gets replaced with the distro name and
|
||||
second %s with the distro version to upgrade to */
|
||||
return _("%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have backed up and that the computer is plugged in.").format(distroName, distroVersion);
|
||||
},
|
||||
disableTimer: true,
|
||||
showBatteryWarning: false,
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart & Install") }],
|
||||
iconName: 'view-refresh-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const DialogType = {
|
||||
LOGOUT: 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */,
|
||||
SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */,
|
||||
RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */,
|
||||
UPDATE_RESTART: 3
|
||||
UPDATE_RESTART: 3,
|
||||
UPGRADE_RESTART: 4
|
||||
};
|
||||
|
||||
const DialogContent = {
|
||||
0 /* DialogType.LOGOUT */: logoutDialogContent,
|
||||
1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* DialogType.RESTART */: restartDialogContent,
|
||||
3 /* DialogType.UPDATE_RESTART */: restartInstallDialogContent
|
||||
3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent,
|
||||
4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent
|
||||
};
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
@ -163,7 +183,10 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
|
||||
const PkOfflineIface = '<node> \
|
||||
<interface name="org.freedesktop.PackageKit.Offline"> \
|
||||
<property name="UpdatePrepared" type="b" access="read"/> \
|
||||
<property name="TriggerAction" type="s" access="read"/> \
|
||||
<property name="UpdateTriggered" type="b" access="read"/> \
|
||||
<property name="UpgradePrepared" type="b" access="read"/> \
|
||||
<property name="UpgradeTriggered" type="b" access="read"/> \
|
||||
<property name="PreparedUpgrade" type="a{sv}" access="read"/> \
|
||||
<method name="Trigger"> \
|
||||
<arg type="s" name="action" direction="in"/> \
|
||||
</method> \
|
||||
@ -415,18 +438,25 @@ const EndSessionDialog = new Lang.Class({
|
||||
|
||||
if (dialogContent.descriptionWithUser)
|
||||
description = dialogContent.descriptionWithUser(realName, displayTime);
|
||||
else
|
||||
description = dialogContent.description(displayTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Use a different description when we are installing a system upgrade
|
||||
if (dialogContent.upgradeDescription) {
|
||||
let name = this._pkOfflineProxy.PreparedUpgrade['name'].deep_unpack();
|
||||
let version = this._pkOfflineProxy.PreparedUpgrade['version'].deep_unpack();
|
||||
|
||||
if (name != null && version != null)
|
||||
description = dialogContent.upgradeDescription(name, version);
|
||||
}
|
||||
|
||||
// Fall back to regular description
|
||||
if (!description)
|
||||
description = dialogContent.description(displayTime);
|
||||
|
||||
_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,
|
||||
@ -698,9 +728,12 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._type = type;
|
||||
|
||||
if (this._type == DialogType.RESTART &&
|
||||
this._pkOfflineProxy.TriggerAction == 'reboot')
|
||||
this._type = DialogType.UPDATE_RESTART;
|
||||
if (this._type == DialogType.RESTART) {
|
||||
if (this._pkOfflineProxy.UpdateTriggered)
|
||||
this._type = DialogType.UPDATE_RESTART;
|
||||
else if (this._pkOfflineProxy.UpgradeTriggered)
|
||||
this._type = DialogType.UPGRADE_RESTART;
|
||||
}
|
||||
|
||||
this._applications = [];
|
||||
this._applicationList.destroy_all_children();
|
||||
@ -727,19 +760,19 @@ const EndSessionDialog = new Lang.Class({
|
||||
if (dialogContent.showOtherSessions)
|
||||
this._loadSessions();
|
||||
|
||||
let updateAlreadyTriggered = this._pkOfflineProxy.TriggerAction == 'power-off' || this._pkOfflineProxy.TriggerAction == 'reboot';
|
||||
let updateTriggered = this._pkOfflineProxy.UpdateTriggered;
|
||||
let updatePrepared = this._pkOfflineProxy.UpdatePrepared;
|
||||
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
|
||||
|
||||
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
|
||||
this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed);
|
||||
this._checkBox.actor.checked = (updatePrepared && updateAlreadyTriggered);
|
||||
this._checkBox.actor.checked = (updatePrepared && updateTriggered);
|
||||
|
||||
// We show the warning either together with the checkbox, or when
|
||||
// updates have already been triggered, but the user doesn't have
|
||||
// enough permissions to cancel them.
|
||||
this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
|
||||
(this._checkBox.actor.visible || updatePrepared && updateAlreadyTriggered && !updatesAllowed));
|
||||
(this._checkBox.actor.visible || updatePrepared && updateTriggered && !updatesAllowed));
|
||||
|
||||
this._updateButtons();
|
||||
|
||||
@ -749,7 +782,9 @@ const EndSessionDialog = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
this._startTimer();
|
||||
if (!dialogContent.disableTimer)
|
||||
this._startTimer();
|
||||
|
||||
this._sync();
|
||||
|
||||
let signalId = this.connect('opened',
|
||||
|
@ -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);
|
||||
},
|
||||
});
|
||||
|
@ -220,7 +220,8 @@ const LayoutManager = new Lang.Class({
|
||||
global.stage.add_child(this.uiGroup);
|
||||
|
||||
this.overviewGroup = new St.Widget({ name: 'overviewGroup',
|
||||
visible: false });
|
||||
visible: false,
|
||||
reactive: true });
|
||||
this.addChrome(this.overviewGroup);
|
||||
|
||||
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
|
||||
@ -591,7 +592,10 @@ const LayoutManager = new Lang.Class({
|
||||
this.addChrome(this._coverPane);
|
||||
|
||||
if (Meta.is_restart()) {
|
||||
// On restart, we don't do an animation
|
||||
// On restart, we don't do an animation. Force an update of the
|
||||
// regions immediately so that maximized windows restore to the
|
||||
// right size taking struts into account.
|
||||
this._updateRegions();
|
||||
} else if (Main.sessionMode.isGreeter) {
|
||||
this.panelBox.translation_y = -this.panelBox.height;
|
||||
} else {
|
||||
@ -938,6 +942,11 @@ const LayoutManager = new Lang.Class({
|
||||
if (Main.modalCount > 0)
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
// Bug workaround - get_transformed_position()/get_transformed_size() don't work after
|
||||
// a change in stage size until the first pick or paint.
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=761565
|
||||
global.stage.get_actor_at_pos(Clutter.PickMode.ALL, 0, 0);
|
||||
|
||||
let rects = [], struts = [], i;
|
||||
let isPopupMenuVisible = global.top_window_group.get_children().some(isPopupMetaWindow);
|
||||
let wantsInputRegion = !isPopupMenuVisible;
|
||||
|
@ -11,6 +11,7 @@ const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AccessDialog = imports.ui.accessDialog;
|
||||
const AudioDeviceSelection = imports.ui.audioDeviceSelection;
|
||||
const Components = imports.ui.components;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
@ -25,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;
|
||||
@ -60,9 +62,11 @@ let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let padOsdService = null;
|
||||
let osdWindowManager = null;
|
||||
let osdMonitorLabeler = null;
|
||||
let sessionMode = null;
|
||||
let shellAccessDialogDBusService = null;
|
||||
let shellAudioSelectionDBusService = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
@ -122,6 +126,7 @@ function start() {
|
||||
_loadDefaultStylesheet);
|
||||
_initializeUI();
|
||||
|
||||
shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus();
|
||||
shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus();
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
@ -152,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();
|
||||
@ -191,6 +197,8 @@ function _initializeUI() {
|
||||
return true;
|
||||
});
|
||||
|
||||
global.display.connect('gl-video-memory-purged', loadTheme);
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
|
@ -259,7 +259,7 @@ const ModalDialog = new Lang.Class({
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
return true;
|
||||
|
||||
if (!this.pushModal({ timestamp: timestamp }))
|
||||
if (!this.pushModal(timestamp))
|
||||
return false;
|
||||
|
||||
this._fadeOpen(onPrimary);
|
||||
@ -318,8 +318,11 @@ const ModalDialog = new Lang.Class({
|
||||
pushModal: function (timestamp) {
|
||||
if (this._hasModal)
|
||||
return true;
|
||||
if (!Main.pushModal(this._group, { timestamp: timestamp,
|
||||
actionMode: this._actionMode }))
|
||||
|
||||
let params = { actionMode: this._actionMode };
|
||||
if (timestamp)
|
||||
params['timestamp'] = timestamp;
|
||||
if (!Main.pushModal(this._group, params))
|
||||
return false;
|
||||
|
||||
this._hasModal = true;
|
||||
|
@ -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() {
|
||||
@ -690,6 +690,7 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||
throw new InvalidAppError();
|
||||
|
||||
this._notifications = {};
|
||||
this._notificationPending = false;
|
||||
|
||||
this.parent(this._app.get_name());
|
||||
},
|
||||
@ -702,27 +703,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 +745,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) {
|
||||
|
@ -22,11 +22,13 @@ const LevelBar = new Lang.Class({
|
||||
this._level = 0;
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'level',
|
||||
x_fill: true, y_fill: true });
|
||||
this._bar = new St.DrawingArea();
|
||||
this._bar.connect('repaint', Lang.bind(this, this._repaint));
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this._bar = new St.Widget({ style_class: 'level-bar' });
|
||||
|
||||
this.actor.set_child(this._bar);
|
||||
|
||||
this.actor.connect('notify::width', () => { this.level = this.level; });
|
||||
},
|
||||
|
||||
get level() {
|
||||
@ -34,39 +36,44 @@ const LevelBar = new Lang.Class({
|
||||
},
|
||||
|
||||
set level(value) {
|
||||
let newValue = Math.max(0, Math.min(value, 100));
|
||||
if (newValue == this._level)
|
||||
return;
|
||||
this._level = newValue;
|
||||
this._bar.queue_repaint();
|
||||
this._level = Math.max(0, Math.min(value, 100));
|
||||
|
||||
let alloc = this.actor.get_allocation_box();
|
||||
let newWidth = Math.round((alloc.x2 - alloc.x1) * this._level / 100);
|
||||
if (newWidth != this._bar.width)
|
||||
this._bar.width = newWidth;
|
||||
}
|
||||
});
|
||||
|
||||
const OsdWindowConstraint = new Lang.Class({
|
||||
Name: 'OsdWindowConstraint',
|
||||
Extends: Clutter.Constraint,
|
||||
|
||||
_init: function(props) {
|
||||
this._minSize = 0;
|
||||
this.parent(props);
|
||||
},
|
||||
|
||||
_repaint: function() {
|
||||
let cr = this._bar.get_context();
|
||||
set minSize(v) {
|
||||
this._minSize = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
let node = this.actor.get_theme_node();
|
||||
let radius = node.get_border_radius(0); // assume same radius for all corners
|
||||
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
||||
vfunc_update_allocation: function(actor, actorBox) {
|
||||
// Clutter will adjust the allocation for margins,
|
||||
// so add it to our minimum size
|
||||
let minSize = this._minSize + actor.margin_top + actor.margin_bottom;
|
||||
let [width, height] = actorBox.get_size();
|
||||
|
||||
let [w, h] = this._bar.get_surface_size();
|
||||
w *= (this._level / 100.);
|
||||
// Enforce a ratio of 1
|
||||
let size = Math.ceil(Math.max(minSize, height));
|
||||
actorBox.set_size(size, size);
|
||||
|
||||
if (w == 0)
|
||||
return;
|
||||
|
||||
cr.moveTo(radius, 0);
|
||||
if (w >= radius)
|
||||
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
|
||||
else
|
||||
cr.lineTo(w, 0);
|
||||
if (w >= radius)
|
||||
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
|
||||
else
|
||||
cr.lineTo(w, h);
|
||||
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
|
||||
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
// Recenter
|
||||
let [x, y] = actorBox.get_origin();
|
||||
actorBox.set_origin(Math.ceil(x + width / 2 - size / 2),
|
||||
Math.ceil(y + height / 2 - size / 2));
|
||||
}
|
||||
});
|
||||
|
||||
@ -74,7 +81,6 @@ const OsdWindow = new Lang.Class({
|
||||
Name: 'OsdWindow',
|
||||
|
||||
_init: function(monitorIndex) {
|
||||
this._popupSize = 0;
|
||||
this.actor = new St.Widget({ x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -84,19 +90,12 @@ const OsdWindow = new Lang.Class({
|
||||
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this._boxConstraint = new OsdWindowConstraint();
|
||||
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
||||
vertical: true });
|
||||
this._box.add_constraint(this._boxConstraint);
|
||||
this.actor.add_actor(this._box);
|
||||
|
||||
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
this._box.connect('notify::height', Lang.bind(this,
|
||||
function() {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||
function() {
|
||||
this._box.width = this._box.height;
|
||||
}));
|
||||
}));
|
||||
|
||||
this._icon = new St.Icon();
|
||||
this._box.add(this._icon, { expand: true });
|
||||
|
||||
@ -110,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);
|
||||
},
|
||||
|
||||
@ -189,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)
|
||||
@ -198,30 +200,12 @@ const OsdWindow = new Lang.Class({
|
||||
let scalew = monitor.width / 640.0;
|
||||
let scaleh = monitor.height / 480.0;
|
||||
let scale = Math.min(scalew, scaleh);
|
||||
this._popupSize = 110 * Math.max(1, scale);
|
||||
let popupSize = 110 * Math.max(1, scale);
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._icon.icon_size = this._popupSize / (2 * scaleFactor);
|
||||
this._icon.icon_size = popupSize / (2 * scaleFactor);
|
||||
this._box.translation_y = monitor.height / 4;
|
||||
this._box.style_changed();
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this._box.get_theme_node();
|
||||
let horizontalPadding = themeNode.get_horizontal_padding();
|
||||
let verticalPadding = themeNode.get_vertical_padding();
|
||||
let topBorder = themeNode.get_border_width(St.Side.TOP);
|
||||
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
|
||||
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
|
||||
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
|
||||
|
||||
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
|
||||
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
|
||||
|
||||
// minWidth/minHeight here are in real pixels,
|
||||
// but the theme takes measures in unscaled dimensions
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor);
|
||||
this._boxConstraint.minSize = popupSize;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -107,18 +107,12 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._overviewCreated = true;
|
||||
|
||||
let layout = new Clutter.BinLayout();
|
||||
this._stack = new Clutter.Actor({ layout_manager: layout });
|
||||
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: 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"),
|
||||
reactive: true,
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
vertical: true });
|
||||
this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||
this._overview._delegate = this;
|
||||
|
||||
// The main Background actors are inside global.window_group which are
|
||||
@ -126,7 +120,7 @@ const Overview = new Lang.Class({
|
||||
// one. Instances of this class share a single CoglTexture behind the
|
||||
// scenes which allows us to show the background with different
|
||||
// rendering options without duplicating the texture data.
|
||||
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||
this._backgroundGroup = new Meta.BackgroundGroup({ reactive: true });
|
||||
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
|
||||
this._bgManagers = [];
|
||||
|
||||
@ -149,8 +143,7 @@ const Overview = new Lang.Class({
|
||||
Main.layoutManager.overviewGroup.add_child(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; }));
|
||||
|
||||
this._stack.add_actor(this._overview);
|
||||
Main.layoutManager.overviewGroup.add_child(this._stack);
|
||||
Main.layoutManager.overviewGroup.add_child(this._overview);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
@ -159,6 +152,9 @@ const Overview = new Lang.Class({
|
||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||
};
|
||||
|
||||
|
||||
Main.layoutManager.overviewGroup.connect('scroll-event',
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
@ -255,7 +251,6 @@ const Overview = new Lang.Class({
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
|
||||
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
@ -372,7 +367,7 @@ const Overview = new Lang.Class({
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._overview.add_action(action);
|
||||
this._backgroundGroup.add_action(action);
|
||||
},
|
||||
|
||||
_getDesktopClone: function() {
|
||||
@ -383,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();
|
||||
@ -552,8 +547,8 @@ const Overview = new Lang.Class({
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this.viewSelector.show();
|
||||
|
||||
this._stack.opacity = 0;
|
||||
Tweener.addTween(this._stack,
|
||||
this._overview.opacity = 0;
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -618,7 +613,7 @@ const Overview = new Lang.Class({
|
||||
this.viewSelector.animateFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._stack,
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
|
@ -421,7 +421,6 @@ const ControlsManager = new Lang.Class({
|
||||
|
||||
let layout = new ControlsLayout();
|
||||
this.actor = new St.Widget({ layout_manager: layout,
|
||||
reactive: true,
|
||||
x_expand: true, y_expand: true,
|
||||
clip_to_allocation: true });
|
||||
this._group = new St.BoxLayout({ name: 'overview-group',
|
||||
|
761
js/ui/padOsd.js
Normal file
761
js/ui/padOsd.js
Normal file
@ -0,0 +1,761 @@
|
||||
// -*- 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 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._settings = settings;
|
||||
this._imagePath = imagePath;
|
||||
this._editionMode = editionMode;
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
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();
|
||||
}));
|
||||
|
||||
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._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());
|
||||
this.actor.add_actor(this._titleLabel);
|
||||
|
||||
this._tipLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_actor(this._tipLabel);
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (this._deviceRemovedId != 0) {
|
||||
let deviceManager = Clutter.DeviceManager.get_default();
|
||||
deviceManager.disconnect(this._deviceRemovedId);
|
||||
this._deviceRemovedId = 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;
|
||||
|
@ -349,7 +349,6 @@ const Arrow = new Lang.Class({
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
this.x_fill = this.y_fill = true;
|
||||
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
|
||||
this._drawingArea = new St.DrawingArea();
|
||||
this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
|
||||
@ -377,6 +376,22 @@ const Arrow = new Lang.Class({
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
vfunc_get_paint_volume: function(volume) {
|
||||
if (!this.parent(volume))
|
||||
return false;
|
||||
|
||||
if (!this._shadow)
|
||||
return true;
|
||||
|
||||
let shadow_box = new Clutter.ActorBox();
|
||||
this._shadow.get_box(this._drawingArea.get_allocation_box(), shadow_box);
|
||||
|
||||
volume.set_width(Math.max(shadow_box.x2 - shadow_box.x1, volume.get_width()));
|
||||
volume.set_height(Math.max(shadow_box.y2 - shadow_box.y1, volume.get_height()));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
vfunc_style_changed: function() {
|
||||
let node = this.get_theme_node();
|
||||
this._shadow = node.get_shadow('-arrow-shadow');
|
||||
@ -384,6 +399,8 @@ const Arrow = new Lang.Class({
|
||||
this._shadowHelper = St.ShadowHelper.new(this._shadow);
|
||||
else
|
||||
this._shadowHelper = null;
|
||||
|
||||
this.parent();
|
||||
},
|
||||
|
||||
vfunc_paint: function() {
|
||||
@ -559,6 +576,9 @@ const ScreenShield = new Lang.Class({
|
||||
if (prevIsActive != this._isActive)
|
||||
this.emit('active-changed');
|
||||
|
||||
if (this._loginSession)
|
||||
this._loginSession.SetLockedHintRemote(active);
|
||||
|
||||
this._syncInhibitor();
|
||||
},
|
||||
|
||||
@ -645,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 &&
|
||||
@ -708,7 +731,7 @@ const ScreenShield = new Lang.Class({
|
||||
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
|
||||
let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
|
||||
for (let i = 0; i < arrows.length; i++) {
|
||||
arrows.opacity = 0;
|
||||
arrows[i].opacity = 0;
|
||||
Tweener.addTween(arrows[i],
|
||||
{ opacity: 0,
|
||||
delay: unitaryDelay * (N_ARROWS - (i + 1)),
|
||||
@ -827,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)
|
||||
|
@ -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");
|
||||
},
|
||||
|
@ -61,8 +61,8 @@ const InputSource = new Lang.Class({
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.emit('activate');
|
||||
activate: function(interactive) {
|
||||
this.emit('activate', !!interactive);
|
||||
},
|
||||
|
||||
_getXkbId: function() {
|
||||
@ -109,7 +109,7 @@ const InputSourcePopup = new Lang.Class({
|
||||
_finish : function() {
|
||||
this.parent();
|
||||
|
||||
this._items[this._selectedIndex].activate();
|
||||
this._items[this._selectedIndex].activate(true);
|
||||
},
|
||||
});
|
||||
|
||||
@ -159,6 +159,14 @@ const InputSourceSettings = new Lang.Class({
|
||||
return [];
|
||||
},
|
||||
|
||||
get mruSources() {
|
||||
return [];
|
||||
},
|
||||
|
||||
set mruSources(sourcesList) {
|
||||
// do nothing
|
||||
},
|
||||
|
||||
get keyboardOptions() {
|
||||
return [];
|
||||
},
|
||||
@ -251,6 +259,7 @@ const InputSourceSessionSettings = new Lang.Class({
|
||||
|
||||
_DESKTOP_INPUT_SOURCES_SCHEMA: 'org.gnome.desktop.input-sources',
|
||||
_KEY_INPUT_SOURCES: 'sources',
|
||||
_KEY_MRU_SOURCES: 'mru-sources',
|
||||
_KEY_KEYBOARD_OPTIONS: 'xkb-options',
|
||||
_KEY_PER_WINDOW: 'per-window',
|
||||
|
||||
@ -261,9 +270,9 @@ const InputSourceSessionSettings = new Lang.Class({
|
||||
this._settings.connect('changed::' + this._KEY_PER_WINDOW, Lang.bind(this, this._emitPerWindowChanged));
|
||||
},
|
||||
|
||||
get inputSources() {
|
||||
_getSourcesList: function(key) {
|
||||
let sourcesList = [];
|
||||
let sources = this._settings.get_value(this._KEY_INPUT_SOURCES);
|
||||
let sources = this._settings.get_value(key);
|
||||
let nSources = sources.n_children();
|
||||
|
||||
for (let i = 0; i < nSources; i++) {
|
||||
@ -273,6 +282,19 @@ const InputSourceSessionSettings = new Lang.Class({
|
||||
return sourcesList;
|
||||
},
|
||||
|
||||
get inputSources() {
|
||||
return this._getSourcesList(this._KEY_INPUT_SOURCES);
|
||||
},
|
||||
|
||||
get mruSources() {
|
||||
return this._getSourcesList(this._KEY_MRU_SOURCES);
|
||||
},
|
||||
|
||||
set mruSources(sourcesList) {
|
||||
let sources = GLib.Variant.new('a(ss)', sourcesList);
|
||||
this._settings.set_value(this._KEY_MRU_SOURCES, sources);
|
||||
},
|
||||
|
||||
get keyboardOptions() {
|
||||
return this._settings.get_strv(this._KEY_KEYBOARD_OPTIONS);
|
||||
},
|
||||
@ -372,7 +394,7 @@ const InputSourceManager = new Lang.Class({
|
||||
while (!(is = this._inputSources[nextIndex]))
|
||||
nextIndex += 1;
|
||||
|
||||
is.activate();
|
||||
is.activate(true);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -400,6 +422,25 @@ const InputSourceManager = new Lang.Class({
|
||||
this._keyboardManager.reapply();
|
||||
},
|
||||
|
||||
_updateMruSettings: function() {
|
||||
// If IBus is not ready we don't have a full picture of all
|
||||
// the available sources, so don't update the setting
|
||||
if (!this._ibusReady)
|
||||
return;
|
||||
|
||||
// If IBus is temporarily disabled, don't update the setting
|
||||
if (this._disableIBus)
|
||||
return;
|
||||
|
||||
let sourcesList = [];
|
||||
for (let i = 0; i < this._mruSources.length; ++i) {
|
||||
let source = this._mruSources[i];
|
||||
sourcesList.push([source.type, source.id]);
|
||||
}
|
||||
|
||||
this._settings.mruSources = sourcesList;
|
||||
},
|
||||
|
||||
_currentInputSourceChanged: function(newSource) {
|
||||
let oldSource;
|
||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||
@ -416,7 +457,7 @@ const InputSourceManager = new Lang.Class({
|
||||
this._changePerWindowSource();
|
||||
},
|
||||
|
||||
_activateInputSource: function(is) {
|
||||
_activateInputSource: function(is, interactive) {
|
||||
KeyboardManager.holdKeyboard();
|
||||
this._keyboardManager.apply(is.xkbId);
|
||||
|
||||
@ -434,6 +475,54 @@ const InputSourceManager = new Lang.Class({
|
||||
|
||||
this._ibusManager.setEngine(engine, KeyboardManager.releaseKeyboard);
|
||||
this._currentInputSourceChanged(is);
|
||||
|
||||
if (interactive)
|
||||
this._updateMruSettings();
|
||||
},
|
||||
|
||||
_updateMruSources: function() {
|
||||
let sourcesList = [];
|
||||
for (let i in this._inputSources)
|
||||
sourcesList.push(this._inputSources[i]);
|
||||
|
||||
this._keyboardManager.setUserLayouts(sourcesList.map(function(x) { return x.xkbId; }));
|
||||
|
||||
if (!this._disableIBus && this._mruSourcesBackup) {
|
||||
this._mruSources = this._mruSourcesBackup;
|
||||
this._mruSourcesBackup = null;
|
||||
}
|
||||
|
||||
// Initialize from settings when we have no MRU sources list
|
||||
if (this._mruSources.length == 0) {
|
||||
let mruSettings = this._settings.mruSources;
|
||||
for (let i = 0; i < mruSettings.length; i++) {
|
||||
let mruSettingSource = mruSettings[i];
|
||||
let mruSource = null;
|
||||
|
||||
for (let j = 0; j < sourcesList.length; j++) {
|
||||
let source = sourcesList[j];
|
||||
if (source.type == mruSettingSource.type &&
|
||||
source.id == mruSettingSource.id) {
|
||||
mruSource = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mruSource)
|
||||
this._mruSources.push(mruSource);
|
||||
}
|
||||
}
|
||||
|
||||
let mruSources = [];
|
||||
for (let i = 0; i < this._mruSources.length; i++) {
|
||||
for (let j = 0; j < sourcesList.length; j++)
|
||||
if (this._mruSources[i].type == sourcesList[j].type &&
|
||||
this._mruSources[i].id == sourcesList[j].id) {
|
||||
mruSources = mruSources.concat(sourcesList.splice(j, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._mruSources = mruSources.concat(sourcesList);
|
||||
},
|
||||
|
||||
_inputSourcesChanged: function() {
|
||||
@ -510,30 +599,10 @@ const InputSourceManager = new Lang.Class({
|
||||
|
||||
this.emit('sources-changed');
|
||||
|
||||
let sourcesList = [];
|
||||
for (let i in this._inputSources)
|
||||
sourcesList.push(this._inputSources[i]);
|
||||
|
||||
this._keyboardManager.setUserLayouts(sourcesList.map(function(x) { return x.xkbId; }));
|
||||
|
||||
if (!this._disableIBus && this._mruSourcesBackup) {
|
||||
this._mruSources = this._mruSourcesBackup;
|
||||
this._mruSourcesBackup = null;
|
||||
}
|
||||
|
||||
let mruSources = [];
|
||||
for (let i = 0; i < this._mruSources.length; i++) {
|
||||
for (let j = 0; j < sourcesList.length; j++)
|
||||
if (this._mruSources[i].type == sourcesList[j].type &&
|
||||
this._mruSources[i].id == sourcesList[j].id) {
|
||||
mruSources = mruSources.concat(sourcesList.splice(j, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._mruSources = mruSources.concat(sourcesList);
|
||||
this._updateMruSources();
|
||||
|
||||
if (this._mruSources.length > 0)
|
||||
this._mruSources[0].activate();
|
||||
this._mruSources[0].activate(false);
|
||||
|
||||
// All ibus engines are preloaded here to reduce the launching time
|
||||
// when users switch the input sources.
|
||||
@ -642,7 +711,7 @@ const InputSourceManager = new Lang.Class({
|
||||
}
|
||||
|
||||
if (window._currentSource)
|
||||
window._currentSource.activate();
|
||||
window._currentSource.activate(false);
|
||||
},
|
||||
|
||||
_sourcesPerWindowChanged: function() {
|
||||
@ -763,7 +832,10 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let is = this._inputSourceManager.inputSources[i];
|
||||
|
||||
let menuItem = new LayoutMenuItem(is.displayName, is.shortName);
|
||||
menuItem.connect('activate', Lang.bind(is, is.activate));
|
||||
menuItem.connect('activate', function() {
|
||||
is.activate(true);
|
||||
});
|
||||
|
||||
let indicatorLabel = new St.Label({ text: is.shortName,
|
||||
visible: false });
|
||||
|
||||
|
@ -62,8 +62,8 @@ var AgentIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
var XdgAppIface = '<node> \
|
||||
<interface name="org.freedesktop.XdgApp.PermissionStore"> \
|
||||
var PermissionStoreIface = '<node> \
|
||||
<interface name="org.freedesktop.impl.portal.PermissionStore"> \
|
||||
<method name="Lookup"> \
|
||||
<arg name="table" type="s" direction="in"/> \
|
||||
<arg name="id" type="s" direction="in"/> \
|
||||
@ -80,7 +80,7 @@ var XdgAppIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PermissionStore = Gio.DBusProxy.makeProxyWrapper(XdgAppIface);
|
||||
const PermissionStore = Gio.DBusProxy.makeProxyWrapper(PermissionStoreIface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LocationIndicator',
|
||||
@ -254,8 +254,8 @@ const Indicator = new Lang.Class({
|
||||
_connectToPermissionStore: function() {
|
||||
this._permStoreProxy = null;
|
||||
new PermissionStore(Gio.DBus.session,
|
||||
'org.freedesktop.XdgApp',
|
||||
'/org/freedesktop/XdgApp/PermissionStore',
|
||||
'org.freedesktop.impl.portal.PermissionStore',
|
||||
'/org/freedesktop/impl/portal/PermissionStore',
|
||||
Lang.bind(this, this._onPermStoreProxyReady));
|
||||
},
|
||||
|
||||
@ -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)
|
||||
@ -621,7 +635,7 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
_init: function(client, device, settings) {
|
||||
this.parent(client, device, settings);
|
||||
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
|
||||
this.item.menu.addMenuItem(createSettingsAction(_("Bluetooth Settings"), device));
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
@ -629,7 +643,7 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
},
|
||||
|
||||
getConnectLabel: function() {
|
||||
return _("Use as Internet connection");
|
||||
return _("Connect to Internet");
|
||||
},
|
||||
|
||||
getIndicatorIcon: function() {
|
||||
@ -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);
|
||||
@ -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));
|
||||
@ -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;
|
||||
}
|
||||
});
|
@ -6,6 +6,7 @@ const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
@ -47,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() {
|
||||
@ -57,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)
|
||||
@ -95,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({
|
||||
@ -110,6 +138,7 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._monitorManager = Meta.MonitorManager.get();
|
||||
this._haveShutdown = true;
|
||||
this._haveSuspend = true;
|
||||
|
||||
@ -155,6 +184,8 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._orientationSettings.connect('changed::orientation-lock',
|
||||
Lang.bind(this, this._updateOrientationLock));
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._updateOrientationLock));
|
||||
Gio.DBus.system.watch_name(SENSOR_BUS_NAME,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
Lang.bind(this, this._sensorProxyAppeared),
|
||||
@ -264,7 +295,8 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_updateOrientationLock: function() {
|
||||
if (this._sensorProxy)
|
||||
this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer;
|
||||
this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer &&
|
||||
this._monitorManager.get_is_builtin_display_on();
|
||||
else
|
||||
this._orientationLockAction.visible = false;
|
||||
|
||||
@ -301,14 +333,17 @@ const Indicator = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._updateSuspend();
|
||||
}));
|
||||
this._loginManager.canSuspend(Lang.bind(this,
|
||||
function(canSuspend, needsAuth) {
|
||||
this._haveSuspend = canSuspend;
|
||||
this._suspendNeedsAuth = needsAuth;
|
||||
this._updateSuspend();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspend: function() {
|
||||
let disabled = Main.sessionMode.isLocked ||
|
||||
let disabled = (Main.sessionMode.isLocked &&
|
||||
this._suspendNeedsAuth) ||
|
||||
(Main.sessionMode.isGreeter &&
|
||||
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
|
||||
this._suspendAction.visible = this._haveSuspend && !disabled;
|
||||
|
@ -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,6 +52,28 @@ 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,
|
||||
@ -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,7 @@ 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 SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2;
|
||||
@ -32,6 +33,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 +530,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 +554,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 +592,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 +658,6 @@ const AppSwitchAction = new Lang.Class({
|
||||
return true;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AppSwitchAction.prototype);
|
||||
|
||||
const ResizePopup = new Lang.Class({
|
||||
Name: 'ResizePopup',
|
||||
@ -683,6 +703,8 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
this._dimmedWindows = [];
|
||||
|
||||
this._skippedActors = [];
|
||||
|
||||
this._allowedKeybindings = {};
|
||||
|
||||
this._isWorkspacePrepended = false;
|
||||
@ -703,6 +725,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));
|
||||
@ -850,22 +873,34 @@ const WindowManager = new Lang.Class({
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-applications',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-group',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-applications-backward',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-group-backward',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-windows',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startWindowSwitcher));
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-windows-backward',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startWindowSwitcher));
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('cycle-windows',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('cycle-windows-backward',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('cycle-group',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('cycle-group-backward',
|
||||
Shell.ActionMode.NORMAL,
|
||||
Lang.bind(this, this._startSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-panels',
|
||||
Shell.ActionMode.NORMAL |
|
||||
Shell.ActionMode.OVERVIEW |
|
||||
@ -903,6 +938,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++)
|
||||
@ -932,7 +996,13 @@ const WindowManager = new Lang.Class({
|
||||
gesture = new AppSwitchAction();
|
||||
gesture.connect('activated', Lang.bind(this, this._switchApp));
|
||||
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) {
|
||||
@ -1023,6 +1093,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);
|
||||
@ -1049,9 +1123,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;
|
||||
},
|
||||
@ -1226,37 +1306,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);
|
||||
@ -1264,10 +1324,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,
|
||||
@ -1280,9 +1358,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;
|
||||
|
||||
@ -1308,6 +1394,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);
|
||||
@ -1315,31 +1410,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;
|
||||
},
|
||||
@ -1408,6 +1497,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());
|
||||
@ -1744,23 +1838,37 @@ const WindowManager = new Lang.Class({
|
||||
this._windowMenuManager.showWindowMenuForWindow(window, menu, rect);
|
||||
},
|
||||
|
||||
_startAppSwitcher : function(display, screen, window, binding) {
|
||||
_startSwitcher: function(display, screen, window, binding) {
|
||||
let constructor = null;
|
||||
switch (binding.get_name()) {
|
||||
case 'switch-applications':
|
||||
case 'switch-applications-backward':
|
||||
case 'switch-group':
|
||||
case 'switch-group-backward':
|
||||
constructor = AltTab.AppSwitcherPopup;
|
||||
break;
|
||||
case 'switch-windows':
|
||||
case 'switch-windows-backward':
|
||||
constructor = AltTab.WindowSwitcherPopup;
|
||||
break;
|
||||
case 'cycle-windows':
|
||||
case 'cycle-windows-backward':
|
||||
constructor = AltTab.WindowCyclerPopup;
|
||||
break;
|
||||
case 'cycle-group':
|
||||
case 'cycle-group-backward':
|
||||
constructor = AltTab.GroupCyclerPopup;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!constructor)
|
||||
return;
|
||||
|
||||
/* prevent a corner case where both popups show up at once */
|
||||
if (this._workspaceSwitcherPopup != null)
|
||||
this._workspaceSwitcherPopup.destroy();
|
||||
|
||||
let tabPopup = new AltTab.AppSwitcherPopup();
|
||||
|
||||
if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
|
||||
tabPopup.destroy();
|
||||
},
|
||||
|
||||
_startWindowSwitcher : function(display, screen, window, binding) {
|
||||
/* prevent a corner case where both popups show up at once */
|
||||
if (this._workspaceSwitcherPopup != null)
|
||||
this._workspaceSwitcherPopup.destroy();
|
||||
|
||||
let tabPopup = new AltTab.WindowSwitcherPopup();
|
||||
let tabPopup = new constructor();
|
||||
|
||||
if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
|
||||
tabPopup.destroy();
|
||||
|
@ -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,
|
||||
|
@ -45,11 +45,11 @@ const PrimaryActorLayout = new Lang.Class({
|
||||
this.primaryActor = primaryActor;
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(forHeight) {
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
return this.primaryActor.get_preferred_width(forHeight);
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(forWidth) {
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
return this.primaryActor.get_preferred_height(forWidth);
|
||||
},
|
||||
});
|
||||
|
@ -103,7 +103,7 @@ const WorkspacesView = new Lang.Class({
|
||||
page_increment: 1,
|
||||
page_size: 1,
|
||||
step_increment: 0,
|
||||
upper: 0 });
|
||||
upper: global.screen.n_workspaces });
|
||||
this.scrollAdjustment.connect('notify::value',
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
@ -374,6 +374,10 @@ const ExtraWorkspaceView = new Lang.Class({
|
||||
this._workspace.setActualGeometry(this._actualGeometry);
|
||||
},
|
||||
|
||||
getActiveWorkspace: function() {
|
||||
return this._workspace;
|
||||
},
|
||||
|
||||
animateToOverview: function(animationType) {
|
||||
if (animationType == AnimationType.ZOOM)
|
||||
this._workspace.zoomToOverview();
|
||||
@ -421,8 +425,10 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
// Only switch to the workspace when there's no application
|
||||
// windows open. The problem is that it's too easy to miss
|
||||
// an app window and get the wrong one focused.
|
||||
let event = Clutter.get_current_event();
|
||||
let index = this._getMonitorIndexForEvent(event);
|
||||
if ((action.get_button() == 1 || action.get_button() == 0) &&
|
||||
this._getPrimaryView().getActiveWorkspace().isEmpty())
|
||||
this._workspacesViews[index].getActiveWorkspace().isEmpty())
|
||||
Main.overview.hide();
|
||||
}));
|
||||
Main.overview.addAction(clickAction);
|
||||
@ -431,11 +437,18 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let panAction = new Clutter.PanAction({ threshold_trigger_edge: Clutter.GestureTriggerEdge.AFTER });
|
||||
panAction.connect('pan', Lang.bind(this, this._onPan));
|
||||
panAction.connect('gesture-begin', Lang.bind(this, function() {
|
||||
if (this._workspacesOnlyOnPrimary) {
|
||||
let event = Clutter.get_current_event();
|
||||
if (this._getMonitorIndexForEvent(event) != this._primaryIndex)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].startSwipeScroll();
|
||||
return true;
|
||||
}));
|
||||
panAction.connect('gesture-cancel', Lang.bind(this, function() {
|
||||
clickAction.release();
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].endSwipeScroll();
|
||||
}));
|
||||
@ -581,6 +594,12 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_getMonitorIndexForEvent: function(event) {
|
||||
let [x, y] = event.get_coords();
|
||||
let rect = new Meta.Rectangle({ x: x, y: y, width: 1, height: 1 });
|
||||
return global.screen.get_monitor_index_for_rect(rect);
|
||||
},
|
||||
|
||||
_getPrimaryView: function() {
|
||||
if (!this._workspacesViews.length)
|
||||
return null;
|
||||
@ -661,6 +680,11 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
_onScrollEvent: function(actor, event) {
|
||||
if (!this.actor.mapped)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._workspacesOnlyOnPrimary &&
|
||||
this._getMonitorIndexForEvent(event) != this._primaryIndex)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let activeWs = global.screen.get_active_workspace();
|
||||
let ws;
|
||||
switch (event.get_scroll_direction()) {
|
||||
|
@ -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
|
||||
@ -61,7 +62,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"
|
||||
|
2062
po/en_GB.po
2062
po/en_GB.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user