Compare commits
	
		
			2 Commits
		
	
	
		
			gnome-3-24
			...
			wip/loc-ap
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					11db28b216 | ||
| 
						 | 
					614f37b116 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -16,6 +16,7 @@ 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
 | 
			
		||||
@@ -25,6 +26,8 @@ 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
 | 
			
		||||
@@ -71,6 +74,7 @@ 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										417
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										417
									
								
								NEWS
									
									
									
									
									
								
							@@ -1,420 +1,3 @@
 | 
			
		||||
3.24.3
 | 
			
		||||
======
 | 
			
		||||
* Bypass proxies for captive portal [Bastien; #769692]
 | 
			
		||||
* Fix missing icons in freedesktop notifications [Florian; #784245]
 | 
			
		||||
* Fix blocked clicks in shutdown dialog [Florian; #781738]
 | 
			
		||||
* Implement tablet rings/strips configuration [Carlos; #782033]
 | 
			
		||||
* Misc. bug fixes [Matthias, Jeremy, Bastien, Florian; #780215, #782802,
 | 
			
		||||
  #783286, #784130, #784353, #781471]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jeremy Bicha, Carlos Garnacho, Matthias Liertzer, Florian Müllner,
 | 
			
		||||
  Bastien Nocera
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Christian Stadelmann [de], Марко Костић [sr], Милош Поповић [sr@latin],
 | 
			
		||||
  Furkan Ahmet Kara [tr], Jeremy Bicha [es, he]
 | 
			
		||||
 | 
			
		||||
3.24.2
 | 
			
		||||
======
 | 
			
		||||
* Only fetch weather information when there's a valid location [Rares; #780404]
 | 
			
		||||
* Handle extension errors during reload due to settings change [Emilio; #781728]
 | 
			
		||||
* Fix StEntry::primary-icon-clicked emission [Florian; #782190]
 | 
			
		||||
* Allow search providers to provide clipboard text for results [Daiki; #775099]
 | 
			
		||||
* Misc. bug fixes [Florian; #781545]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Florian Müllner, Emilio Pozuelo Monfort, Daiki Ueno, Rares Visalom
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Milo Casagrande [it], Милош Поповић [sr], Khaled Hosny [ar]
 | 
			
		||||
 | 
			
		||||
3.24.1
 | 
			
		||||
======
 | 
			
		||||
* Close Wifi selection dialog on lock [Florian; #780054]
 | 
			
		||||
* Fix DND over window previews in overview [Florian; #737166]
 | 
			
		||||
* Do not lock the screen when disabled by lockdown settings [Florian; #780212]
 | 
			
		||||
* Follow GNOME Weather's location permissions [Florian; #780252]
 | 
			
		||||
* Fix portals that require a new window to be loaded [Catalin; #759044]
 | 
			
		||||
* Fix restricting menus to screen height on HiDPI displays [Cosimo; #753305]
 | 
			
		||||
* Misc. bug fixes and cleanups [Florian, Cosimo, Bastien, Catalin, Carlos;
 | 
			
		||||
  #780063, #780321, #780381, #780453, #758873, #780606, #642652]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Cosimo Cecchi, Carlos Garnacho, Catalin Iacob, Florian Müllner, Bastien Nocera
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Marek Cernocky [cs], Piotr Drąg [pl], Anders Jonsson [sv], Stas Solovey [ru],
 | 
			
		||||
  Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Daniel Korostil [uk],
 | 
			
		||||
  Kukuh Syafaat [id], Milo Casagrande [it], Jiri Grönroos [fi],
 | 
			
		||||
  Daniel Mustieles [es], Balázs Úr [hu], Guillaume Bernard [fr],
 | 
			
		||||
  Changwoo Ryu [ko], Mario Blättermann [de], Fran Dieguez [gl],
 | 
			
		||||
  Dušan Kazik [sk], Yuras Shumovich [be], Fabio Tomat [fur],
 | 
			
		||||
  Kjartan Maraas [nb], Aurimas Černius [lt], Trần Ngọc Quân [vi],
 | 
			
		||||
  Rūdolfs Mazurs [lv], Ask Hjorth Larsen [da], Tom Tryfonidis [el], gogo [hr]
 | 
			
		||||
 | 
			
		||||
3.24.0
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  GNOME Translation Robot [tg], Мирослав Николић [sr, sr@latin],
 | 
			
		||||
  Guillaume Bernard [fr], Rūdolfs Mazurs [lv], Emin Tufan Çetin [tr],
 | 
			
		||||
  sujiniku [ja], Daniel Korostil [uk]
 | 
			
		||||
 | 
			
		||||
3.23.92
 | 
			
		||||
=======
 | 
			
		||||
* Implement DND to overview on wayland [Hyungwon; #765003]
 | 
			
		||||
* Make telepathy optional at runtime [Florian; #771721, #779878]
 | 
			
		||||
* Don't show forecasts for NYC when geoclue gets stuck [Sebastian; #779898]
 | 
			
		||||
* Add bottom edge drag gesture to bring up the OSK [Jan-Michael; #757712]
 | 
			
		||||
* Allow switching between pads in the same group [Carlos; #779986]
 | 
			
		||||
* Ignore showBanners policy for critical notifications [Florian; #779974]
 | 
			
		||||
* Misc. bug fixes [Florian; #779435, #779819, #779820]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jan-Michael Brummer, Allan Day, Carlos Garnacho, Hyungwon Hwang,
 | 
			
		||||
  Sebastian Keller, Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Enrico Nicoletto [pt_BR], Jiri Grönroos [fi], Chao-Hsiung Liao [zh_TW],
 | 
			
		||||
  Piotr Drąg [pl], Piotr Drąg [he], Balázs Meskó [hu], Kris Thomsen [da],
 | 
			
		||||
  Yuras Shumovich [be], Sveinn í Felli [is], Inaki Larranaga Murgoitio [eu],
 | 
			
		||||
  Changwoo Ryu [ko], Jordi Mas [ca], Aurimas Černius [lt],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Christian Kirbach [de], Anders Jonsson [sv],
 | 
			
		||||
  Fabio Tomat [fur], GNOME Translation Robot [gd], Dušan Kazik [sk],
 | 
			
		||||
  Kukuh Syafaat [id], Marek Černocký [cs], Stas Solovey [ru],
 | 
			
		||||
  Milo Casagrande [it], Fran Dieguez [gl], Daniel Boles [gl], A S Alam [pa],
 | 
			
		||||
  Daniel Mustieles [es]
 | 
			
		||||
 | 
			
		||||
3.23.91
 | 
			
		||||
=======
 | 
			
		||||
* Use the original timestamps for restored notifications [Florian; #766410]
 | 
			
		||||
* Add weather information to date+time drop-down [Florian; #754031]
 | 
			
		||||
* Refine message list layout in date+time drop-down [Florian; #775763]
 | 
			
		||||
* Make next/prev media controls insensitive when unavailable [Florian; #773884]
 | 
			
		||||
* Misc. bug fixes [Piotr, Bastien, Florian; #772210, #769546, #775799]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Piotr Drąg, Carlos Garnacho, Florian Müllner, Bastien Nocera
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Jordi Mas [ca], Ask Hjorth Larsen [da],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Daniel Mustieles [es], Dušan Kazik [sk],
 | 
			
		||||
  Aurimas Černius [lt], Jiri Grönroos [fi], Kjartan Maraas [nb],
 | 
			
		||||
  Piotr Drąg [pl], Daniel Korostil [uk], Kukuh Syafaat [id],
 | 
			
		||||
  Milo Casagrande [it], Fabio Tomat [fur], Rafael Fontenelle [pt_BR],
 | 
			
		||||
  Fran Dieguez [gl], Мирослав Николић [sr, sr@latin], Balázs Meskó [hu],
 | 
			
		||||
  Chao-Hsiung Liao [zh_TW]
 | 
			
		||||
 | 
			
		||||
3.23.90
 | 
			
		||||
=======
 | 
			
		||||
* Handle Ctrl+Q and Ctrl+W in portal window [Bastien; #764133]
 | 
			
		||||
* Allow to scroll through ibus candidates with mouse [Peng; #776032]
 | 
			
		||||
* Reload apps on .desktop file content changes [Adrian; #773636]
 | 
			
		||||
* Use private data/cache directories in portal helper [Bastien; #775639]
 | 
			
		||||
* Fix subsurfaces not showing up in previews [Rui; #756715]
 | 
			
		||||
* Fix theme node transitions [Florian; #778145]
 | 
			
		||||
* Update pad (o)leds on mode switches [Carlos; #776543]
 | 
			
		||||
* Add security indicators to defend against malicious portals [Bastien; #749197]
 | 
			
		||||
* Don't allow type ahead at the login screen [Ray; #766139]
 | 
			
		||||
* Don't fail to load because of TLS errors [Bastien; #778253]
 | 
			
		||||
* Ensure the network lists remains sorted on rename [Benjamin; #778686]
 | 
			
		||||
* Toggle power-off/suspend button on long-press [Florian; #721173]
 | 
			
		||||
* Add "kill-switch" for user extensions [Florian; #778664]
 | 
			
		||||
* Add night light indicator to status area [Florian; #741224]
 | 
			
		||||
* Misc. bug fixes [Michael, Bastien, Carlos, Rui, Florian, Alan, Philip, Jonas;
 | 
			
		||||
  #759793, #735233, #762444, #777784, #777934, #778158, #776199, #778425,
 | 
			
		||||
  #771098, #778552, #777317, #778660, #778661, #745626, #778672]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Philip Chimento,
 | 
			
		||||
  Alan Coopersmith, Piotr Drąg, Carlos Garnacho, Yuri Konotopov,
 | 
			
		||||
  Lionel Landwerlin, Rui Matos, Florian Müllner, Bastien Nocera,
 | 
			
		||||
  Adrian Perez de Castro, Robert Roth, Ray Strode, Peng Wu
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Jiri Grönroos [fi], Balázs Meskó [hu], Gábor Kelemen [hu],
 | 
			
		||||
  Daniel Mustieles [es], Dušan Kazik [sk],
 | 
			
		||||
  Piotr Drąg [ar, eu, fa, hr, pa, pt, sr, sr@latin], Rafael Fontenelle [pt_BR],
 | 
			
		||||
  Jordi Mas [ca], Piotr Drąg [pl], Alexandre Franke [fr],
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Yuras Shumovich [be], Mandy Wang [zh_CN],
 | 
			
		||||
  Marek Černocký [cs], Kukuh Syafaat [id], Kjartan Maraas [nb],
 | 
			
		||||
  Daniel Korostil [uk]
 | 
			
		||||
 | 
			
		||||
3.23.3
 | 
			
		||||
======
 | 
			
		||||
* Fix replacing of GNotifications [Florian; #775149]
 | 
			
		||||
* Prepare for mozjs31 GJS [Philip; #775374]
 | 
			
		||||
* Misc. bug fixes [Niels, Jonas; #775507, #776130]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jonas Ådahl, Michael Catanzaro, Philip Chimento, Niels De Graef,
 | 
			
		||||
  Carlos Garnacho, Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Muhammet Kara [tr], Christian Kirbach [de], Baurzhan Muftakhidinov [kk],
 | 
			
		||||
  Cheng-Chia Tseng [zh_TW], A S Alam [pa], Gianvito Cavasoli [it]
 | 
			
		||||
 | 
			
		||||
3.23.2
 | 
			
		||||
======
 | 
			
		||||
* Implement Pad configuration OSD [Carlos; #771067]
 | 
			
		||||
* Show overview on three-finger touchpad pinch [Carlos; #765937]
 | 
			
		||||
* Summarize network sections with too many devices [Florian; #773892]
 | 
			
		||||
* Always show primary network icon when connected [Florian; #773890]
 | 
			
		||||
* Fix fullscreen transitions on wayland [Rui; #770345]
 | 
			
		||||
* Work around portal failures by using a URL without HTPPS redirect [Debarshi; #769940]
 | 
			
		||||
* Fix app view hiding when no usage data is available [Florian, Xiaoguang; #774381]
 | 
			
		||||
* Misc. bug fixes [Florian, Ray; #773875, #740043, #773893, #774643, #774805]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Carlos Garnacho, Rui Matos, Florian Müllner, Debarshi Ray, Ray Strode,
 | 
			
		||||
  Xiaoguang Wang
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Balázs Meskó [hu], Fabio Tomat [fur], Marek Cernocky [cs], Stas Solovey [ru],
 | 
			
		||||
  Daniel Mustieles [es], Marek Černocký [cs], Piotr Drąg [pl],
 | 
			
		||||
  Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Jiri Grönroos [fi],
 | 
			
		||||
  Kjartan Maraas [nb]
 | 
			
		||||
 | 
			
		||||
3.23.1
 | 
			
		||||
======
 | 
			
		||||
* Request periodic scans while WiFi list is open [Dan; #767918]
 | 
			
		||||
* Include extension UUID in structured log metadata [Jonh; #770717]
 | 
			
		||||
* Line-wrap PAM messages on login screen [Tao; #764445]
 | 
			
		||||
* Add a way to launch an app on the discrete GPU [Bastien; #773117]
 | 
			
		||||
* Only allow graphs to lift screen shield when locked [Florian; #773328]
 | 
			
		||||
* Add reload option to gnome-shell-extension-tool [Jonh; #772593]
 | 
			
		||||
* Update background animations when resuming from suspend [Florian; #773265]
 | 
			
		||||
* Misc. bug fixes [Cosimo, Bastien, Florian, Philip, Carlos; #772723, #772287,
 | 
			
		||||
  #756432, #772386, #772386, #773085, #773634]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Cosimo Cecchi, Philip Chimento, Carlos Garcia Campos, Florian Müllner,
 | 
			
		||||
  Bastien Nocera, Jonh Wendell, Dan Williams, Tao Yang
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Fabio Tomat [fur], Philip Chimento [zh_CN], YunQiang Su [zh_CN],
 | 
			
		||||
  Jordi Mas [ca], Piotr Drąg [pl], Muhammet Kara [tr], Marek Černocký [cs],
 | 
			
		||||
  Daniel Korostil [uk], Dušan Kazik [sk]
 | 
			
		||||
 | 
			
		||||
3.22.1
 | 
			
		||||
======
 | 
			
		||||
* Fix hidden network indicator on startup [Florian; #772249]
 | 
			
		||||
* Fix order of windows with modal dialogs in window switcher [Florian; #747153]
 | 
			
		||||
* Fix feedback loop between StClipboard and X11 bridge [Carlos; #760745]
 | 
			
		||||
* Reliably match windows from Flatpak apps [Florian; #772615]
 | 
			
		||||
* Misc. bug fixes [Philip; #742249]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Philip Chimento, Carlos Garnacho, Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Khaled Hosny [ar], BM [uz@cyrillic],
 | 
			
		||||
  Milo Casagrande [it], Cheng-Chia Tseng [zh_TW], gogo [hr]
 | 
			
		||||
 | 
			
		||||
3.22.0
 | 
			
		||||
======
 | 
			
		||||
* Misc. bug fixes [Florian, Rui; #771391, #771536] #771656]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Rui Matos, Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Ask Hjorth Larsen [da], GNOME Translation Robot [gd], Alexandre Franke [fr],
 | 
			
		||||
  Daniel Korostil [uk], Jordi Mas [ca], Khaled Hosny [ar], David King [en_GB]
 | 
			
		||||
 | 
			
		||||
3.21.92
 | 
			
		||||
=======
 | 
			
		||||
* Adjust screen capture to work with multiple stage views [Jonas; #770128]
 | 
			
		||||
* Improve handling of cycle shortcuts [Florian; #771063]
 | 
			
		||||
* Fix windows not getting undimmed in some cases [Rui; #770163, #752524]
 | 
			
		||||
* Disable extension version check by default [Florian; #770887]
 | 
			
		||||
* Misc. bug fixes [Rui, Florian, Michael; #770382, #770888, #770328]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jonas Ådahl, Michael Catanzaro, Fran Dieguez, Olivier Fourdan, Rui Matos,
 | 
			
		||||
  Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Aurimas Černius [lt],
 | 
			
		||||
  Muhammet Kara [tr], Trần Ngọc Quân [vi], A S Alam [pa], Yosef Or Boczko [he],
 | 
			
		||||
  Anders Jonsson [sv], Tiago Santos [pt], Hannie Dumoleyn [nl],
 | 
			
		||||
  Rūdolfs Mazurs [lv], Claude Paroz [fr], Arash Mousavi [fa],
 | 
			
		||||
  Fran Dieguez [gl], Stas Solovey [ru], Tom Tryfonidis [el]
 | 
			
		||||
 | 
			
		||||
3.21.91
 | 
			
		||||
=======
 | 
			
		||||
Translations:
 | 
			
		||||
  Mario Blättermann [de], Jiri Grönroos [fi], Dušan Kazik [sk],
 | 
			
		||||
  Andika Triwidada [id], Daniel Mustieles [es], Fabio Tomat [fur],
 | 
			
		||||
  Enrico Nicoletto [pt_BR], Matej Urbančič [sl], Мирослав Николић [sr, sr@latin]
 | 
			
		||||
 | 
			
		||||
3.21.90.1
 | 
			
		||||
=========
 | 
			
		||||
Contributors:
 | 
			
		||||
  Piotr Drąg
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Marek Černocký [cs], Balázs Úr [hu]
 | 
			
		||||
 | 
			
		||||
3.21.90
 | 
			
		||||
=======
 | 
			
		||||
* Improve on-screen keyboard on wayland [Carlos; #765009]
 | 
			
		||||
* Misc. bug fixes [Florian; #769156, #769216, #769074]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Carlos Garnacho, Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Fabio Tomat [fur], Tiago Santos [pt], Daniel Mustieles [es],
 | 
			
		||||
  Bernd Homuth [de], Aurimas Černius [lt], Balázs Úr [hu],
 | 
			
		||||
  Yosef Or Boczko [he], Jiri Grönroos [fi], Marek Cernocky [cs],
 | 
			
		||||
  Muhammet Kara [tr], Enrico Nicoletto [pt_BR], Andika Triwidada [id]
 | 
			
		||||
 | 
			
		||||
3.21.4
 | 
			
		||||
======
 | 
			
		||||
* overview: Fix switching workspaces when scrolling on non-primary monitors
 | 
			
		||||
  [Florian; #766883, #768316]
 | 
			
		||||
* Fix crash when using screen recorder under wayland [Rui; #767001]
 | 
			
		||||
* Update theme on video memory purge errors [Rui; #739178]
 | 
			
		||||
* Free old backgrounds immediately [Hyungwon; #766353]
 | 
			
		||||
* Add support for system upgrades to end session dialog [Kalev; #763611]
 | 
			
		||||
* Fix maximized windows flickering to the wrong size on restart [Owen; #761566]
 | 
			
		||||
* Hide ignored events in calendar as well [Florian; #768538]
 | 
			
		||||
* calendar: Only hide dismissed occurrence of recurring event [Florian; #748226]
 | 
			
		||||
* Provide org.freedesktop.impl.portal.access implementation [Florian; #768669]
 | 
			
		||||
* Misc. bug fixes and cleanups [Rui, Florian, Marinus, Jonas; #767954, #768317,
 | 
			
		||||
  #746867, #762206, #768956, #768979]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jonas Ådahl, Piotr Drąg, Hyungwon Hwang, Kalev Lember, Rui Matos,
 | 
			
		||||
  Florian Müllner, Marinus Schraal, Owen W. Taylor
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Andika Triwidada [id], Daniel Mustieles [es], Bruce Cowan [en_GB],
 | 
			
		||||
  Dušan Kazik [sk], Piotr Drąg [pl], Chao-Hsiung Liao [zh_HK]
 | 
			
		||||
 | 
			
		||||
3.21.3
 | 
			
		||||
======
 | 
			
		||||
 * Do not disable suspend action when locked [Florian; #725960]
 | 
			
		||||
 * 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
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Milo Casagrande [it],
 | 
			
		||||
  Anders Jonsson [sv], Muhammet Kara [tr], Alexandre Franke [fr],
 | 
			
		||||
  Rūdolfs Mazurs [lv], Ask Hjorth Larsen [da], Jiro Matsuzawa [ja]
 | 
			
		||||
 | 
			
		||||
3.19.92
 | 
			
		||||
=======
 | 
			
		||||
* Update location dialog according to latest mockups [Zeeshan; #762480]
 | 
			
		||||
* Fix deleting chat notifications in calendar [Florian; #747991]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Rūdolfs Mazurs [lv], Changwoo Ryu [ko], Matej Urbančič [sl],
 | 
			
		||||
  Justin van Steijn [nl], Fabio Tomat [fur], Kris Thomsen [da],
 | 
			
		||||
  Marek Černocký [cs], Piotr Drąg [pl], Dušan Kazik [sk],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Balázs Úr [hu], Yosef Or Boczko [he],
 | 
			
		||||
  Daniel Mustieles [es], Fran Dieguez [gl], Bernd Homuth [de],
 | 
			
		||||
  Tom Tryfonidis [el], Jiri Grönroos [fi], Gil Forcada [ca],
 | 
			
		||||
  Artur Morais [pt_BR], Aurimas Černius [lt], Stas Solovey [ru]
 | 
			
		||||
 | 
			
		||||
3.19.91
 | 
			
		||||
=======
 | 
			
		||||
* location: Ask user only once [Zeeshan; #762559]
 | 
			
		||||
* Fix jiggling when auto-hiding legacy tray [Florian; #747957]
 | 
			
		||||
* Misc. bug fixes [Florian, Michael, Ting-Wei; #762475, #762507, #755659]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Michael Catanzaro, Ting-Wei Lan, Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Piotr Drąg [pl], A S Alam [pa],
 | 
			
		||||
  Artur de Aquino Morais [pt_BR], Daniel Mustieles [es],
 | 
			
		||||
  Chao-Hsiung Liao [zh_TW], Daniel Korostil [uk], Fran Dieguez [gl],
 | 
			
		||||
  Tom Tryfonidis [el], Bernd Homuth [de], Sebastian Rasmussen [sv],
 | 
			
		||||
  Jordi Mas [ca], Piotr Drąg [ga], Cédric Valmary [oc], Gábor Kelemen [hu],
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Friedel Wolff [af], Marek Černocký [cs],
 | 
			
		||||
  Mingye Wang (Arthur2e5) [zh_CN], Aron Xu [zh_CN], Khaled Hosny [ar],
 | 
			
		||||
  Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he]
 | 
			
		||||
 | 
			
		||||
3.19.90
 | 
			
		||||
=======
 | 
			
		||||
* Correctly identify VPN secret requests [Lubomir; #760999]
 | 
			
		||||
* Improve week number presentation [Jakub; #683245]
 | 
			
		||||
* Add audio device selection dialog [Florian; #760284]
 | 
			
		||||
* Add media controls to the time and date drop down [Florian; #756491]
 | 
			
		||||
* Fix IBus candidate popup position under wayland [Rui; #753476]
 | 
			
		||||
* Ask user to grant applications access to location [Zeeshan; #762119]
 | 
			
		||||
* Misc. bug fixes [Mario, Jakub, Florian; #761208, #761772, #762270]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Michael Catanzaro, Rui Matos, Florian Müllner,
 | 
			
		||||
  Lubomir Rintel, Mario Sanchez Prada, Jakub Steiner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Alexander Shopov [bg], Balázs Meskó [hu], Fabio Tomat [fur],
 | 
			
		||||
  Dušan Kazik [sk], Piotr Drąg [pl], Alexandre Franke [fr],
 | 
			
		||||
  Mario Blättermann [de], Milo Casagrande [it], Jordi Mas [ca]
 | 
			
		||||
 | 
			
		||||
3.19.4
 | 
			
		||||
======
 | 
			
		||||
* gdm: Do not allow bypassing disabled Sign In button [Michael; #746180]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								autogen.sh
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								autogen.sh
									
									
									
									
									
								
							@@ -4,12 +4,8 @@
 | 
			
		||||
srcdir=`dirname $0`
 | 
			
		||||
test -z "$srcdir" && srcdir=.
 | 
			
		||||
 | 
			
		||||
olddir="$(pwd)"
 | 
			
		||||
 | 
			
		||||
cd "${srcdir}"
 | 
			
		||||
 | 
			
		||||
(test -f configure.ac \
 | 
			
		||||
  && test -d src) || {
 | 
			
		||||
(test -f $srcdir/configure.ac \
 | 
			
		||||
  && test -d $srcdir/src) || {
 | 
			
		||||
    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
 | 
			
		||||
    echo " top-level gnome-shell directory"
 | 
			
		||||
    exit 1
 | 
			
		||||
@@ -23,13 +19,9 @@ then
 | 
			
		||||
fi
 | 
			
		||||
git submodule update
 | 
			
		||||
 | 
			
		||||
aclocal --install || exit 1
 | 
			
		||||
gtkdocize --copy || exit 1
 | 
			
		||||
intltoolize --force --copy --automake || exit 1
 | 
			
		||||
autoreconf --verbose --force --install || exit 1
 | 
			
		||||
 | 
			
		||||
cd "${olddir}"
 | 
			
		||||
 | 
			
		||||
if [ "$NOCONFIGURE" = "" ]; then
 | 
			
		||||
    "${srcdir}/configure" "$@" || exit 1
 | 
			
		||||
fi
 | 
			
		||||
which gnome-autogen.sh || {
 | 
			
		||||
    echo "You need to install gnome-common from GNOME Git (or from"
 | 
			
		||||
    echo "your OS vendor's package manager)."
 | 
			
		||||
    exit 1
 | 
			
		||||
}
 | 
			
		||||
. gnome-autogen.sh
 | 
			
		||||
 
 | 
			
		||||
@@ -1029,7 +1029,6 @@ NPP_GetValue(NPP          instance,
 | 
			
		||||
    if (!instance->pdata)
 | 
			
		||||
      return NPERR_INVALID_INSTANCE_ERROR;
 | 
			
		||||
 | 
			
		||||
    funcs.retainobject (instance->pdata);
 | 
			
		||||
    *(NPObject**)value = instance->pdata;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								configure.ac
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.24.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[3.19.4],[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,15 +41,6 @@ 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
 | 
			
		||||
@@ -61,7 +52,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 $LIBMUTTER_CLUTTER)
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
 | 
			
		||||
else
 | 
			
		||||
   AC_MSG_RESULT(no)
 | 
			
		||||
fi
 | 
			
		||||
@@ -83,13 +74,15 @@ AS_IF([test x$enable_systemd != xno], [
 | 
			
		||||
 | 
			
		||||
AC_MSG_RESULT($enable_systemd)
 | 
			
		||||
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=1.49.1
 | 
			
		||||
GJS_MIN_VERSION=1.47.0
 | 
			
		||||
MUTTER_MIN_VERSION=3.24.0
 | 
			
		||||
CLUTTER_MIN_VERSION=1.21.5
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=1.45.4
 | 
			
		||||
GJS_MIN_VERSION=1.39.0
 | 
			
		||||
MUTTER_MIN_VERSION=3.19.4
 | 
			
		||||
GTK_MIN_VERSION=3.15.0
 | 
			
		||||
GIO_MIN_VERSION=2.45.3
 | 
			
		||||
LIBECAL_MIN_VERSION=3.5.3
 | 
			
		||||
LIBEDATASERVER_MIN_VERSION=3.17.2
 | 
			
		||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
 | 
			
		||||
POLKIT_MIN_VERSION=0.100
 | 
			
		||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
 | 
			
		||||
GCR_MIN_VERSION=3.7.5
 | 
			
		||||
@@ -102,14 +95,15 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
            libxml-2.0
 | 
			
		||||
            gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
            atk-bridge-2.0
 | 
			
		||||
            gjs-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
            gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
            $recorder_modules
 | 
			
		||||
            gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
            $LIBMUTTER_CLUTTER >= $MUTTER_MIN_VERSION
 | 
			
		||||
            $LIBMUTTER_COGL_PANGO
 | 
			
		||||
            clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
            clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
            libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
 | 
			
		||||
            gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
 | 
			
		||||
            libcanberra libcanberra-gtk3
 | 
			
		||||
            telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
 | 
			
		||||
            polkit-agent-1 >= $POLKIT_MIN_VERSION
 | 
			
		||||
            gcr-base-3 >= $GCR_MIN_VERSION"
 | 
			
		||||
if test x$have_systemd = xyes; then
 | 
			
		||||
@@ -117,17 +111,15 @@ 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-1.0 >= $GJS_MIN_VERSION)
 | 
			
		||||
PKG_CHECK_MODULES(ST, $LIBMUTTER_CLUTTER gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
 | 
			
		||||
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(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, $LIBMUTTER_CLUTTER gtk+-3.0)
 | 
			
		||||
PKG_CHECK_MODULES(TRAY, clutter-1.0 gtk+-3.0)
 | 
			
		||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
 | 
			
		||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.21.3)
 | 
			
		||||
 | 
			
		||||
AC_SUBST(LIBMUTTER_API_VERSION)
 | 
			
		||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.19.2)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(browser-plugin,
 | 
			
		||||
              [AS_HELP_STRING([--enable-browser-plugin],
 | 
			
		||||
@@ -153,10 +145,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`
 | 
			
		||||
@@ -262,6 +254,7 @@ AC_CONFIG_FILES([
 | 
			
		||||
  docs/reference/st/Makefile
 | 
			
		||||
  docs/reference/st/st-docs.sgml
 | 
			
		||||
  js/Makefile
 | 
			
		||||
  src/calendar-server/evolution-calendar.desktop.in
 | 
			
		||||
  src/Makefile
 | 
			
		||||
  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,16 +7,12 @@ 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.desktop.in \
 | 
			
		||||
	org.gnome.Shell.PortalHelper.service \
 | 
			
		||||
	org.gnome.Shell.PortalHelper.desktop \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
@@ -32,13 +28,10 @@ endif
 | 
			
		||||
	    -e "s|@VERSION[@]|$(VERSION)|" \
 | 
			
		||||
	    $< > $@ || rm $@
 | 
			
		||||
 | 
			
		||||
%.desktop:%.desktop.in
 | 
			
		||||
	$(AM_V_GEN) $(MSGFMT) --desktop --template $(builddir)/$< \
 | 
			
		||||
	                      -d $(top_srcdir)/po -o $@
 | 
			
		||||
@INTLTOOL_DESKTOP_RULE@
 | 
			
		||||
 | 
			
		||||
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	\
 | 
			
		||||
@@ -64,7 +57,6 @@ 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)
 | 
			
		||||
 | 
			
		||||
@@ -88,11 +80,14 @@ perf-background.xml: perf-background.xml.in
 | 
			
		||||
	    $< > $@ || rm $@
 | 
			
		||||
 | 
			
		||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
 | 
			
		||||
keys_DATA = 50-gnome-shell-system.xml
 | 
			
		||||
keys_in_files = 50-gnome-shell-system.xml.in
 | 
			
		||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
 | 
			
		||||
 | 
			
		||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
 | 
			
		||||
 | 
			
		||||
%.gschema.xml: %.gschema.xml.in Makefile
 | 
			
		||||
@INTLTOOL_XML_NOMERGE_RULE@
 | 
			
		||||
 | 
			
		||||
%.gschema.xml.in: %.gschema.xml.in.in Makefile
 | 
			
		||||
	$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
 | 
			
		||||
	$< > $@ || rm $@
 | 
			
		||||
 | 
			
		||||
@@ -111,16 +106,15 @@ 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_DATA)					\
 | 
			
		||||
	$(keys_in_files)				\
 | 
			
		||||
	$(dist_theme_files)				\
 | 
			
		||||
	perf-background.xml.in				\
 | 
			
		||||
	org.gnome.Shell.PortalHelper.desktop.in.in	\
 | 
			
		||||
	org.gnome.Shell.PortalHelper.desktop.in		\
 | 
			
		||||
	org.gnome.Shell.PortalHelper.service.in		\
 | 
			
		||||
	org.gnome.shell.gschema.xml.in			\
 | 
			
		||||
	org.gnome.shell.gschema.xml.in.in		\
 | 
			
		||||
	gnome-shell-theme.gresource.xml 		\
 | 
			
		||||
	$(resource_files)				\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -129,9 +123,11 @@ 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=Shell Extensions
 | 
			
		||||
Comment=Configure GNOME Shell Extensions
 | 
			
		||||
_Name=GNOME Shell Extension Preferences
 | 
			
		||||
_Comment=Configure GNOME Shell Extensions
 | 
			
		||||
Exec=@bindir@/gnome-shell-extension-prefs %u
 | 
			
		||||
X-GNOME-Bugzilla-Bugzilla=GNOME
 | 
			
		||||
X-GNOME-Bugzilla-Product=gnome-shell
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
[portal]
 | 
			
		||||
DBusName=org.freedesktop.impl.portal.desktop.gnome
 | 
			
		||||
Interfaces=org.freedesktop.impl.portal.Access
 | 
			
		||||
UseIn=gnome
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
<!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,10 +1,9 @@
 | 
			
		||||
[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,139 +3,143 @@
 | 
			
		||||
          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>
 | 
			
		||||
    </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>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="disable-extension-version-validation" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <summary>Disables the validation of extension version compatibility</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Disables the validation of extension version compatibility</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        GNOME Shell will only load extensions that claim to support the current
 | 
			
		||||
        running version. Enabling this option will disable this check and try to
 | 
			
		||||
        load all extensions regardless of the versions they claim to support.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="favorite-apps" type="as">
 | 
			
		||||
      <default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
 | 
			
		||||
      <summary>List of desktop file IDs for favorite applications</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>List of desktop file IDs for favorite applications</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The applications corresponding to these identifiers
 | 
			
		||||
        will be displayed in the favorites area.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="app-picker-view" type="u">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <summary>App Picker View</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>App Picker View</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Index of the currently selected view in the application picker.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="command-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <summary>History for command (Alt-F2) dialog</summary>
 | 
			
		||||
      <_summary>History for command (Alt-F2) dialog</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="looking-glass-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <!-- Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass -->
 | 
			
		||||
      <summary>History for the looking glass dialog</summary>
 | 
			
		||||
      <!-- Translators: looking glass is a debugger and inspector tool, see https://live.gnome.org/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>
 | 
			
		||||
        This key overrides the automatic hiding of the “Log out”
 | 
			
		||||
      <_summary>Always show the 'Log out' menu item in the user menu.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the automatic hiding of the 'Log out'
 | 
			
		||||
        menu item in single-user, single-session situations.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="remember-mount-password" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <summary>Whether to remember password for mounting encrypted or remote filesystems</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The shell will request a password when an encrypted device or a
 | 
			
		||||
        remote filesystem is mounted.  If the password can be saved for
 | 
			
		||||
        future use a “Remember Password” checkbox will be present.
 | 
			
		||||
        future use a 'Remember Password' checkbox will be present.
 | 
			
		||||
        This key sets the default state of the checkbox.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="had-bluetooth-devices-setup" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <summary>Whether the default Bluetooth adapter had set up devices associated to it</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>Whether the default Bluetooth adapter had set up devices associated to it</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The shell will only show a Bluetooth menu item if a Bluetooth
 | 
			
		||||
        adapter is powered, or if there were devices set up associated
 | 
			
		||||
        with the default adapter. This will be reset if the default
 | 
			
		||||
        adapter is ever seen not to have devices associated to it.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <child name="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>
 | 
			
		||||
        Keybinding to open the “Show Applications” view of the Activities
 | 
			
		||||
      <_summary>Keybinding to open the "Show Applications" view</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to open the "Show Applications" view of the Activities
 | 
			
		||||
        Overview.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-overview" type="as">
 | 
			
		||||
      <default>["<Super>s"]</default>
 | 
			
		||||
      <summary>Keybinding to open the overview</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>Keybinding to open the overview</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to open the Activities Overview.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-message-tray" type="as">
 | 
			
		||||
      <default>["<Super>v","<Super>m"]</default>
 | 
			
		||||
      <summary>Keybinding to toggle the visibility of the notification list</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>Keybinding to toggle the visibility of the notification list</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to toggle the visibility of the notification list.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="focus-active-notification" type="as">
 | 
			
		||||
      <default>["<Super>n"]</default>
 | 
			
		||||
      <summary>Keybinding to focus the active notification</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>Keybinding to focus the active notification</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to focus the active notification.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="pause-resume-tweens" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
 | 
			
		||||
      <description></description>
 | 
			
		||||
      <_summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</_summary>
 | 
			
		||||
      <_description></_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
@@ -143,10 +147,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>
 | 
			
		||||
 | 
			
		||||
@@ -155,11 +159,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>
 | 
			
		||||
 | 
			
		||||
@@ -173,20 +177,20 @@
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode">
 | 
			
		||||
      <default>'both'</default>
 | 
			
		||||
      <summary>The application icon mode.</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>The application icon mode.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
	Configures how the windows are shown in the switcher. Valid possibilities
 | 
			
		||||
	are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only”
 | 
			
		||||
	(shows only the application icon) or “both”.
 | 
			
		||||
      </description>
 | 
			
		||||
	are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only'
 | 
			
		||||
	(shows only the application icon) or 'both'.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key type="b" name="current-workspace-only">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <summary>Limit switcher to current workspace.</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
      <_summary>Limit switcher to current workspace.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
	If true, only windows from the current workspace are shown in the switcher.
 | 
			
		||||
	Otherwise, all windows are included.
 | 
			
		||||
      </description>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
@@ -194,43 +198,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>
 | 
			
		||||
@@ -399,77 +399,6 @@ StScrollBar {
 | 
			
		||||
  width: 48px;
 | 
			
		||||
  height: 48px; }
 | 
			
		||||
 | 
			
		||||
/* Audio selection dialog */
 | 
			
		||||
.audio-device-selection-dialog {
 | 
			
		||||
  spacing: 30px; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-content {
 | 
			
		||||
  spacing: 20px;
 | 
			
		||||
  padding: 24px; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-title {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  text-align: center; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-box {
 | 
			
		||||
  spacing: 20px; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-device {
 | 
			
		||||
  border: 1px solid rgba(238, 238, 236, 0.2);
 | 
			
		||||
  border-radius: 12px; }
 | 
			
		||||
  .audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus {
 | 
			
		||||
    background-color: #215d9c; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-device-box {
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  spacing: 20px; }
 | 
			
		||||
 | 
			
		||||
.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; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-main-layout {
 | 
			
		||||
  spacing: 12px; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-content {
 | 
			
		||||
  spacing: 20px; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-icon {
 | 
			
		||||
  icon-size: 48px; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-title {
 | 
			
		||||
  font-weight: bold; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-reason {
 | 
			
		||||
  color: #999999;
 | 
			
		||||
  font-weight: bold; }
 | 
			
		||||
 | 
			
		||||
/* Network Agent Dialog */
 | 
			
		||||
.network-dialog-secret-table {
 | 
			
		||||
  spacing-rows: 15px;
 | 
			
		||||
@@ -558,21 +487,6 @@ 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); }
 | 
			
		||||
  .pad-osd-window .pad-osd-title-box {
 | 
			
		||||
    spacing: 12px; }
 | 
			
		||||
  .pad-osd-window .pad-osd-title-menu-box {
 | 
			
		||||
    spacing: 6px; }
 | 
			
		||||
 | 
			
		||||
.combo-box-label {
 | 
			
		||||
  width: 15em; }
 | 
			
		||||
 | 
			
		||||
/* App Switcher */
 | 
			
		||||
.switcher-popup {
 | 
			
		||||
@@ -616,10 +530,6 @@ StScrollBar {
 | 
			
		||||
  width: 96px;
 | 
			
		||||
  height: 96px; }
 | 
			
		||||
 | 
			
		||||
/* Window Cycler */
 | 
			
		||||
.cycler-highlight {
 | 
			
		||||
  border: 5px solid #215d9c; }
 | 
			
		||||
 | 
			
		||||
/* Workspace Switcher */
 | 
			
		||||
.workspace-switcher-group {
 | 
			
		||||
  padding: 12px; }
 | 
			
		||||
@@ -738,20 +648,9 @@ StScrollBar {
 | 
			
		||||
.datemenu-displays-section {
 | 
			
		||||
  padding-bottom: 3em; }
 | 
			
		||||
 | 
			
		||||
.datemenu-displays-box {
 | 
			
		||||
  spacing: 1em; }
 | 
			
		||||
 | 
			
		||||
.datemenu-calendar-column {
 | 
			
		||||
  border: 0 solid #0d0d0d; }
 | 
			
		||||
  .datemenu-calendar-column:ltr {
 | 
			
		||||
    border-left-width: 1px; }
 | 
			
		||||
  .datemenu-calendar-column:rtl {
 | 
			
		||||
    border-right-width: 1px; }
 | 
			
		||||
 | 
			
		||||
.datemenu-today-button,
 | 
			
		||||
.world-clocks-button,
 | 
			
		||||
.weather-button,
 | 
			
		||||
.events-section-title {
 | 
			
		||||
.message-list-section-title {
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  padding: .4em; }
 | 
			
		||||
 | 
			
		||||
@@ -764,15 +663,12 @@ StScrollBar {
 | 
			
		||||
.datemenu-today-button:hover, .datemenu-today-button:focus,
 | 
			
		||||
.world-clocks-button:hover,
 | 
			
		||||
.world-clocks-button:focus,
 | 
			
		||||
.weather-button:hover,
 | 
			
		||||
.weather-button:focus,
 | 
			
		||||
.events-section-title:hover,
 | 
			
		||||
.events-section-title:focus {
 | 
			
		||||
.message-list-section-title:hover,
 | 
			
		||||
.message-list-section-title:focus {
 | 
			
		||||
  background-color: #0d0d0d; }
 | 
			
		||||
.datemenu-today-button:active,
 | 
			
		||||
.world-clocks-button:active,
 | 
			
		||||
.weather-button:active,
 | 
			
		||||
.events-section-title:active {
 | 
			
		||||
.message-list-section-title:active {
 | 
			
		||||
  color: white;
 | 
			
		||||
  background-color: #215d9c; }
 | 
			
		||||
 | 
			
		||||
@@ -780,17 +676,13 @@ StScrollBar {
 | 
			
		||||
  font-size: 1.5em; }
 | 
			
		||||
 | 
			
		||||
.world-clocks-header,
 | 
			
		||||
.weather-header,
 | 
			
		||||
.events-section-title {
 | 
			
		||||
.message-list-section-title {
 | 
			
		||||
  color: #999999;
 | 
			
		||||
  font-weight: bold; }
 | 
			
		||||
 | 
			
		||||
.world-clocks-grid {
 | 
			
		||||
  spacing-rows: 0.4em; }
 | 
			
		||||
 | 
			
		||||
.weather-box {
 | 
			
		||||
  spacing: 0.4em; }
 | 
			
		||||
 | 
			
		||||
.calendar-month-label {
 | 
			
		||||
  color: #f2f2f2;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
@@ -826,7 +718,7 @@ StScrollBar {
 | 
			
		||||
  border-radius: 1.4em; }
 | 
			
		||||
  .calendar-day-base:hover, .calendar-day-base:focus {
 | 
			
		||||
    background-color: #0d0d0d; }
 | 
			
		||||
  .calendar-day-base:active, .calendar-day-base:selected {
 | 
			
		||||
  .calendar-day-base:active {
 | 
			
		||||
    color: white;
 | 
			
		||||
    background-color: #215d9c;
 | 
			
		||||
    border-color: transparent; }
 | 
			
		||||
@@ -863,87 +755,71 @@ StScrollBar {
 | 
			
		||||
.calendar-week-number {
 | 
			
		||||
  font-size: 70%;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  width: 2.3em;
 | 
			
		||||
  height: 1.8em;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  padding: 0.5em 0 0;
 | 
			
		||||
  margin: 6px;
 | 
			
		||||
  background-color: rgba(255, 255, 255, 0.3);
 | 
			
		||||
  width: 2.8em;
 | 
			
		||||
  height: 2em;
 | 
			
		||||
  border-radius: 2px 1em 2px 2px;
 | 
			
		||||
  padding: 0.9em 0 0;
 | 
			
		||||
  margin: 3px;
 | 
			
		||||
  background-color: rgba(255, 255, 255, 0.1);
 | 
			
		||||
  color: #000; }
 | 
			
		||||
 | 
			
		||||
/* Message list */
 | 
			
		||||
.message-list {
 | 
			
		||||
  width: 31.5em; }
 | 
			
		||||
 | 
			
		||||
.message-list-clear-button.button {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  margin: 1.5em 1.5em 0; }
 | 
			
		||||
  .message-list-clear-button.button:hover, .message-list-clear-button.button:focus {
 | 
			
		||||
    background-color: #0d0d0d; }
 | 
			
		||||
 | 
			
		||||
.message-list-sections {
 | 
			
		||||
  spacing: 1em; }
 | 
			
		||||
  spacing: 1.5em; }
 | 
			
		||||
 | 
			
		||||
.message-list-section,
 | 
			
		||||
.message-list-section-list {
 | 
			
		||||
  spacing: 0.7em; }
 | 
			
		||||
 | 
			
		||||
.message-list-section-title-box {
 | 
			
		||||
  spacing: 0.4em; }
 | 
			
		||||
 | 
			
		||||
.message-list-section-close > StIcon {
 | 
			
		||||
  icon-size: 16px;
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  color: #000;
 | 
			
		||||
  background-color: #666666; }
 | 
			
		||||
 | 
			
		||||
/* FIXME: how do you do this in sass? */
 | 
			
		||||
.message-list-section-close:hover > StIcon,
 | 
			
		||||
.message-list-section-close:focus > StIcon {
 | 
			
		||||
  background-color: #999999; }
 | 
			
		||||
 | 
			
		||||
.message {
 | 
			
		||||
  background-color: #0d0d0d;
 | 
			
		||||
  border-radius: 3px; }
 | 
			
		||||
  .message:hover, .message:focus {
 | 
			
		||||
    background-color: #0d0d0d; }
 | 
			
		||||
    background-color: #262626; }
 | 
			
		||||
 | 
			
		||||
.message-icon-bin {
 | 
			
		||||
  padding: 10px 3px 10px 10px; }
 | 
			
		||||
  padding: 8px 0px 8px 8px; }
 | 
			
		||||
  .message-icon-bin:rtl {
 | 
			
		||||
    padding: 10px 10px 10px 3px; }
 | 
			
		||||
    padding: 8px 8px 8px 0px; }
 | 
			
		||||
 | 
			
		||||
.message-icon-bin > StIcon {
 | 
			
		||||
  color: #cccccc;
 | 
			
		||||
  icon-size: 16px;
 | 
			
		||||
  -st-icon-style: symbolic; }
 | 
			
		||||
  icon-size: 32px; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin:ltr {
 | 
			
		||||
  padding-left: 8px; }
 | 
			
		||||
.message-secondary-bin:rtl {
 | 
			
		||||
  padding-right: 8px; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin {
 | 
			
		||||
  padding: 0 12px; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin > .event-time {
 | 
			
		||||
  color: #999999;
 | 
			
		||||
  font-size: 0.7em;
 | 
			
		||||
  /* HACK: the label should be baseline-aligned with a 1em label,
 | 
			
		||||
           fake this with some bottom padding */
 | 
			
		||||
  padding-bottom: 0.13em; }
 | 
			
		||||
  color: #999999; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin > StIcon {
 | 
			
		||||
  icon-size: 16px; }
 | 
			
		||||
 | 
			
		||||
.message-title {
 | 
			
		||||
  color: #f2f2f2; }
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  font-size: 1.1em; }
 | 
			
		||||
 | 
			
		||||
.message-content {
 | 
			
		||||
  color: #cccccc;
 | 
			
		||||
  padding: 10px; }
 | 
			
		||||
 | 
			
		||||
.message-media-control {
 | 
			
		||||
  padding: 12px;
 | 
			
		||||
  color: #cccccc; }
 | 
			
		||||
  .message-media-control:last-child:ltr {
 | 
			
		||||
    padding-right: 18px; }
 | 
			
		||||
  .message-media-control:last-child:rtl {
 | 
			
		||||
    padding-left: 18px; }
 | 
			
		||||
  .message-media-control:hover {
 | 
			
		||||
    color: #fff; }
 | 
			
		||||
  .message-media-control:insensitive {
 | 
			
		||||
    color: #999999; }
 | 
			
		||||
 | 
			
		||||
.media-message-cover-icon {
 | 
			
		||||
  icon-size: 48px !important; }
 | 
			
		||||
  .media-message-cover-icon.fallback {
 | 
			
		||||
    color: #1a1a1a;
 | 
			
		||||
    background-color: #000;
 | 
			
		||||
    border: 2px solid #000;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    icon-size: 16px;
 | 
			
		||||
    padding: 8px; }
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
  font-size: .9em; }
 | 
			
		||||
 | 
			
		||||
.system-switch-user-submenu-icon.user-icon {
 | 
			
		||||
  icon-size: 20px;
 | 
			
		||||
 
 | 
			
		||||
 Submodule data/theme/gnome-shell-sass updated: e94bce1fcf...1d35c2890a
									
								
							@@ -399,77 +399,6 @@ StScrollBar {
 | 
			
		||||
  width: 48px;
 | 
			
		||||
  height: 48px; }
 | 
			
		||||
 | 
			
		||||
/* Audio selection dialog */
 | 
			
		||||
.audio-device-selection-dialog {
 | 
			
		||||
  spacing: 30px; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-content {
 | 
			
		||||
  spacing: 20px;
 | 
			
		||||
  padding: 24px; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-title {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  text-align: center; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-box {
 | 
			
		||||
  spacing: 20px; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-device {
 | 
			
		||||
  border: 1px solid rgba(238, 238, 236, 0.2);
 | 
			
		||||
  border-radius: 12px; }
 | 
			
		||||
  .audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus {
 | 
			
		||||
    background-color: #215d9c; }
 | 
			
		||||
 | 
			
		||||
.audio-selection-device-box {
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  spacing: 20px; }
 | 
			
		||||
 | 
			
		||||
.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; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-main-layout {
 | 
			
		||||
  spacing: 12px; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-content {
 | 
			
		||||
  spacing: 20px; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-icon {
 | 
			
		||||
  icon-size: 48px; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-title {
 | 
			
		||||
  font-weight: bold; }
 | 
			
		||||
 | 
			
		||||
.geolocation-dialog-reason {
 | 
			
		||||
  color: #8e8e80;
 | 
			
		||||
  font-weight: bold; }
 | 
			
		||||
 | 
			
		||||
/* Network Agent Dialog */
 | 
			
		||||
.network-dialog-secret-table {
 | 
			
		||||
  spacing-rows: 15px;
 | 
			
		||||
@@ -558,21 +487,6 @@ 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); }
 | 
			
		||||
  .pad-osd-window .pad-osd-title-box {
 | 
			
		||||
    spacing: 12px; }
 | 
			
		||||
  .pad-osd-window .pad-osd-title-menu-box {
 | 
			
		||||
    spacing: 6px; }
 | 
			
		||||
 | 
			
		||||
.combo-box-label {
 | 
			
		||||
  width: 15em; }
 | 
			
		||||
 | 
			
		||||
/* App Switcher */
 | 
			
		||||
.switcher-popup {
 | 
			
		||||
@@ -616,10 +530,6 @@ StScrollBar {
 | 
			
		||||
  width: 96px;
 | 
			
		||||
  height: 96px; }
 | 
			
		||||
 | 
			
		||||
/* Window Cycler */
 | 
			
		||||
.cycler-highlight {
 | 
			
		||||
  border: 5px solid #215d9c; }
 | 
			
		||||
 | 
			
		||||
/* Workspace Switcher */
 | 
			
		||||
.workspace-switcher-group {
 | 
			
		||||
  padding: 12px; }
 | 
			
		||||
@@ -738,20 +648,9 @@ StScrollBar {
 | 
			
		||||
.datemenu-displays-section {
 | 
			
		||||
  padding-bottom: 3em; }
 | 
			
		||||
 | 
			
		||||
.datemenu-displays-box {
 | 
			
		||||
  spacing: 1em; }
 | 
			
		||||
 | 
			
		||||
.datemenu-calendar-column {
 | 
			
		||||
  border: 0 solid #454c4c; }
 | 
			
		||||
  .datemenu-calendar-column:ltr {
 | 
			
		||||
    border-left-width: 1px; }
 | 
			
		||||
  .datemenu-calendar-column:rtl {
 | 
			
		||||
    border-right-width: 1px; }
 | 
			
		||||
 | 
			
		||||
.datemenu-today-button,
 | 
			
		||||
.world-clocks-button,
 | 
			
		||||
.weather-button,
 | 
			
		||||
.events-section-title {
 | 
			
		||||
.message-list-section-title {
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  padding: .4em; }
 | 
			
		||||
 | 
			
		||||
@@ -764,15 +663,12 @@ StScrollBar {
 | 
			
		||||
.datemenu-today-button:hover, .datemenu-today-button:focus,
 | 
			
		||||
.world-clocks-button:hover,
 | 
			
		||||
.world-clocks-button:focus,
 | 
			
		||||
.weather-button:hover,
 | 
			
		||||
.weather-button:focus,
 | 
			
		||||
.events-section-title:hover,
 | 
			
		||||
.events-section-title:focus {
 | 
			
		||||
.message-list-section-title:hover,
 | 
			
		||||
.message-list-section-title:focus {
 | 
			
		||||
  background-color: #454c4c; }
 | 
			
		||||
.datemenu-today-button:active,
 | 
			
		||||
.world-clocks-button:active,
 | 
			
		||||
.weather-button:active,
 | 
			
		||||
.events-section-title:active {
 | 
			
		||||
.message-list-section-title:active {
 | 
			
		||||
  color: white;
 | 
			
		||||
  background-color: #215d9c; }
 | 
			
		||||
 | 
			
		||||
@@ -780,17 +676,13 @@ StScrollBar {
 | 
			
		||||
  font-size: 1.5em; }
 | 
			
		||||
 | 
			
		||||
.world-clocks-header,
 | 
			
		||||
.weather-header,
 | 
			
		||||
.events-section-title {
 | 
			
		||||
.message-list-section-title {
 | 
			
		||||
  color: #8e8e80;
 | 
			
		||||
  font-weight: bold; }
 | 
			
		||||
 | 
			
		||||
.world-clocks-grid {
 | 
			
		||||
  spacing-rows: 0.4em; }
 | 
			
		||||
 | 
			
		||||
.weather-box {
 | 
			
		||||
  spacing: 0.4em; }
 | 
			
		||||
 | 
			
		||||
.calendar-month-label {
 | 
			
		||||
  color: #e2e2df;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
@@ -826,7 +718,7 @@ StScrollBar {
 | 
			
		||||
  border-radius: 1.4em; }
 | 
			
		||||
  .calendar-day-base:hover, .calendar-day-base:focus {
 | 
			
		||||
    background-color: #454c4c; }
 | 
			
		||||
  .calendar-day-base:active, .calendar-day-base:selected {
 | 
			
		||||
  .calendar-day-base:active {
 | 
			
		||||
    color: white;
 | 
			
		||||
    background-color: #215d9c;
 | 
			
		||||
    border-color: transparent; }
 | 
			
		||||
@@ -863,87 +755,71 @@ StScrollBar {
 | 
			
		||||
.calendar-week-number {
 | 
			
		||||
  font-size: 70%;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  width: 2.3em;
 | 
			
		||||
  height: 1.8em;
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
  padding: 0.5em 0 0;
 | 
			
		||||
  margin: 6px;
 | 
			
		||||
  background-color: rgba(238, 238, 236, 0.3);
 | 
			
		||||
  width: 2.8em;
 | 
			
		||||
  height: 2em;
 | 
			
		||||
  border-radius: 2px 1em 2px 2px;
 | 
			
		||||
  padding: 0.9em 0 0;
 | 
			
		||||
  margin: 3px;
 | 
			
		||||
  background-color: rgba(238, 238, 236, 0.1);
 | 
			
		||||
  color: #393f3f; }
 | 
			
		||||
 | 
			
		||||
/* Message list */
 | 
			
		||||
.message-list {
 | 
			
		||||
  width: 31.5em; }
 | 
			
		||||
 | 
			
		||||
.message-list-clear-button.button {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  margin: 1.5em 1.5em 0; }
 | 
			
		||||
  .message-list-clear-button.button:hover, .message-list-clear-button.button:focus {
 | 
			
		||||
    background-color: #454c4c; }
 | 
			
		||||
 | 
			
		||||
.message-list-sections {
 | 
			
		||||
  spacing: 1em; }
 | 
			
		||||
  spacing: 1.5em; }
 | 
			
		||||
 | 
			
		||||
.message-list-section,
 | 
			
		||||
.message-list-section-list {
 | 
			
		||||
  spacing: 0.7em; }
 | 
			
		||||
 | 
			
		||||
.message-list-section-title-box {
 | 
			
		||||
  spacing: 0.4em; }
 | 
			
		||||
 | 
			
		||||
.message-list-section-close > StIcon {
 | 
			
		||||
  icon-size: 16px;
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  color: #393f3f;
 | 
			
		||||
  background-color: #59594f; }
 | 
			
		||||
 | 
			
		||||
/* FIXME: how do you do this in sass? */
 | 
			
		||||
.message-list-section-close:hover > StIcon,
 | 
			
		||||
.message-list-section-close:focus > StIcon {
 | 
			
		||||
  background-color: #8e8e80; }
 | 
			
		||||
 | 
			
		||||
.message {
 | 
			
		||||
  background-color: #454c4c;
 | 
			
		||||
  border-radius: 3px; }
 | 
			
		||||
  .message:hover, .message:focus {
 | 
			
		||||
    background-color: #454c4c; }
 | 
			
		||||
    background-color: #5d6767; }
 | 
			
		||||
 | 
			
		||||
.message-icon-bin {
 | 
			
		||||
  padding: 10px 3px 10px 10px; }
 | 
			
		||||
  padding: 8px 0px 8px 8px; }
 | 
			
		||||
  .message-icon-bin:rtl {
 | 
			
		||||
    padding: 10px 10px 10px 3px; }
 | 
			
		||||
    padding: 8px 8px 8px 0px; }
 | 
			
		||||
 | 
			
		||||
.message-icon-bin > StIcon {
 | 
			
		||||
  color: #bebeb6;
 | 
			
		||||
  icon-size: 16px;
 | 
			
		||||
  -st-icon-style: symbolic; }
 | 
			
		||||
  icon-size: 32px; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin:ltr {
 | 
			
		||||
  padding-left: 8px; }
 | 
			
		||||
.message-secondary-bin:rtl {
 | 
			
		||||
  padding-right: 8px; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin {
 | 
			
		||||
  padding: 0 12px; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin > .event-time {
 | 
			
		||||
  color: #8e8e80;
 | 
			
		||||
  font-size: 0.7em;
 | 
			
		||||
  /* HACK: the label should be baseline-aligned with a 1em label,
 | 
			
		||||
           fake this with some bottom padding */
 | 
			
		||||
  padding-bottom: 0.13em; }
 | 
			
		||||
  color: #8e8e80; }
 | 
			
		||||
 | 
			
		||||
.message-secondary-bin > StIcon {
 | 
			
		||||
  icon-size: 16px; }
 | 
			
		||||
 | 
			
		||||
.message-title {
 | 
			
		||||
  color: #e2e2df; }
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  font-size: 1.1em; }
 | 
			
		||||
 | 
			
		||||
.message-content {
 | 
			
		||||
  color: #bebeb6;
 | 
			
		||||
  padding: 10px; }
 | 
			
		||||
 | 
			
		||||
.message-media-control {
 | 
			
		||||
  padding: 12px;
 | 
			
		||||
  color: #bebeb6; }
 | 
			
		||||
  .message-media-control:last-child:ltr {
 | 
			
		||||
    padding-right: 18px; }
 | 
			
		||||
  .message-media-control:last-child:rtl {
 | 
			
		||||
    padding-left: 18px; }
 | 
			
		||||
  .message-media-control:hover {
 | 
			
		||||
    color: #eeeeec; }
 | 
			
		||||
  .message-media-control:insensitive {
 | 
			
		||||
    color: #8e8e80; }
 | 
			
		||||
 | 
			
		||||
.media-message-cover-icon {
 | 
			
		||||
  icon-size: 48px !important; }
 | 
			
		||||
  .media-message-cover-icon.fallback {
 | 
			
		||||
    color: #515a5a;
 | 
			
		||||
    background-color: #393f3f;
 | 
			
		||||
    border: 2px solid #393f3f;
 | 
			
		||||
    border-radius: 2px;
 | 
			
		||||
    icon-size: 16px;
 | 
			
		||||
    padding: 8px; }
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
  font-size: .9em; }
 | 
			
		||||
 | 
			
		||||
.system-switch-user-submenu-icon.user-icon {
 | 
			
		||||
  icon-size: 20px;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
.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 -rpath $(MUTTER_TYPELIB_DIR)
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
@@ -125,7 +125,7 @@ EXTRA_DIST +=
 | 
			
		||||
# Files not to distribute
 | 
			
		||||
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
 | 
			
		||||
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
 | 
			
		||||
DISTCLEANFILES = $(DOC_MODULE).types
 | 
			
		||||
DISTCLEANFILES = $(DOC_MODULES).types
 | 
			
		||||
 | 
			
		||||
# Comment this out if you want 'make check' to test you doc status
 | 
			
		||||
# and run some sanity checks
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@
 | 
			
		||||
    <xi:include href="xml/shell-util.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-mount-operation.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-polkit-authentication-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-tp-client.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter id="object-tree">
 | 
			
		||||
    <title>Object Hierarchy</title>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 -rpath $(MUTTER_TYPELIB_DIR)
 | 
			
		||||
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ const GLib = imports.gi.GLib;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Format = imports.format;
 | 
			
		||||
 | 
			
		||||
@@ -24,6 +23,11 @@ const GnomeShellIface = '<node> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const customCss = '.prefs-button { \
 | 
			
		||||
                       padding: 8px; \
 | 
			
		||||
                       border-radius: 20px; \
 | 
			
		||||
                   }';
 | 
			
		||||
 | 
			
		||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
 | 
			
		||||
 | 
			
		||||
function stripPrefix(string, prefix) {
 | 
			
		||||
@@ -93,11 +97,9 @@ const Application = new Lang.Class({
 | 
			
		||||
            widget = this._buildErrorUI(extension, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let dialog = new Gtk.Window({ modal: !this._skipMainWindow,
 | 
			
		||||
                                      type_hint: Gdk.WindowTypeHint.DIALOG });
 | 
			
		||||
        dialog.set_titlebar(new Gtk.HeaderBar({ show_close_button: true,
 | 
			
		||||
                                                title: extension.metadata.name,
 | 
			
		||||
                                                visible: true }));
 | 
			
		||||
        let dialog = new Gtk.Dialog({ use_header_bar: true,
 | 
			
		||||
                                      modal: true,
 | 
			
		||||
                                      title: extension.metadata.name });
 | 
			
		||||
 | 
			
		||||
        if (this._skipMainWindow) {
 | 
			
		||||
            this.application.add_window(dialog);
 | 
			
		||||
@@ -110,7 +112,7 @@ const Application = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        dialog.set_default_size(600, 400);
 | 
			
		||||
        dialog.add(widget);
 | 
			
		||||
        dialog.get_content_area().add(widget);
 | 
			
		||||
        dialog.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -146,21 +148,16 @@ const Application = new Lang.Class({
 | 
			
		||||
        this._window = new Gtk.ApplicationWindow({ application: app,
 | 
			
		||||
                                                   window_position: Gtk.WindowPosition.CENTER });
 | 
			
		||||
 | 
			
		||||
        this._window.set_default_size(800, 500);
 | 
			
		||||
        this._window.set_size_request(800, 500);
 | 
			
		||||
 | 
			
		||||
        this._titlebar = new Gtk.HeaderBar({ show_close_button: true,
 | 
			
		||||
                                             title: _("Shell Extensions") });
 | 
			
		||||
                                             title: _("GNOME Shell Extensions") });
 | 
			
		||||
        this._window.set_titlebar(this._titlebar);
 | 
			
		||||
 | 
			
		||||
        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.DEFAULT |
 | 
			
		||||
                            Gio.SettingsBindFlags.INVERT_BOOLEAN);
 | 
			
		||||
 | 
			
		||||
        let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER });
 | 
			
		||||
        let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER,
 | 
			
		||||
                                              shadow_type: Gtk.ShadowType.IN,
 | 
			
		||||
                                              halign: Gtk.Align.CENTER,
 | 
			
		||||
                                              margin: 18 });
 | 
			
		||||
        this._window.add(scroll);
 | 
			
		||||
 | 
			
		||||
        this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE });
 | 
			
		||||
@@ -179,6 +176,21 @@ const Application = new Lang.Class({
 | 
			
		||||
        this._window.show_all();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addCustomStyle: function() {
 | 
			
		||||
        let provider = new Gtk.CssProvider();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            provider.load_from_data(customCss, -1);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to add application style');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let screen = this._window.window.get_screen();
 | 
			
		||||
        let priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
 | 
			
		||||
        Gtk.StyleContext.add_provider_for_screen(screen, provider, priority);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sortList: function(row1, row2) {
 | 
			
		||||
        let name1 = ExtensionUtils.extensions[row1.uuid].metadata.name;
 | 
			
		||||
        let name2 = ExtensionUtils.extensions[row2.uuid].metadata.name;
 | 
			
		||||
@@ -227,6 +239,7 @@ const Application = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onStartup: function(app) {
 | 
			
		||||
        this._buildUI(app);
 | 
			
		||||
        this._addCustomStyle();
 | 
			
		||||
        this._scanExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -253,18 +266,6 @@ 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,
 | 
			
		||||
@@ -283,10 +284,6 @@ 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();
 | 
			
		||||
    },
 | 
			
		||||
@@ -295,8 +292,7 @@ const ExtensionRow = new Lang.Class({
 | 
			
		||||
        let extension = ExtensionUtils.extensions[this.uuid];
 | 
			
		||||
 | 
			
		||||
        let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
 | 
			
		||||
                                 hexpand: true, margin_end: 24, spacing: 24,
 | 
			
		||||
                                 margin: 12 });
 | 
			
		||||
                                 hexpand: true, margin: 12, spacing: 6 });
 | 
			
		||||
        this.add(hbox);
 | 
			
		||||
 | 
			
		||||
        let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
 | 
			
		||||
@@ -310,9 +306,9 @@ const ExtensionRow = new Lang.Class({
 | 
			
		||||
        vbox.add(label);
 | 
			
		||||
 | 
			
		||||
        let desc = extension.metadata.description.split('\n')[0];
 | 
			
		||||
        label = new DescriptionLabel({ label: desc, wrap: true, lines: 2,
 | 
			
		||||
                                       ellipsize: Pango.EllipsizeMode.END,
 | 
			
		||||
                                       xalign: 0, yalign: 0 });
 | 
			
		||||
        label = new Gtk.Label({ label: desc,
 | 
			
		||||
                                ellipsize: Pango.EllipsizeMode.END,
 | 
			
		||||
                                halign: Gtk.Align.START });
 | 
			
		||||
        vbox.add(label);
 | 
			
		||||
 | 
			
		||||
        let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
 | 
			
		||||
@@ -320,7 +316,7 @@ const ExtensionRow = new Lang.Class({
 | 
			
		||||
        button.add(new Gtk.Image({ icon_name: 'emblem-system-symbolic',
 | 
			
		||||
                                   icon_size: Gtk.IconSize.BUTTON,
 | 
			
		||||
                                   visible: true }));
 | 
			
		||||
        button.get_style_context().add_class('circular');
 | 
			
		||||
        button.get_style_context().add_class('prefs-button');
 | 
			
		||||
        hbox.add(button);
 | 
			
		||||
 | 
			
		||||
        this.prefsButton = button;
 | 
			
		||||
@@ -343,8 +339,7 @@ const ExtensionRow = new Lang.Class({
 | 
			
		||||
        let extension = ExtensionUtils.extensions[this.uuid];
 | 
			
		||||
        let checkVersion = !this._settings.get_boolean('disable-extension-version-validation');
 | 
			
		||||
 | 
			
		||||
        return !this._settings.get_boolean('disable-user-extensions') &&
 | 
			
		||||
               !(checkVersion && ExtensionUtils.isOutOfDate(extension));
 | 
			
		||||
        return !(checkVersion && ExtensionUtils.isOutOfDate(extension));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isEnabled: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ 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;
 | 
			
		||||
@@ -114,7 +113,6 @@ 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',
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,6 @@ function FprintManager() {
 | 
			
		||||
                                   g_object_path: '/net/reactivated/Fprint/Manager',
 | 
			
		||||
                                   g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        self.init(null);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        log('Failed to connect to Fprint service: ' + e.message);
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self.init(null);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -804,11 +804,6 @@ 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();
 | 
			
		||||
@@ -1225,7 +1220,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        // Don't allow type ahead at the login screen
 | 
			
		||||
        this._authPrompt.addCharacter(unichar);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    finish: function(onComplete) {
 | 
			
		||||
 
 | 
			
		||||
@@ -128,22 +128,18 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._client = client;
 | 
			
		||||
 | 
			
		||||
        this._defaultService = null;
 | 
			
		||||
        this._preemptingService = null;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed',
 | 
			
		||||
                               Lang.bind(this, this._updateDefaultService));
 | 
			
		||||
        this._updateDefaultService();
 | 
			
		||||
 | 
			
		||||
        this._fprintManager = Fprint.FprintManager();
 | 
			
		||||
        this._fprintManager = new Fprint.FprintManager();
 | 
			
		||||
        this._smartcardManager = SmartcardManager.getSmartcardManager();
 | 
			
		||||
 | 
			
		||||
        // We check for smartcards right away, since an inserted smartcard
 | 
			
		||||
        // at startup should result in immediately initiating authentication.
 | 
			
		||||
        // This is different than fingeprint readers, where we only check them
 | 
			
		||||
        // after a user has been picked.
 | 
			
		||||
        this.smartcardDetected = false;
 | 
			
		||||
        this._checkForSmartcard();
 | 
			
		||||
 | 
			
		||||
        this._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted',
 | 
			
		||||
@@ -297,8 +293,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
    _checkForFingerprintReader: function() {
 | 
			
		||||
        this._haveFingerprintReader = false;
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY) ||
 | 
			
		||||
            this._fprintManager == null) {
 | 
			
		||||
        if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) {
 | 
			
		||||
            this._updateDefaultService();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,22 +23,18 @@
 | 
			
		||||
    <file>misc/modemManager.js</file>
 | 
			
		||||
    <file>misc/objectManager.js</file>
 | 
			
		||||
    <file>misc/params.js</file>
 | 
			
		||||
    <file>misc/permissionStore.js</file>
 | 
			
		||||
    <file>misc/smartcardManager.js</file>
 | 
			
		||||
    <file>misc/util.js</file>
 | 
			
		||||
    <file>misc/weather.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>perf/core.js</file>
 | 
			
		||||
    <file>perf/hwtest.js</file>
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
    <file>ui/appFavorites.js</file>
 | 
			
		||||
    <file>ui/audioDeviceSelection.js</file>
 | 
			
		||||
    <file>ui/backgroundMenu.js</file>
 | 
			
		||||
    <file>ui/background.js</file>
 | 
			
		||||
    <file>ui/boxpointer.js</file>
 | 
			
		||||
@@ -66,15 +62,12 @@
 | 
			
		||||
    <file>ui/magnifierDBus.js</file>
 | 
			
		||||
    <file>ui/main.js</file>
 | 
			
		||||
    <file>ui/messageTray.js</file>
 | 
			
		||||
    <file>ui/messageList.js</file>
 | 
			
		||||
    <file>ui/modalDialog.js</file>
 | 
			
		||||
    <file>ui/mpris.js</file>
 | 
			
		||||
    <file>ui/notificationDaemon.js</file>
 | 
			
		||||
    <file>ui/osdWindow.js</file>
 | 
			
		||||
    <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>
 | 
			
		||||
@@ -119,7 +112,6 @@
 | 
			
		||||
    <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,5 +15,3 @@ 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,7 +6,9 @@
 | 
			
		||||
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;
 | 
			
		||||
@@ -19,25 +21,14 @@ 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.split('\n');
 | 
			
		||||
    let extensionStackLine;
 | 
			
		||||
    let stack = (new Error()).stack;
 | 
			
		||||
 | 
			
		||||
    // 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 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];
 | 
			
		||||
    if (!extensionStackLine)
 | 
			
		||||
        return null;
 | 
			
		||||
        throw new Error('Could not find current extension');
 | 
			
		||||
 | 
			
		||||
    // The stack line is like:
 | 
			
		||||
    //   init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
 | 
			
		||||
@@ -47,7 +38,7 @@ function getCurrentExtension() {
 | 
			
		||||
    //   @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
 | 
			
		||||
    let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
 | 
			
		||||
    if (!match)
 | 
			
		||||
        return null;
 | 
			
		||||
        throw new Error('Could not find current extension');
 | 
			
		||||
 | 
			
		||||
    let path = match[1];
 | 
			
		||||
    let file = Gio.File.new_for_path(path);
 | 
			
		||||
@@ -61,7 +52,7 @@ function getCurrentExtension() {
 | 
			
		||||
        file = file.get_parent();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
    throw new Error('Could not find current extension');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -149,13 +140,12 @@ function createExtensionObject(uuid, dir, type) {
 | 
			
		||||
    return extension;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _extension = null;
 | 
			
		||||
 | 
			
		||||
function installImporter(extension) {
 | 
			
		||||
    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;
 | 
			
		||||
    _extension = extension;
 | 
			
		||||
    ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path);
 | 
			
		||||
    _extension = null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ExtensionFinder = new Lang.Class({
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ const HistoryManager = new Lang.Class({
 | 
			
		||||
            this._indexChanged();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._historyIndex ? this._history[this._historyIndex -1] : null;
 | 
			
		||||
        return this._historyIndex[this._history.length];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(input) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,10 @@ const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
let IBusCandidatePopup;
 | 
			
		||||
try {
 | 
			
		||||
    var IBus = imports.gi.IBus;
 | 
			
		||||
    _checkIBusVersion(1, 5, 2);
 | 
			
		||||
    IBusCandidatePopup = imports.ui.ibusCandidatePopup;
 | 
			
		||||
    const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
 | 
			
		||||
} catch (e) {
 | 
			
		||||
    var IBus = null;
 | 
			
		||||
    log(e);
 | 
			
		||||
@@ -190,7 +189,7 @@ const IBusManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEngineDesc: function(id) {
 | 
			
		||||
        if (!IBus || !this._ready || !this._engines.hasOwnProperty(id))
 | 
			
		||||
        if (!IBus || !this._ready)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return this._engines[id];
 | 
			
		||||
 
 | 
			
		||||
@@ -128,8 +128,7 @@ const KeyboardManager = new Lang.Class({
 | 
			
		||||
        if (!found)
 | 
			
		||||
            [, , id] = GnomeDesktop.get_input_source_from_locale(DEFAULT_LOCALE);
 | 
			
		||||
 | 
			
		||||
        let _layout, _variant;
 | 
			
		||||
        [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
 | 
			
		||||
        let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
 | 
			
		||||
        if (found)
 | 
			
		||||
            return { layout: _layout, variant: _variant };
 | 
			
		||||
        else
 | 
			
		||||
 
 | 
			
		||||
@@ -40,9 +40,6 @@ 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>';
 | 
			
		||||
 | 
			
		||||
@@ -134,13 +131,10 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanSuspendRemote(function(result, error) {
 | 
			
		||||
            if (error) {
 | 
			
		||||
                asyncCallback(false, false);
 | 
			
		||||
            } else {
 | 
			
		||||
                let needsAuth = result[0] == 'challenge';
 | 
			
		||||
                let canSuspend = needsAuth || result[0] == 'yes';
 | 
			
		||||
                asyncCallback(canSuspend, needsAuth);
 | 
			
		||||
            }
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no' && result[0] != 'na');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -193,7 +187,7 @@ const LoginManagerDummy = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        asyncCallback(false, false);
 | 
			
		||||
        asyncCallback(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    listSessions: function(asyncCallback) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
 | 
			
		||||
const 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"/> \
 | 
			
		||||
      <arg name="permissions" type="a{sas}" direction="out"/> \
 | 
			
		||||
      <arg name="data" type="v" direction="out"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
    <method name="Set"> \
 | 
			
		||||
      <arg name="table" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="create" type="b" direction="in"/> \
 | 
			
		||||
      <arg name="id" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="app_permissions" type="a{sas}" direction="in"/> \
 | 
			
		||||
      <arg name="data" type="v" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
    <signal name="Changed"> \
 | 
			
		||||
      <arg name="table" type="s" direction="out"/> \
 | 
			
		||||
      <arg name="id" type="s" direction="out"/> \
 | 
			
		||||
      <arg name="deleted" type="b" direction="out"/> \
 | 
			
		||||
      <arg name="data" type="v" direction="out"/> \
 | 
			
		||||
      <arg name="permissions" type="a{sas}" direction="out"/> \
 | 
			
		||||
    </signal> \
 | 
			
		||||
  </interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const PermissionStoreProxy = Gio.DBusProxy.makeProxyWrapper(PermissionStoreIface);
 | 
			
		||||
 | 
			
		||||
function PermissionStore(initCallback, cancellable) {
 | 
			
		||||
    return new PermissionStoreProxy(Gio.DBus.session,
 | 
			
		||||
                                    'org.freedesktop.impl.portal.PermissionStore',
 | 
			
		||||
                                    '/org/freedesktop/impl/portal/PermissionStore',
 | 
			
		||||
                                    initCallback, cancellable);
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										136
									
								
								js/misc/util.js
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								js/misc/util.js
									
									
									
									
									
								
							@@ -1,12 +1,9 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gettext = imports.gettext;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +94,7 @@ function spawnApp(argv) {
 | 
			
		||||
                                                      Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
 | 
			
		||||
 | 
			
		||||
        let context = global.create_app_launch_context(0, -1);
 | 
			
		||||
        app.launch([], context, false);
 | 
			
		||||
        app.launch([], context);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        _handleSpawnError(argv[0], err);
 | 
			
		||||
    }
 | 
			
		||||
@@ -164,41 +161,6 @@ function _handleSpawnError(command, err) {
 | 
			
		||||
    Main.notifyError(title, err.message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatTimeSpan(date) {
 | 
			
		||||
    let now = GLib.DateTime.new_now_local();
 | 
			
		||||
 | 
			
		||||
    let timespan = now.difference(date);
 | 
			
		||||
 | 
			
		||||
    let minutesAgo = timespan / GLib.TIME_SPAN_MINUTE;
 | 
			
		||||
    let hoursAgo = timespan / GLib.TIME_SPAN_HOUR;
 | 
			
		||||
    let daysAgo = timespan / GLib.TIME_SPAN_DAY;
 | 
			
		||||
    let weeksAgo = daysAgo / 7;
 | 
			
		||||
    let monthsAgo = daysAgo / 30;
 | 
			
		||||
    let yearsAgo = weeksAgo / 52;
 | 
			
		||||
 | 
			
		||||
    if (minutesAgo < 5)
 | 
			
		||||
        return _("Just now");
 | 
			
		||||
    if (hoursAgo < 1)
 | 
			
		||||
        return Gettext.ngettext("%d minute ago",
 | 
			
		||||
                                "%d minutes ago", minutesAgo).format(minutesAgo);
 | 
			
		||||
    if (daysAgo < 1)
 | 
			
		||||
        return Gettext.ngettext("%d hour ago",
 | 
			
		||||
                                "%d hours ago", hoursAgo).format(hoursAgo);
 | 
			
		||||
    if (daysAgo < 2)
 | 
			
		||||
        return _("Yesterday");
 | 
			
		||||
    if (daysAgo < 15)
 | 
			
		||||
        return Gettext.ngettext("%d day ago",
 | 
			
		||||
                                "%d days ago", daysAgo).format(daysAgo);
 | 
			
		||||
    if (weeksAgo < 8)
 | 
			
		||||
        return Gettext.ngettext("%d week ago",
 | 
			
		||||
                                "%d weeks ago", weeksAgo).format(weeksAgo);
 | 
			
		||||
    if (yearsAgo < 1)
 | 
			
		||||
        return Gettext.ngettext("%d month ago",
 | 
			
		||||
                                "%d months ago", monthsAgo).format(monthsAgo);
 | 
			
		||||
    return Gettext.ngettext("%d year ago",
 | 
			
		||||
                            "%d years ago", yearsAgo).format(yearsAgo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatTime(time, params) {
 | 
			
		||||
    let date;
 | 
			
		||||
    // HACK: The built-in Date type sucks at timezones, which we need for the
 | 
			
		||||
@@ -281,10 +243,7 @@ function formatTime(time, params) {
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            format = N_("%B %d %Y, %l\u2236%M %p");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let formattedTime = date.format(Shell.util_translate_time_string(format));
 | 
			
		||||
    // prepend LTR-mark to colon/ratio to force a text direction on times
 | 
			
		||||
    return formattedTime.replace(/([:\u2236])/g, '\u200e$1');
 | 
			
		||||
    return date.format(Shell.util_translate_time_string(format));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createTimeLabel(date, params) {
 | 
			
		||||
@@ -439,94 +398,3 @@ function ensureActorVisibleInScrollView(scrollView, actor) {
 | 
			
		||||
                       time: SCROLL_TIME,
 | 
			
		||||
                       transition: 'easeOutQuad' });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AppSettingsMonitor = new Lang.Class({
 | 
			
		||||
    Name: 'AppSettingsMonitor',
 | 
			
		||||
 | 
			
		||||
    _init: function(appId, schemaId) {
 | 
			
		||||
        this._appId = appId;
 | 
			
		||||
        this._schemaId = schemaId;
 | 
			
		||||
 | 
			
		||||
        this._app = null;
 | 
			
		||||
        this._settings = null;
 | 
			
		||||
        this._handlers = [];
 | 
			
		||||
 | 
			
		||||
        this._schemaSource = Gio.SettingsSchemaSource.get_default();
 | 
			
		||||
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this._appSystem.connect('installed-changed',
 | 
			
		||||
                                Lang.bind(this, this._onInstalledChanged));
 | 
			
		||||
        this._onInstalledChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get available() {
 | 
			
		||||
        return this._app != null && this._settings != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateApp: function() {
 | 
			
		||||
        if (this._app)
 | 
			
		||||
            this._app.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    watchSetting: function(key, callback) {
 | 
			
		||||
        let handler = { id: 0, key: key, callback: callback };
 | 
			
		||||
        this._handlers.push(handler);
 | 
			
		||||
 | 
			
		||||
        this._connectHandler(handler);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectHandler: function(handler) {
 | 
			
		||||
        if (!this._settings || handler.id > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        handler.id = this._settings.connect('changed::' + handler.key,
 | 
			
		||||
                                            handler.callback);
 | 
			
		||||
        handler.callback(this._settings, handler.key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _disconnectHandler: function(handler) {
 | 
			
		||||
        if (this._settings && handler.id > 0)
 | 
			
		||||
            this._settings.disconnect(handler.id);
 | 
			
		||||
        handler.id = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInstalledChanged: function() {
 | 
			
		||||
        let hadApp = (this._app != null);
 | 
			
		||||
        this._app = this._appSystem.lookup_app(this._appId);
 | 
			
		||||
        let haveApp = (this._app != null);
 | 
			
		||||
 | 
			
		||||
        if (hadApp == haveApp)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (haveApp)
 | 
			
		||||
            this._checkSettings();
 | 
			
		||||
        else
 | 
			
		||||
            this._setSettings(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setSettings: function(settings) {
 | 
			
		||||
        this._handlers.forEach((handler) => { this._disconnectHandler(handler); });
 | 
			
		||||
 | 
			
		||||
        let hadSettings = (this._settings != null);
 | 
			
		||||
        this._settings = settings;
 | 
			
		||||
        let haveSettings = (this._settings != null);
 | 
			
		||||
 | 
			
		||||
        this._handlers.forEach((handler) => { this._connectHandler(handler); });
 | 
			
		||||
 | 
			
		||||
        if (hadSettings != haveSettings)
 | 
			
		||||
            this.emit('available-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkSettings: function() {
 | 
			
		||||
        let schema = this._schemaSource.lookup(this._schemaId, true);
 | 
			
		||||
        if (schema) {
 | 
			
		||||
            this._setSettings(new Gio.Settings({ settings_schema: schema }));
 | 
			
		||||
        } else if (this._app) {
 | 
			
		||||
            Mainloop.timeout_add_seconds(1, () => {
 | 
			
		||||
                this._checkSettings();
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AppSettingsMonitor.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,247 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Geoclue = imports.gi.Geoclue;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GWeather = imports.gi.GWeather;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const PermissionStore = imports.misc.permissionStore;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
// Minimum time between updates to show loading indication
 | 
			
		||||
const UPDATE_THRESHOLD = 10 * GLib.TIME_SPAN_MINUTE;
 | 
			
		||||
 | 
			
		||||
const WeatherClient = new Lang.Class({
 | 
			
		||||
    Name: 'WeatherClient',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._loading = false;
 | 
			
		||||
        this._locationValid = false;
 | 
			
		||||
        this._lastUpdate = GLib.DateTime.new_from_unix_local(0);
 | 
			
		||||
 | 
			
		||||
        this._autoLocationRequested = false;
 | 
			
		||||
        this._mostRecentLocation = null;
 | 
			
		||||
 | 
			
		||||
        this._gclueService = null;
 | 
			
		||||
        this._gclueStarted = false;
 | 
			
		||||
        this._gclueStarting = false;
 | 
			
		||||
        this._gclueLocationChangedId = 0;
 | 
			
		||||
 | 
			
		||||
        this._weatherAuthorized = false;
 | 
			
		||||
        this._permStore = new PermissionStore.PermissionStore((proxy, error) => {
 | 
			
		||||
            if (error) {
 | 
			
		||||
                log('Failed to connect to permissionStore: ' + error.message);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._permStore.LookupRemote('gnome', 'geolocation', (res, error) => {
 | 
			
		||||
                if (error)
 | 
			
		||||
                    log('Error looking up permission: ' + error.message);
 | 
			
		||||
 | 
			
		||||
                let [perms, data] = error ? [{}, null] : res;
 | 
			
		||||
                let  params = ['gnome', 'geolocation', false, data, perms];
 | 
			
		||||
                this._onPermStoreChanged(this._permStore, '', params);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        this._permStore.connectSignal('Changed',
 | 
			
		||||
                                      Lang.bind(this, this._onPermStoreChanged));
 | 
			
		||||
 | 
			
		||||
        this._locationSettings = new Gio.Settings({ schema_id: 'org.gnome.system.location' });
 | 
			
		||||
        this._locationSettings.connect('changed::enabled',
 | 
			
		||||
                                       Lang.bind(this, this._updateAutoLocation));
 | 
			
		||||
 | 
			
		||||
        this._world = GWeather.Location.get_world();
 | 
			
		||||
 | 
			
		||||
        this._providers = GWeather.Provider.METAR |
 | 
			
		||||
                          GWeather.Provider.YR_NO |
 | 
			
		||||
                          GWeather.Provider.OWM;
 | 
			
		||||
 | 
			
		||||
        this._weatherInfo = new GWeather.Info({ enabled_providers: 0 });
 | 
			
		||||
        this._weatherInfo.connect_after('updated', () => {
 | 
			
		||||
            this._lastUpdate = GLib.DateTime.new_now_local();
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this._weatherAppMon = new Util.AppSettingsMonitor('org.gnome.Weather.Application.desktop',
 | 
			
		||||
                                                          'org.gnome.Weather.Application');
 | 
			
		||||
        this._weatherAppMon.connect('available-changed', () => { this.emit('changed'); });
 | 
			
		||||
        this._weatherAppMon.watchSetting('automatic-location',
 | 
			
		||||
                                         Lang.bind(this, this._onAutomaticLocationChanged));
 | 
			
		||||
        this._weatherAppMon.watchSetting('locations',
 | 
			
		||||
                                         Lang.bind(this, this._onLocationsChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get available() {
 | 
			
		||||
        return this._weatherAppMon.available;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get loading() {
 | 
			
		||||
        return this._loading;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get hasLocation() {
 | 
			
		||||
        return this._locationValid;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get info() {
 | 
			
		||||
        return this._weatherInfo;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateApp: function() {
 | 
			
		||||
        this._weatherAppMon.activateApp();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update: function() {
 | 
			
		||||
        if (!this._locationValid)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let now = GLib.DateTime.new_now_local();
 | 
			
		||||
        // Update without loading indication if the current info is recent enough
 | 
			
		||||
        if (this._weatherInfo.is_valid() &&
 | 
			
		||||
            now.difference(this._lastUpdate) < UPDATE_THRESHOLD)
 | 
			
		||||
            this._weatherInfo.update();
 | 
			
		||||
        else
 | 
			
		||||
            this._loadInfo();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get _useAutoLocation() {
 | 
			
		||||
        return this._autoLocationRequested &&
 | 
			
		||||
               this._locationSettings.get_boolean('enabled') &&
 | 
			
		||||
               this._weatherAuthorized;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadInfo: function() {
 | 
			
		||||
        let id = this._weatherInfo.connect('updated', () => {
 | 
			
		||||
            this._weatherInfo.disconnect(id);
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this._loading = true;
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
 | 
			
		||||
        this._weatherInfo.update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _locationsEqual: function(loc1, loc2) {
 | 
			
		||||
        if (loc1 == loc2)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (loc1 == null || loc2 == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return loc1.equal(loc2);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setLocation: function(location) {
 | 
			
		||||
        if (this._locationsEqual(this._weatherInfo.location, location))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._weatherInfo.abort();
 | 
			
		||||
        this._weatherInfo.set_location(location);
 | 
			
		||||
        this._locationValid = (location != null);
 | 
			
		||||
 | 
			
		||||
        this._weatherInfo.set_enabled_providers(location ? this._providers : 0);
 | 
			
		||||
 | 
			
		||||
        if (location)
 | 
			
		||||
            this._loadInfo();
 | 
			
		||||
        else
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLocationMonitoring: function() {
 | 
			
		||||
        if (this._useAutoLocation) {
 | 
			
		||||
            if (this._gclueLocationChangedId != 0 || this._gclueService == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            this._gclueLocationChangedId =
 | 
			
		||||
                this._gclueService.connect('notify::location',
 | 
			
		||||
                                           Lang.bind(this, this._onGClueLocationChanged));
 | 
			
		||||
            this._onGClueLocationChanged();
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._gclueLocationChangedId)
 | 
			
		||||
                this._gclueService.disconnect(this._gclueLocationChangedId);
 | 
			
		||||
            this._gclueLocationChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startGClueService: function() {
 | 
			
		||||
        if (this._gclueStarting)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._gclueStarting = true;
 | 
			
		||||
 | 
			
		||||
        Geoclue.Simple.new('org.gnome.Shell', Geoclue.AccuracyLevel.CITY, null,
 | 
			
		||||
            (o, res) => {
 | 
			
		||||
                try {
 | 
			
		||||
                    this._gclueService = Geoclue.Simple.new_finish(res);
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    log('Failed to connect to Geoclue2 service: ' + e.message);
 | 
			
		||||
                    this._setLocation(this._mostRecentLocation);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                this._gclueStarted = true;
 | 
			
		||||
                this._gclueService.get_client().distance_threshold = 100;
 | 
			
		||||
                this._updateLocationMonitoring();
 | 
			
		||||
            });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGClueLocationChanged: function() {
 | 
			
		||||
        let geoLocation = this._gclueService.location;
 | 
			
		||||
        let location = GWeather.Location.new_detached(geoLocation.description,
 | 
			
		||||
                                                      null,
 | 
			
		||||
                                                      geoLocation.latitude,
 | 
			
		||||
                                                      geoLocation.longitude);
 | 
			
		||||
        this._setLocation(location);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAutomaticLocationChanged: function(settings, key) {
 | 
			
		||||
        let useAutoLocation = settings.get_boolean(key);
 | 
			
		||||
        if (this._autoLocationRequested == useAutoLocation)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._autoLocationRequested = useAutoLocation;
 | 
			
		||||
 | 
			
		||||
        this._updateAutoLocation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAutoLocation: function() {
 | 
			
		||||
        this._updateLocationMonitoring();
 | 
			
		||||
 | 
			
		||||
        if (this._useAutoLocation)
 | 
			
		||||
            this._startGClueService();
 | 
			
		||||
        else
 | 
			
		||||
            this._setLocation(this._mostRecentLocation);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLocationsChanged: function(settings, key) {
 | 
			
		||||
        let serialized = settings.get_value(key).deep_unpack().shift();
 | 
			
		||||
        let mostRecentLocation = null;
 | 
			
		||||
 | 
			
		||||
        if (serialized)
 | 
			
		||||
            mostRecentLocation = this._world.deserialize(serialized);
 | 
			
		||||
 | 
			
		||||
        if (this._locationsEqual(this._mostRecentLocation, mostRecentLocation))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._mostRecentLocation = mostRecentLocation;
 | 
			
		||||
 | 
			
		||||
        if (!this._useAutoLocation || !this._gclueStarted)
 | 
			
		||||
            this._setLocation(this._mostRecentLocation);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPermStoreChanged: function(proxy, sender, params) {
 | 
			
		||||
        let [table, id, deleted, data, perms] = params;
 | 
			
		||||
 | 
			
		||||
        if (table != 'gnome' || id != 'geolocation')
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let permission = perms['org.gnome.Weather.Application'] || ['NONE'];
 | 
			
		||||
        let [accuracy] = permission;
 | 
			
		||||
        this._weatherAuthorized = accuracy != 'NONE';
 | 
			
		||||
 | 
			
		||||
        this._updateAutoLocation();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(WeatherClient.prototype);
 | 
			
		||||
@@ -19,15 +19,7 @@ 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> \
 | 
			
		||||
@@ -50,71 +42,6 @@ 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,
 | 
			
		||||
@@ -122,14 +49,8 @@ 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 = CONNECTIVITY_CHECK_URI;
 | 
			
		||||
            url = 'http://www.gnome.org';
 | 
			
		||||
            this._originalUrlWasGnome = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._originalUrlWasGnome = false;
 | 
			
		||||
@@ -141,38 +62,28 @@ const PortalWindow = new Lang.Class({
 | 
			
		||||
        this._lastRecheck = 0;
 | 
			
		||||
        this._recheckAtExit = false;
 | 
			
		||||
 | 
			
		||||
        this._webContext = WebKit.WebContext.new_ephemeral();
 | 
			
		||||
        this._webContext.set_cache_model(WebKit.CacheModel.DOCUMENT_VIEWER);
 | 
			
		||||
        this._webContext.set_network_proxy_settings(WebKit.NetworkProxyMode.NO_PROXY, null);
 | 
			
		||||
 | 
			
		||||
        this._webView = WebKit.WebView.new_with_context(this._webContext);
 | 
			
		||||
        this._webView = new WebKit.WebView();
 | 
			
		||||
        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::uri', Lang.bind(this, this._syncUri));
 | 
			
		||||
        this._syncUri();
 | 
			
		||||
        this._webView.connect('notify::title', Lang.bind(this, this._syncTitle));
 | 
			
		||||
        this._syncTitle();
 | 
			
		||||
 | 
			
		||||
        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']);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroyWindow: function() {
 | 
			
		||||
        this.destroy();
 | 
			
		||||
    },
 | 
			
		||||
    _syncTitle: function() {
 | 
			
		||||
        let title = this._webView.title;
 | 
			
		||||
 | 
			
		||||
    _syncUri: function() {
 | 
			
		||||
        let uri = this._webView.uri;
 | 
			
		||||
        if (uri)
 | 
			
		||||
            this._headerBar.setSubtitle(GLib.uri_unescape_string(uri, null));
 | 
			
		||||
        else
 | 
			
		||||
            this._headerBar.setSubtitle('');
 | 
			
		||||
        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");
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    refresh: function() {
 | 
			
		||||
@@ -188,46 +99,8 @@ const PortalWindow = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLoadChanged: function(view, loadEvent) {
 | 
			
		||||
        if (loadEvent == WebKit.LoadEvent.STARTED) {
 | 
			
		||||
            this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED);
 | 
			
		||||
        } else if (loadEvent == WebKit.LoadEvent.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) {
 | 
			
		||||
            let navigationAction = decision.get_navigation_action();
 | 
			
		||||
            if (navigationAction.is_user_gesture()) {
 | 
			
		||||
                // Even though the portal asks for a new window,
 | 
			
		||||
                // perform the navigation in the current one. Some
 | 
			
		||||
                // portals open a window as their last login step and
 | 
			
		||||
                // ignoring that window causes them to not let the
 | 
			
		||||
                // user go through. We don't risk popups taking over
 | 
			
		||||
                // the page because we check that the navigation is
 | 
			
		||||
                // user initiated.
 | 
			
		||||
                this._webView.load_request(navigationAction.get_request());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            decision.ignore();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -239,12 +112,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() == CONNECTIVITY_CHECK_HOST && this._everSeenRedirect) {
 | 
			
		||||
            if (uri.get_host() == 'www.gnome.org' && this._everSeenRedirect) {
 | 
			
		||||
                // Yay, we got to gnome!
 | 
			
		||||
                decision.ignore();
 | 
			
		||||
                this._doneCallback(PortalHelperResult.COMPLETED);
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if (uri.get_host() != CONNECTIVITY_CHECK_HOST) {
 | 
			
		||||
            } else if (uri.get_host() != 'www.gnome.org') {
 | 
			
		||||
                this._everSeenRedirect = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -293,10 +166,6 @@ 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) {
 | 
			
		||||
@@ -328,7 +197,7 @@ const WebPortalHelper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            if (obj.connection == connection) {
 | 
			
		||||
                if (obj.window)
 | 
			
		||||
                    obj.window.destroyWindow();
 | 
			
		||||
                    obj.window.destroy();
 | 
			
		||||
                this._queue.splice(i, 1);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -357,7 +226,7 @@ const WebPortalHelper = new Lang.Class({
 | 
			
		||||
        if (top.window != null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        top.window = new PortalWindow(this, top.url, top.timestamp, Lang.bind(this, function(result) {
 | 
			
		||||
        top.window = new PortalWindow(this, top.uri, top.timestamp, Lang.bind(this, function(result) {
 | 
			
		||||
            this._dbusImpl.emit_signal('Done', new GLib.Variant('(ou)', [top.connection, result]));
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
@@ -370,11 +239,6 @@ function initEnvironment() {
 | 
			
		||||
function main(argv) {
 | 
			
		||||
    initEnvironment();
 | 
			
		||||
 | 
			
		||||
    if (!WebKit.WebContext.new_ephemeral) {
 | 
			
		||||
        log('WebKitGTK 2.16 is required for the portal-helper, see https://bugzilla.gnome.org/show_bug.cgi?id=780453');
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
 | 
			
		||||
    Gettext.textdomain(Config.GETTEXT_PACKAGE);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,202 +0,0 @@
 | 
			
		||||
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,9 +33,10 @@ const AppIconMode = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _createWindowClone(window, size) {
 | 
			
		||||
    let [width, height] = window.get_size();
 | 
			
		||||
    let windowTexture = window.get_texture();
 | 
			
		||||
    let [width, height] = windowTexture.get_size();
 | 
			
		||||
    let scale = Math.min(1.0, size / width, size / height);
 | 
			
		||||
    return new Clutter.Clone({ source: window,
 | 
			
		||||
    return new Clutter.Clone({ source: windowTexture,
 | 
			
		||||
                               width: width * scale,
 | 
			
		||||
                               height: height * scale,
 | 
			
		||||
                               x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
@@ -45,19 +46,6 @@ 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,
 | 
			
		||||
@@ -366,149 +354,6 @@ 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,
 | 
			
		||||
@@ -529,7 +374,7 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _getWindowList: function() {
 | 
			
		||||
        let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
 | 
			
		||||
        return getWindows(workspace);
 | 
			
		||||
        return global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyPressHandler: function(keysym, action) {
 | 
			
		||||
@@ -556,32 +401,6 @@ 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 = this._animations.get_n_children() > 0;
 | 
			
		||||
        this._isLoaded = true;
 | 
			
		||||
 | 
			
		||||
        if (this._isPlaying)
 | 
			
		||||
            this.play();
 | 
			
		||||
 
 | 
			
		||||
@@ -60,18 +60,6 @@ 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)
 | 
			
		||||
@@ -210,14 +198,6 @@ 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() {
 | 
			
		||||
@@ -233,6 +213,14 @@ 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) {
 | 
			
		||||
@@ -899,8 +887,6 @@ 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;
 | 
			
		||||
@@ -911,6 +897,7 @@ const ViewStackLayout = new Lang.Class({
 | 
			
		||||
        this.parent(actor, box, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ViewStackLayout.prototype);
 | 
			
		||||
 | 
			
		||||
const AppDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'AppDisplay',
 | 
			
		||||
@@ -982,36 +969,10 @@ 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.filter(v => v.control.has_style_pseudo_class('checked')).pop().view;
 | 
			
		||||
        let currentView = this._views[global.settings.get_uint('app-picker-view')].view;
 | 
			
		||||
 | 
			
		||||
        // Animate controls opacity using iconGrid animation time, since
 | 
			
		||||
        // it will be the time the AllView or FrequentView takes to show
 | 
			
		||||
@@ -1083,8 +1044,6 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        this.id = 'applications';
 | 
			
		||||
        this.isRemoteProvider = false;
 | 
			
		||||
        this.canLaunchSearch = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(apps, callback) {
 | 
			
		||||
@@ -1253,7 +1212,6 @@ const FolderIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(id, path, parentView) {
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this.name = '';
 | 
			
		||||
        this._parentView = parentView;
 | 
			
		||||
 | 
			
		||||
        this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder',
 | 
			
		||||
@@ -1856,7 +1814,7 @@ const AppIconMenu = new Lang.Class({
 | 
			
		||||
            if (!source.actor.mapped)
 | 
			
		||||
                this.close();
 | 
			
		||||
        }));
 | 
			
		||||
        source.actor.connect('destroy', Lang.bind(this, this.destroy));
 | 
			
		||||
        source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
@@ -1903,19 +1861,6 @@ 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,23 +10,19 @@ 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': 'org.gnome.Calculator.desktop',
 | 
			
		||||
    'geary.desktop': 'org.gnome.Geary.desktop',
 | 
			
		||||
    'gcalctool.desktop': 'gnome-calculator.desktop',
 | 
			
		||||
    'gedit.desktop': 'org.gnome.gedit.desktop',
 | 
			
		||||
    'glchess.desktop': 'gnome-chess.desktop',
 | 
			
		||||
    'glines.desktop': 'five-or-more.desktop',
 | 
			
		||||
    'gnect.desktop': 'four-in-a-row.desktop',
 | 
			
		||||
    'gnibbles.desktop': 'org.gnome.Nibbles.desktop',
 | 
			
		||||
    'gnibbles.desktop': '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',
 | 
			
		||||
    'gnome-font-viewer.desktop': 'org.gnome.font-viewer.desktop',
 | 
			
		||||
    'gnome-nibbles.desktop': 'org.gnome.Nibbles.desktop',
 | 
			
		||||
    'gnome-photos.desktop': 'org.gnome.Photos.desktop',
 | 
			
		||||
    'gnome-screenshot.desktop': 'org.gnome.Screenshot.desktop',
 | 
			
		||||
    'gnome-software.desktop': 'org.gnome.Software.desktop',
 | 
			
		||||
@@ -59,14 +55,12 @@ 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 &&
 | 
			
		||||
                appSys.lookup_app(newId) != null) {
 | 
			
		||||
            if (newId !== undefined) {
 | 
			
		||||
                updated = true;
 | 
			
		||||
                return newId;
 | 
			
		||||
            }
 | 
			
		||||
@@ -76,6 +70,7 @@ 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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,216 +0,0 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
 | 
			
		||||
const AudioDevice = {
 | 
			
		||||
    HEADPHONES: 1 << 0,
 | 
			
		||||
    HEADSET:    1 << 1,
 | 
			
		||||
    MICROPHONE: 1 << 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AudioDeviceSelectionIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.AudioDeviceSelection"> \
 | 
			
		||||
<method name="Open"> \
 | 
			
		||||
    <arg name="devices" direction="in" type="as" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Close"> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="DeviceSelected"> \
 | 
			
		||||
    <arg name="device" type="s" /> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const AudioDeviceSelectionDialog = new Lang.Class({
 | 
			
		||||
    Name: 'AudioDeviceSelectionDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
 | 
			
		||||
    _init: function(devices) {
 | 
			
		||||
        this.parent({ styleClass: 'audio-device-selection-dialog' });
 | 
			
		||||
 | 
			
		||||
        this._deviceItems = {};
 | 
			
		||||
 | 
			
		||||
        this._buildLayout();
 | 
			
		||||
 | 
			
		||||
        if (devices & AudioDevice.HEADPHONES)
 | 
			
		||||
            this._addDevice(AudioDevice.HEADPHONES);
 | 
			
		||||
        if (devices & AudioDevice.HEADSET)
 | 
			
		||||
            this._addDevice(AudioDevice.HEADSET);
 | 
			
		||||
        if (devices & AudioDevice.MICROPHONE)
 | 
			
		||||
            this._addDevice(AudioDevice.MICROPHONE);
 | 
			
		||||
 | 
			
		||||
        if (this._selectionBox.get_n_children() < 2)
 | 
			
		||||
            throw new Error('Too few devices for a selection');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildLayout: function(devices) {
 | 
			
		||||
        let title = new St.Label({ style_class: 'audio-selection-title',
 | 
			
		||||
                                   text: _("Select Audio Device"),
 | 
			
		||||
                                   x_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
 | 
			
		||||
        this.contentLayout.style_class = 'audio-selection-content';
 | 
			
		||||
        this.contentLayout.add(title);
 | 
			
		||||
 | 
			
		||||
        this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' });
 | 
			
		||||
        this.contentLayout.add(this._selectionBox, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this.addButton({ action: Lang.bind(this, this._openSettings),
 | 
			
		||||
                         label: _("Sound Settings") });
 | 
			
		||||
        this.addButton({ action: Lang.bind(this, this.close),
 | 
			
		||||
                         label: _("Cancel"),
 | 
			
		||||
                         key: Clutter.Escape });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDeviceLabel: function(device) {
 | 
			
		||||
        switch(device) {
 | 
			
		||||
            case AudioDevice.HEADPHONES:
 | 
			
		||||
                return _("Headphones");
 | 
			
		||||
            case AudioDevice.HEADSET:
 | 
			
		||||
                return _("Headset");
 | 
			
		||||
            case AudioDevice.MICROPHONE:
 | 
			
		||||
                return _("Microphone");
 | 
			
		||||
            default:
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDeviceIcon: function(device) {
 | 
			
		||||
        switch(device) {
 | 
			
		||||
            case AudioDevice.HEADPHONES:
 | 
			
		||||
                return 'audio-headphones-symbolic';
 | 
			
		||||
            case AudioDevice.HEADSET:
 | 
			
		||||
                return 'audio-headset-symbolic';
 | 
			
		||||
            case AudioDevice.MICROPHONE:
 | 
			
		||||
                return 'audio-input-microphone-symbolic';
 | 
			
		||||
            default:
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addDevice: function(device) {
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
 | 
			
		||||
                                     vertical: true });
 | 
			
		||||
        box.connect('notify::height',
 | 
			
		||||
            function() {
 | 
			
		||||
                Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
 | 
			
		||||
                    function() {
 | 
			
		||||
                        box.width = box.height;
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ style_class: 'audio-selection-device-icon',
 | 
			
		||||
                                 icon_name: this._getDeviceIcon(device) });
 | 
			
		||||
        box.add(icon);
 | 
			
		||||
 | 
			
		||||
        let label = new St.Label({ style_class: 'audio-selection-device-label',
 | 
			
		||||
                                   text: this._getDeviceLabel(device),
 | 
			
		||||
                                   x_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        box.add(label);
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ style_class: 'audio-selection-device',
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     child: box });
 | 
			
		||||
        this._selectionBox.add(button);
 | 
			
		||||
 | 
			
		||||
        button.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.emit('device-selected', device);
 | 
			
		||||
                this.close();
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _openSettings: function() {
 | 
			
		||||
        let desktopFile = 'gnome-sound-panel.desktop'
 | 
			
		||||
        let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
 | 
			
		||||
 | 
			
		||||
        if (!app) {
 | 
			
		||||
            log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.close();
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        app.activate();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AudioDeviceSelectionDBus = new Lang.Class({
 | 
			
		||||
    Name: 'AudioDeviceSelectionDBus',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._audioSelectionDialog = null;
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection');
 | 
			
		||||
 | 
			
		||||
        Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDialogClosed: function() {
 | 
			
		||||
        this._audioSelectionDialog = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDeviceSelected: function(dialog, device) {
 | 
			
		||||
        let connection = this._dbusImpl.get_connection();
 | 
			
		||||
        let info = this._dbusImpl.get_info();
 | 
			
		||||
        let deviceName = Object.keys(AudioDevice).filter(
 | 
			
		||||
            function(dev) {
 | 
			
		||||
                return AudioDevice[dev] == device;
 | 
			
		||||
            })[0].toLowerCase();
 | 
			
		||||
        connection.emit_signal(this._audioSelectionDialog._sender,
 | 
			
		||||
                               this._dbusImpl.get_object_path(),
 | 
			
		||||
                               info ? info.name : null,
 | 
			
		||||
                               'DeviceSelected',
 | 
			
		||||
                               GLib.Variant.new('(s)', [deviceName]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    OpenAsync: function(params, invocation) {
 | 
			
		||||
        if (this._audioSelectionDialog) {
 | 
			
		||||
            invocation.return_value(null);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let [deviceNames] = params;
 | 
			
		||||
        let devices = 0;
 | 
			
		||||
        deviceNames.forEach(function(n) {
 | 
			
		||||
            devices |= AudioDevice[n.toUpperCase()];
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let dialog;
 | 
			
		||||
        try {
 | 
			
		||||
            dialog = new AudioDeviceSelectionDialog(devices);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            invocation.return_value(null);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        dialog._sender = invocation.get_sender();
 | 
			
		||||
 | 
			
		||||
        dialog.connect('closed', Lang.bind(this, this._onDialogClosed));
 | 
			
		||||
        dialog.connect('device-selected',
 | 
			
		||||
                       Lang.bind(this, this._onDeviceSelected));
 | 
			
		||||
        dialog.open();
 | 
			
		||||
 | 
			
		||||
        this._audioSelectionDialog = dialog;
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    CloseAsync: function(params, invocation) {
 | 
			
		||||
        if (this._audioSelectionDialog &&
 | 
			
		||||
            this._audioSelectionDialog._sender == invocation.get_sender())
 | 
			
		||||
            this._audioSelectionDialog.close();
 | 
			
		||||
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -102,7 +102,6 @@ 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;
 | 
			
		||||
@@ -142,6 +141,7 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
    Name: 'BackgroundCache',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._pendingFileLoads = [];
 | 
			
		||||
        this._fileMonitors = {};
 | 
			
		||||
        this._backgroundSources = {};
 | 
			
		||||
        this._animations = {};
 | 
			
		||||
@@ -166,8 +166,7 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                                        settingsSchema: null,
 | 
			
		||||
                                        onLoaded: null });
 | 
			
		||||
 | 
			
		||||
        let animation = this._animations[params.settingsSchema];
 | 
			
		||||
        if (animation && _fileEqual0(animation.file, params.file)) {
 | 
			
		||||
        if (this._animations[params.settingsSchema] && _fileEqual0(this._animationFile, params.file)) {
 | 
			
		||||
            if (params.onLoaded) {
 | 
			
		||||
                let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                    params.onLoaded(this._animations[params.settingsSchema]);
 | 
			
		||||
@@ -178,7 +177,7 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        animation = new Animation({ file: params.file });
 | 
			
		||||
        let animation = new Animation({ file: params.file });
 | 
			
		||||
 | 
			
		||||
        animation.load(Lang.bind(this, function() {
 | 
			
		||||
                           this._animations[params.settingsSchema] = animation;
 | 
			
		||||
@@ -255,14 +254,6 @@ 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');
 | 
			
		||||
                                        }));
 | 
			
		||||
@@ -285,26 +276,16 @@ 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._refreshAnimation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _refreshAnimation: function() {
 | 
			
		||||
        if (!this._animation)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._removeAnimationTimeout();
 | 
			
		||||
        this._updateAnimation();
 | 
			
		||||
        if (this._animation) {
 | 
			
		||||
            this._removeAnimationTimeout();
 | 
			
		||||
            this._updateAnimation();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setLoaded: function() {
 | 
			
		||||
@@ -382,9 +363,11 @@ 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)
 | 
			
		||||
@@ -713,7 +696,6 @@ const BackgroundManager = new Lang.Class({
 | 
			
		||||
                           time: FADE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: function() {
 | 
			
		||||
                               oldBackgroundActor.background.run_dispose();
 | 
			
		||||
                               oldBackgroundActor.destroy();
 | 
			
		||||
                           }
 | 
			
		||||
                         });
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -64,8 +64,7 @@ function startAppForMount(app, mount) {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        retval = app.launch(files, 
 | 
			
		||||
                            global.create_app_launch_context(0, -1),
 | 
			
		||||
                            false)
 | 
			
		||||
                            global.create_app_launch_context(0, -1))
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        log('Unable to launch the application ' + app.get_name()
 | 
			
		||||
            + ': ' + e.toString());
 | 
			
		||||
 
 | 
			
		||||
@@ -615,14 +615,6 @@ 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));
 | 
			
		||||
 | 
			
		||||
@@ -773,8 +765,9 @@ 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 = this._pluginDir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
            let fileEnum = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
            let info;
 | 
			
		||||
 | 
			
		||||
            while ((info = fileEnum.next_file(null))) {
 | 
			
		||||
@@ -784,7 +777,7 @@ const NetworkAgent = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    let keyfile = new GLib.KeyFile();
 | 
			
		||||
                    keyfile.load_from_file(this._pluginDir.get_child(name).get_path(), GLib.KeyFileFlags.NONE);
 | 
			
		||||
                    keyfile.load_from_file(dir.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;
 | 
			
		||||
@@ -803,21 +796,13 @@ 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 };
 | 
			
		||||
                        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 {
 | 
			
		||||
                    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, this._pluginDir.get_child(name).get_path()));
 | 
			
		||||
                        format(e.message, dir.get_child(name).get_path()));
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -298,7 +298,7 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
                 * requested authentication was not gained; this can happen
 | 
			
		||||
                 * because of an authentication error (like invalid password),
 | 
			
		||||
                 * for instance. */
 | 
			
		||||
                this._errorMessageLabel.set_text(_("Sorry, that didn’t work. Please try again."));
 | 
			
		||||
                this._errorMessageLabel.set_text(_("Sorry, that didn\'t work. Please try again."));
 | 
			
		||||
                this._errorMessageLabel.show();
 | 
			
		||||
                this._infoMessageLabel.hide();
 | 
			
		||||
                this._nullMessageLabel.hide();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,28 +6,20 @@ const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Tpl = imports.gi.TelepathyLogger;
 | 
			
		||||
const Tp = imports.gi.TelepathyGLib;
 | 
			
		||||
 | 
			
		||||
var Tpl = null;
 | 
			
		||||
var Tp = null;
 | 
			
		||||
try {
 | 
			
		||||
    Tpl = imports.gi.TelepathyLogger;
 | 
			
		||||
    Tp = imports.gi.TelepathyGLib;
 | 
			
		||||
} catch(e) {
 | 
			
		||||
    log('Telepathy is not available, chat integration will be disabled.');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
const History = imports.misc.history;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageList = imports.ui.messageList;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const HAVE_TP = (Tp != null && Tpl != null);
 | 
			
		||||
 | 
			
		||||
// See Notification.appendMessage
 | 
			
		||||
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
 | 
			
		||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
 | 
			
		||||
@@ -79,43 +71,8 @@ function makeMessageFromTplEvent(event) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TelepathyComponent = new Lang.Class({
 | 
			
		||||
    Name: 'TelepathyComponent',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._client = null;
 | 
			
		||||
 | 
			
		||||
        if (!HAVE_TP)
 | 
			
		||||
            return; // Telepathy isn't available
 | 
			
		||||
 | 
			
		||||
        this._client = new TelepathyClient();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        if (!this._client)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this._client.register();
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._client.account_manager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
 | 
			
		||||
            this._client.account_manager.prepare_async(null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        if (!this._client)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._client.unregister();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
const TelepathyClient = new Lang.Class({
 | 
			
		||||
    Name: 'TelepathyClient',
 | 
			
		||||
    Extends: Tp.BaseClient,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        // channel path -> ChatSource
 | 
			
		||||
@@ -140,28 +97,39 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
        // channel matching its filters is detected.
 | 
			
		||||
        // The second argument, recover, means _observeChannels will be run
 | 
			
		||||
        // for any existing channel as well.
 | 
			
		||||
        this.parent({ name: 'GnomeShell',
 | 
			
		||||
                      account_manager: this._accountManager,
 | 
			
		||||
                      uniquify_name: true });
 | 
			
		||||
 | 
			
		||||
        // We only care about single-user text-based chats
 | 
			
		||||
        let filter = {};
 | 
			
		||||
        filter[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
 | 
			
		||||
        filter[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE] = Tp.HandleType.CONTACT;
 | 
			
		||||
 | 
			
		||||
        this.set_observer_recover(true);
 | 
			
		||||
        this.add_observer_filter(filter);
 | 
			
		||||
        this.add_approver_filter(filter);
 | 
			
		||||
        this.add_handler_filter(filter);
 | 
			
		||||
        this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
 | 
			
		||||
                                              account_manager: this._accountManager,
 | 
			
		||||
                                              uniquify_name: true });
 | 
			
		||||
        this._tpClient.set_observe_channels_func(
 | 
			
		||||
            Lang.bind(this, this._observeChannels));
 | 
			
		||||
        this._tpClient.set_approve_channels_func(
 | 
			
		||||
            Lang.bind(this, this._approveChannels));
 | 
			
		||||
        this._tpClient.set_handle_channels_func(
 | 
			
		||||
            Lang.bind(this, this._handleChannels));
 | 
			
		||||
 | 
			
		||||
        // Allow other clients (such as Empathy) to pre-empt our channels if
 | 
			
		||||
        // needed
 | 
			
		||||
        this.set_delegated_channels_callback(
 | 
			
		||||
        this._tpClient.set_delegated_channels_callback(
 | 
			
		||||
            Lang.bind(this, this._delegatedChannelsCb));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_observe_channels: function(account, conn, channels,
 | 
			
		||||
                                     dispatchOp, requests, context) {
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        try {
 | 
			
		||||
            this._tpClient.register();
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
 | 
			
		||||
            this._accountManager.prepare_async(null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        this._tpClient.unregister();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _observeChannels: function(observer, account, conn, channels,
 | 
			
		||||
                               dispatchOp, requests, context) {
 | 
			
		||||
        let len = channels.length;
 | 
			
		||||
        for (let i = 0; i < len; i++) {
 | 
			
		||||
            let channel = channels[i];
 | 
			
		||||
@@ -185,7 +153,7 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
        if (this._chatSources[channel.get_object_path()])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let source = new ChatSource(account, conn, channel, contact, this);
 | 
			
		||||
        let source = new ChatSource(account, conn, channel, contact, this._tpClient);
 | 
			
		||||
 | 
			
		||||
        this._chatSources[channel.get_object_path()] = source;
 | 
			
		||||
        source.connect('destroy', Lang.bind(this,
 | 
			
		||||
@@ -194,8 +162,8 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
                       }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_handle_channels: function(account, conn, channels, requests,
 | 
			
		||||
                                    user_action_time, context) {
 | 
			
		||||
    _handleChannels: function(handler, account, conn, channels,
 | 
			
		||||
                              requests, user_action_time, context) {
 | 
			
		||||
        this._handlingChannels(account, conn, channels, true);
 | 
			
		||||
        context.accept();
 | 
			
		||||
    },
 | 
			
		||||
@@ -225,7 +193,7 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
            // Telepathy spec states that handlers must foreground channels
 | 
			
		||||
            // in HandleChannels calls which are already being handled.
 | 
			
		||||
 | 
			
		||||
            if (notify && this.is_handling_channel(channel)) {
 | 
			
		||||
            if (notify && this._tpClient.is_handling_channel(channel)) {
 | 
			
		||||
                // We are already handling the channel, display the source
 | 
			
		||||
                let source = this._chatSources[channel.get_object_path()];
 | 
			
		||||
                if (source)
 | 
			
		||||
@@ -234,8 +202,8 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_add_dispatch_operation: function(account, conn, channels,
 | 
			
		||||
                                           dispatchOp, context) {
 | 
			
		||||
    _approveChannels: function(approver, account, conn, channels,
 | 
			
		||||
                               dispatchOp, context) {
 | 
			
		||||
        let channel = channels[0];
 | 
			
		||||
        let chanType = channel.get_channel_type();
 | 
			
		||||
 | 
			
		||||
@@ -262,7 +230,7 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Approve private text channels right away as we are going to handle it
 | 
			
		||||
        dispatchOp.claim_with_async(this, Lang.bind(this, function(dispatchOp, result) {
 | 
			
		||||
        dispatchOp.claim_with_async(this._tpClient, Lang.bind(this, function(dispatchOp, result) {
 | 
			
		||||
            try {
 | 
			
		||||
                dispatchOp.claim_with_finish(result);
 | 
			
		||||
                this._handlingChannels(account, conn, [channel], false);
 | 
			
		||||
@@ -278,7 +246,7 @@ const TelepathyClient = HAVE_TP ? new Lang.Class({
 | 
			
		||||
        // Nothing to do as we don't make a distinction between observed and
 | 
			
		||||
        // handled channels.
 | 
			
		||||
    },
 | 
			
		||||
}) : null;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ChatSource = new Lang.Class({
 | 
			
		||||
    Name: 'ChatSource',
 | 
			
		||||
@@ -501,17 +469,11 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    destroy: function(reason) {
 | 
			
		||||
        if (this._client.is_handling_channel(this._channel)) {
 | 
			
		||||
            this._ackMessages();
 | 
			
		||||
            // The chat box has been destroyed so it can't
 | 
			
		||||
            // handle the channel any more.
 | 
			
		||||
            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
 | 
			
		||||
@@ -692,9 +654,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (message.direction == NotificationDirection.RECEIVED)
 | 
			
		||||
            this.update(this.source.title, messageBody,
 | 
			
		||||
                        { datetime: GLib.DateTime.new_from_unix_local (message.timestamp),
 | 
			
		||||
                          bannerMarkup: true });
 | 
			
		||||
            this.update(this.source.title, messageBody, { bannerMarkup: true });
 | 
			
		||||
 | 
			
		||||
        let group = (message.direction == NotificationDirection.RECEIVED ?
 | 
			
		||||
                     'received' : 'sent');
 | 
			
		||||
@@ -906,7 +866,7 @@ const ChatNotificationBanner = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addMessage: function(message) {
 | 
			
		||||
        let highlighter = new MessageList.URLHighlighter(message.body, true, true);
 | 
			
		||||
        let highlighter = new Calendar.URLHighlighter(message.body, true, true);
 | 
			
		||||
        let body = highlighter.actor;
 | 
			
		||||
 | 
			
		||||
        let styles = message.styles;
 | 
			
		||||
@@ -996,4 +956,4 @@ const ChatNotificationBanner = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Component = TelepathyComponent;
 | 
			
		||||
const Component = TelepathyClient;
 | 
			
		||||
 
 | 
			
		||||
@@ -259,7 +259,7 @@ const ShowAppsIcon = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIcon: function(size) {
 | 
			
		||||
        this._iconActor = new St.Icon({ icon_name: 'view-app-grid-symbolic',
 | 
			
		||||
        this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
 | 
			
		||||
                                        icon_size: size,
 | 
			
		||||
                                        style_class: 'show-apps-icon',
 | 
			
		||||
                                        track_hover: true });
 | 
			
		||||
@@ -756,44 +756,42 @@ const Dash = new Lang.Class({
 | 
			
		||||
        let newIndex = 0;
 | 
			
		||||
        let oldIndex = 0;
 | 
			
		||||
        while (newIndex < newApps.length || oldIndex < oldApps.length) {
 | 
			
		||||
            let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null;
 | 
			
		||||
            let newApp = newApps.length > newIndex ? newApps[newIndex] : null;
 | 
			
		||||
 | 
			
		||||
            // No change at oldIndex/newIndex
 | 
			
		||||
            if (oldApp == newApp) {
 | 
			
		||||
            if (oldApps[oldIndex] == newApps[newIndex]) {
 | 
			
		||||
                oldIndex++;
 | 
			
		||||
                newIndex++;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // App removed at oldIndex
 | 
			
		||||
            if (oldApp && newApps.indexOf(oldApp) == -1) {
 | 
			
		||||
            if (oldApps[oldIndex] &&
 | 
			
		||||
                newApps.indexOf(oldApps[oldIndex]) == -1) {
 | 
			
		||||
                removedActors.push(children[oldIndex]);
 | 
			
		||||
                oldIndex++;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // App added at newIndex
 | 
			
		||||
            if (newApp && oldApps.indexOf(newApp) == -1) {
 | 
			
		||||
                addedItems.push({ app: newApp,
 | 
			
		||||
                                  item: this._createAppItem(newApp),
 | 
			
		||||
            if (newApps[newIndex] &&
 | 
			
		||||
                oldApps.indexOf(newApps[newIndex]) == -1) {
 | 
			
		||||
                addedItems.push({ app: newApps[newIndex],
 | 
			
		||||
                                  item: this._createAppItem(newApps[newIndex]),
 | 
			
		||||
                                  pos: newIndex });
 | 
			
		||||
                newIndex++;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // App moved
 | 
			
		||||
            let nextApp = newApps.length > newIndex + 1 ? newApps[newIndex + 1]
 | 
			
		||||
                                                        : null;
 | 
			
		||||
            let insertHere = nextApp && nextApp == oldApp;
 | 
			
		||||
            let insertHere = newApps[newIndex + 1] &&
 | 
			
		||||
                             newApps[newIndex + 1] == oldApps[oldIndex];
 | 
			
		||||
            let alreadyRemoved = removedActors.reduce(function(result, actor) {
 | 
			
		||||
                let removedApp = actor.child._delegate.app;
 | 
			
		||||
                return result || removedApp == newApp;
 | 
			
		||||
                return result || removedApp == newApps[newIndex];
 | 
			
		||||
            }, false);
 | 
			
		||||
 | 
			
		||||
            if (insertHere || alreadyRemoved) {
 | 
			
		||||
                let newItem = this._createAppItem(newApp);
 | 
			
		||||
                addedItems.push({ app: newApp,
 | 
			
		||||
                let newItem = this._createAppItem(newApps[newIndex]);
 | 
			
		||||
                addedItems.push({ app: newApps[newIndex],
 | 
			
		||||
                                  item: newItem,
 | 
			
		||||
                                  pos: newIndex + removedActors.length });
 | 
			
		||||
                newIndex++;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ const Gtk = imports.gi.Gtk;
 | 
			
		||||
const GWeather = imports.gi.GWeather;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
@@ -21,7 +20,6 @@ const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
const Weather = imports.misc.weather;
 | 
			
		||||
 | 
			
		||||
function _isToday(date) {
 | 
			
		||||
    let now = new Date();
 | 
			
		||||
@@ -38,7 +36,7 @@ const TodayButton = new Lang.Class({
 | 
			
		||||
        // on the current date can be confusing. So don't make the button reactive
 | 
			
		||||
        // until the selected date changes.
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'datemenu-today-button',
 | 
			
		||||
                                     x_expand: true, x_align: St.Align.START,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     reactive: false
 | 
			
		||||
                                   });
 | 
			
		||||
@@ -79,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".
 | 
			
		||||
         */
 | 
			
		||||
        dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
 | 
			
		||||
        let dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
 | 
			
		||||
        this.actor.accessible_name = date.toLocaleFormat(dateFormat);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -89,7 +87,9 @@ const WorldClocksSection = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._clock = new GnomeDesktop.WallClock();
 | 
			
		||||
        this._settings = null;
 | 
			
		||||
        this._clockNotifyId = 0;
 | 
			
		||||
        this._changedId = 0;
 | 
			
		||||
 | 
			
		||||
        this._locations = [];
 | 
			
		||||
 | 
			
		||||
@@ -98,7 +98,8 @@ const WorldClocksSection = new Lang.Class({
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._clockAppMon.activateApp();
 | 
			
		||||
                let app = this._getClockApp();
 | 
			
		||||
                app.activate();
 | 
			
		||||
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
                Main.panel.closeCalendar();
 | 
			
		||||
@@ -111,25 +112,40 @@ const WorldClocksSection = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.child = this._grid;
 | 
			
		||||
 | 
			
		||||
        this._clockAppMon = new Util.AppSettingsMonitor('org.gnome.clocks.desktop',
 | 
			
		||||
                                                        'org.gnome.clocks');
 | 
			
		||||
        this._clockAppMon.connect('available-changed',
 | 
			
		||||
                                  Lang.bind(this, this._sync));
 | 
			
		||||
        this._clockAppMon.watchSetting('world-clocks',
 | 
			
		||||
                                       Lang.bind(this, this._clocksChanged));
 | 
			
		||||
        Shell.AppSystem.get_default().connect('installed-changed',
 | 
			
		||||
                                              Lang.bind(this, this._sync));
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        this.actor.visible = this._clockAppMon.available;
 | 
			
		||||
    _getClockApp: function() {
 | 
			
		||||
        return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clocksChanged: function(settings) {
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        this.actor.visible = (this._getClockApp() != null);
 | 
			
		||||
 | 
			
		||||
        if (this.actor.visible) {
 | 
			
		||||
            if (!this._settings) {
 | 
			
		||||
                this._settings = new Gio.Settings({ schema_id: 'org.gnome.clocks' });
 | 
			
		||||
                this._changedId =
 | 
			
		||||
                    this._settings.connect('changed::world-clocks',
 | 
			
		||||
                                           Lang.bind(this, this._clocksChanged));
 | 
			
		||||
                this._clocksChanged();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._settings)
 | 
			
		||||
                this._settings.disconnect(this._changedId);
 | 
			
		||||
            this._settings = null;
 | 
			
		||||
            this._changedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clocksChanged: function() {
 | 
			
		||||
        this._grid.destroy_all_children();
 | 
			
		||||
        this._locations = [];
 | 
			
		||||
 | 
			
		||||
        let world = GWeather.Location.get_world();
 | 
			
		||||
        let clocks = settings.get_value('world-clocks').deep_unpack();
 | 
			
		||||
        let clocks = this._settings.get_value('world-clocks').deep_unpack();
 | 
			
		||||
        for (let i = 0; i < clocks.length; i++) {
 | 
			
		||||
            let l = world.deserialize(clocks[i].location);
 | 
			
		||||
            this._locations.push({ location: l });
 | 
			
		||||
@@ -194,136 +210,6 @@ const WorldClocksSection = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const WeatherSection = new Lang.Class({
 | 
			
		||||
    Name: 'WeatherSection',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._weatherClient = new Weather.WeatherClient();
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'weather-button',
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
        this.actor.connect('clicked', () => {
 | 
			
		||||
            this._weatherClient.activateApp();
 | 
			
		||||
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
            Main.panel.closeCalendar();
 | 
			
		||||
        });
 | 
			
		||||
        this.actor.connect('notify::mapped', () => {
 | 
			
		||||
            if (this.actor.mapped)
 | 
			
		||||
                this._weatherClient.update();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'weather-box',
 | 
			
		||||
                                      vertical: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.child = box;
 | 
			
		||||
 | 
			
		||||
        box.add_child(new St.Label({ style_class: 'weather-header',
 | 
			
		||||
                                     x_align: Clutter.ActorAlign.START,
 | 
			
		||||
                                     text: _("Weather") }));
 | 
			
		||||
 | 
			
		||||
        this._conditionsLabel = new St.Label({ style_class: 'weather-conditions',
 | 
			
		||||
                                               x_align: Clutter.ActorAlign.START });
 | 
			
		||||
        this._conditionsLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._conditionsLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        box.add_child(this._conditionsLabel);
 | 
			
		||||
 | 
			
		||||
        this._weatherClient.connect('changed', Lang.bind(this, this._sync));
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSummary: function(info) {
 | 
			
		||||
        let summary = info.get_conditions();
 | 
			
		||||
        if (summary == '-')
 | 
			
		||||
            return info.get_sky();
 | 
			
		||||
        return summary;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sameSummary: function(info1, info2) {
 | 
			
		||||
        let [ok1, phenom1, qualifier1] = info1.get_value_conditions();
 | 
			
		||||
        let [ok2, phenom2, qualifier2] = info2.get_value_conditions();
 | 
			
		||||
        if (ok1 || ok2)
 | 
			
		||||
            return ok1 == ok2 && phenom1 == phenom2 && qualifier1 == qualifier2;
 | 
			
		||||
 | 
			
		||||
        let [, sky1] = info1.get_value_sky();
 | 
			
		||||
        let [, sky2] = info2.get_value_sky();
 | 
			
		||||
        return sky1 == sky2;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSummaryText: function() {
 | 
			
		||||
        let info = this._weatherClient.info;
 | 
			
		||||
        let forecasts = info.get_forecast_list();
 | 
			
		||||
        if (forecasts.length == 0) // No forecasts, just current conditions
 | 
			
		||||
            return '%s.'.format(this._getSummary(info));
 | 
			
		||||
 | 
			
		||||
        let current = info;
 | 
			
		||||
        let summaries = [this._getSummary(info)];
 | 
			
		||||
        for (let i = 0; i < forecasts.length; i++) {
 | 
			
		||||
            let [ok, timestamp] = forecasts[i].get_value_update();
 | 
			
		||||
            if (!_isToday(new Date(timestamp * 1000)))
 | 
			
		||||
                continue; // Ignore forecasts from other days
 | 
			
		||||
 | 
			
		||||
            if (this._sameSummary(current, forecasts[i]))
 | 
			
		||||
                continue; // Ignore consecutive runs of equal summaries
 | 
			
		||||
 | 
			
		||||
            current = forecasts[i];
 | 
			
		||||
            if (summaries.push(this._getSummary(current)) == 3)
 | 
			
		||||
                break; // Use a maximum of three summaries
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let fmt;
 | 
			
		||||
        switch(summaries.length) {
 | 
			
		||||
            /* Translators: %s is a weather condition like "Clear sky"; see
 | 
			
		||||
               libgweather for the possible condition strings. If at all
 | 
			
		||||
               possible, the sentence should match the grammatical case etc. of
 | 
			
		||||
               the inserted conditions. */
 | 
			
		||||
            case 1: fmt = _("%s all day."); break;
 | 
			
		||||
 | 
			
		||||
            /* Translators: %s is a weather condition like "Clear sky"; see
 | 
			
		||||
               libgweather for the possible condition strings. If at all
 | 
			
		||||
               possible, the sentence should match the grammatical case etc. of
 | 
			
		||||
               the inserted conditions. */
 | 
			
		||||
            case 2: fmt = _("%s, then %s later."); break;
 | 
			
		||||
 | 
			
		||||
            /* Translators: %s is a weather condition like "Clear sky"; see
 | 
			
		||||
               libgweather for the possible condition strings. If at all
 | 
			
		||||
               possible, the sentence should match the grammatical case etc. of
 | 
			
		||||
               the inserted conditions. */
 | 
			
		||||
            case 3: fmt = _("%s, then %s, followed by %s later."); break;
 | 
			
		||||
        }
 | 
			
		||||
        return String.prototype.format.apply(fmt, summaries);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getLabelText: function() {
 | 
			
		||||
        if (!this._weatherClient.hasLocation)
 | 
			
		||||
            return _("Select a location…");
 | 
			
		||||
 | 
			
		||||
        if (this._weatherClient.loading)
 | 
			
		||||
            return _("Loading…");
 | 
			
		||||
 | 
			
		||||
        let info = this._weatherClient.info;
 | 
			
		||||
        if (info.is_valid())
 | 
			
		||||
            return this._getSummaryText() + ' ' +
 | 
			
		||||
                   /* Translators: %s is a temperature with unit, e.g. "23℃" */
 | 
			
		||||
                   _("Feels like %s.").format(info.get_apparent());
 | 
			
		||||
 | 
			
		||||
        if (info.network_error())
 | 
			
		||||
            return _("Go online for weather information");
 | 
			
		||||
 | 
			
		||||
        return _("Weather information is currently unavailable");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        this.actor.visible = this._weatherClient.available;
 | 
			
		||||
 | 
			
		||||
        if (!this.actor.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._conditionsLabel.text = this._getLabelText();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const MessagesIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'MessagesIndicator',
 | 
			
		||||
 | 
			
		||||
@@ -370,7 +256,8 @@ const IndicatorPad = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(actor) {
 | 
			
		||||
        this._source = actor;
 | 
			
		||||
        this._source.connect('notify::visible', () => { this.queue_relayout(); });
 | 
			
		||||
        this._source.connect('notify::visible',
 | 
			
		||||
                             Lang.bind(this, this.queue_relayout));
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -410,38 +297,14 @@ const FreezableBinLayout = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(container, forHeight) {
 | 
			
		||||
        if (!this._frozen || this._savedWidth.some(isNaN))
 | 
			
		||||
            return this.parent(container, forHeight);
 | 
			
		||||
            this._savedWidth = this.parent(container, forHeight);
 | 
			
		||||
        return this._savedWidth;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(container, forWidth) {
 | 
			
		||||
        if (!this._frozen || this._savedHeight.some(isNaN))
 | 
			
		||||
            return this.parent(container, forWidth);
 | 
			
		||||
            this._savedHeight = this.parent(container, forWidth);
 | 
			
		||||
        return this._savedHeight;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, allocation, flags) {
 | 
			
		||||
        this.parent(container, allocation, flags);
 | 
			
		||||
 | 
			
		||||
        let [width, height] = allocation.get_size();
 | 
			
		||||
        this._savedWidth = [width, width];
 | 
			
		||||
        this._savedHeight = [height, height];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const CalendarColumnLayout = new Lang.Class({
 | 
			
		||||
    Name: 'CalendarColumnLayout',
 | 
			
		||||
    Extends: Clutter.BoxLayout,
 | 
			
		||||
 | 
			
		||||
    _init: function(actor) {
 | 
			
		||||
        this.parent({ orientation: Clutter.Orientation.VERTICAL });
 | 
			
		||||
        this._calActor = actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(container, forHeight) {
 | 
			
		||||
        if (!this._calActor || this._calActor.get_parent() != container)
 | 
			
		||||
            return this.parent(container, forHeight);
 | 
			
		||||
        return this._calActor.get_preferred_width(forHeight);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -474,8 +337,6 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let layout = new FreezableBinLayout();
 | 
			
		||||
        let bin = new St.Widget({ layout_manager: layout });
 | 
			
		||||
        // For some minimal compatibility with PopupMenuItem
 | 
			
		||||
        bin._delegate = this;
 | 
			
		||||
        this.menu.box.add_child(bin);
 | 
			
		||||
 | 
			
		||||
        hbox = new St.BoxLayout({ name: 'calendarArea' });
 | 
			
		||||
@@ -499,20 +360,18 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Fill up the first column
 | 
			
		||||
        this._messageList = new Calendar.CalendarMessageList();
 | 
			
		||||
        this._messageList = new Calendar.MessageList();
 | 
			
		||||
        hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        // Fill up the second column
 | 
			
		||||
        let boxLayout = new CalendarColumnLayout(this._calendar.actor);
 | 
			
		||||
        vbox = new St.Widget({ style_class: 'datemenu-calendar-column',
 | 
			
		||||
                               layout_manager: boxLayout });
 | 
			
		||||
        boxLayout.hookup_style(vbox);
 | 
			
		||||
        vbox = new St.BoxLayout({ style_class: 'datemenu-calendar-column',
 | 
			
		||||
                                  vertical: true });
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        this._date = new TodayButton(this._calendar);
 | 
			
		||||
        vbox.add_actor(this._date.actor);
 | 
			
		||||
 | 
			
		||||
        vbox.add_actor(this._calendar.actor);
 | 
			
		||||
        vbox.add(this._calendar.actor);
 | 
			
		||||
 | 
			
		||||
        this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade',
 | 
			
		||||
                                                    x_expand: true, x_fill: true,
 | 
			
		||||
@@ -527,8 +386,6 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        this._clocksItem = new WorldClocksSection();
 | 
			
		||||
        displaysBox.add(this._clocksItem.actor, { x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._weatherItem = new WeatherSection();
 | 
			
		||||
        displaysBox.add(this._weatherItem.actor, { x_fill: true });
 | 
			
		||||
 | 
			
		||||
        // Done with hbox for calendar and event list
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							@@ -571,13 +571,20 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._animateDragEnd(eventTime,
 | 
			
		||||
                             { x: snapBackX,
 | 
			
		||||
                               y: snapBackY,
 | 
			
		||||
                               scale_x: snapBackScale,
 | 
			
		||||
                               scale_y: snapBackScale,
 | 
			
		||||
                               time: SNAP_BACK_ANIMATION_TIME,
 | 
			
		||||
                             });
 | 
			
		||||
        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]
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _restoreDragActor: function(eventTime) {
 | 
			
		||||
@@ -589,44 +596,18 @@ 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;
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
        Tweener.addTween(this._dragActor,
 | 
			
		||||
                         { opacity: this._dragOrigOpacity,
 | 
			
		||||
                           time: REVERT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: this._onAnimationComplete,
 | 
			
		||||
                           onCompleteScope: this,
 | 
			
		||||
                           onCompleteParams: [this._dragActor, eventTime]
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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);
 | 
			
		||||
@@ -635,9 +616,12 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        } else {
 | 
			
		||||
            dragActor.destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
        this.emit('drag-end', eventTime, false);
 | 
			
		||||
        this._finishAnimation();
 | 
			
		||||
 | 
			
		||||
        this._animationInProgress = false;
 | 
			
		||||
        if (!this._buttonDown)
 | 
			
		||||
            this._dragComplete();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragComplete: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ const DRAG_DISTANCE = 80;
 | 
			
		||||
const EdgeDragAction = new Lang.Class({
 | 
			
		||||
    Name: 'EdgeDragAction',
 | 
			
		||||
    Extends: Clutter.GestureAction,
 | 
			
		||||
    Signals: { 'activated': {} },
 | 
			
		||||
 | 
			
		||||
    _init : function(side, allowedModes) {
 | 
			
		||||
        this.parent();
 | 
			
		||||
@@ -82,3 +81,4 @@ 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-2016 Red Hat, Inc
 | 
			
		||||
 * Copyright 2010 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 restartUpdateDialogContent = {
 | 
			
		||||
const restartInstallDialogContent = {
 | 
			
		||||
 | 
			
		||||
    subject: C_("title", "Restart & Install Updates"),
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
@@ -132,38 +132,18 @@ const restartUpdateDialogContent = {
 | 
			
		||||
    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,
 | 
			
		||||
  UPGRADE_RESTART: 4
 | 
			
		||||
  UPDATE_RESTART: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DialogContent = {
 | 
			
		||||
    0 /* DialogType.LOGOUT */: logoutDialogContent,
 | 
			
		||||
    1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
 | 
			
		||||
    2 /* DialogType.RESTART */: restartDialogContent,
 | 
			
		||||
    3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent,
 | 
			
		||||
    4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent
 | 
			
		||||
    3 /* DialogType.UPDATE_RESTART */: restartInstallDialogContent
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
 | 
			
		||||
@@ -183,10 +163,7 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
 | 
			
		||||
const PkOfflineIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.PackageKit.Offline"> \
 | 
			
		||||
    <property name="UpdatePrepared" type="b" 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"/> \
 | 
			
		||||
    <property name="TriggerAction" type="s" access="read"/> \
 | 
			
		||||
    <method name="Trigger"> \
 | 
			
		||||
        <arg type="s" name="action" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
@@ -438,25 +415,18 @@ 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,
 | 
			
		||||
@@ -728,12 +698,9 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this._totalSecondsToStayOpen = totalSecondsToStayOpen;
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        if (this._type == DialogType.RESTART) {
 | 
			
		||||
            if (this._pkOfflineProxy.UpdateTriggered)
 | 
			
		||||
                this._type = DialogType.UPDATE_RESTART;
 | 
			
		||||
            else if (this._pkOfflineProxy.UpgradeTriggered)
 | 
			
		||||
                this._type = DialogType.UPGRADE_RESTART;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._type == DialogType.RESTART &&
 | 
			
		||||
            this._pkOfflineProxy.TriggerAction == 'reboot')
 | 
			
		||||
            this._type = DialogType.UPDATE_RESTART;
 | 
			
		||||
 | 
			
		||||
        this._applications = [];
 | 
			
		||||
        this._applicationList.destroy_all_children();
 | 
			
		||||
@@ -760,19 +727,19 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        if (dialogContent.showOtherSessions)
 | 
			
		||||
            this._loadSessions();
 | 
			
		||||
 | 
			
		||||
        let updateTriggered = this._pkOfflineProxy.UpdateTriggered;
 | 
			
		||||
        let updateAlreadyTriggered = this._pkOfflineProxy.TriggerAction == 'power-off' || this._pkOfflineProxy.TriggerAction == 'reboot';
 | 
			
		||||
        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 && updateTriggered);
 | 
			
		||||
        this._checkBox.actor.checked = (updatePrepared && updateAlreadyTriggered);
 | 
			
		||||
 | 
			
		||||
        // 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 && updateTriggered && !updatesAllowed));
 | 
			
		||||
                                        (this._checkBox.actor.visible || updatePrepared && updateAlreadyTriggered && !updatesAllowed));
 | 
			
		||||
 | 
			
		||||
        this._updateButtons();
 | 
			
		||||
 | 
			
		||||
@@ -782,9 +749,7 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!dialogContent.disableTimer)
 | 
			
		||||
            this._startTimer();
 | 
			
		||||
 | 
			
		||||
        this._startTimer();
 | 
			
		||||
        this._sync();
 | 
			
		||||
 | 
			
		||||
        let signalId = this.connect('opened',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
 | 
			
		||||
imports.gi.versions.Clutter = Config.LIBMUTTER_API_VERSION;
 | 
			
		||||
imports.gi.versions.Clutter = '1.0';
 | 
			
		||||
imports.gi.versions.Gio = '2.0';
 | 
			
		||||
imports.gi.versions.Gdk = '3.0';
 | 
			
		||||
imports.gi.versions.GdkPixbuf = '2.0';
 | 
			
		||||
@@ -63,19 +61,10 @@ function _patchLayoutClass(layoutClass, styleProps) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 _makeLoggingFunc(func) {
 | 
			
		||||
    return function() {
 | 
			
		||||
        return func([].join.call(arguments, ', '));
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
@@ -83,7 +72,7 @@ function init() {
 | 
			
		||||
    // browser convention of having that namespace be called 'window'.)
 | 
			
		||||
    window.global = Shell.Global.get();
 | 
			
		||||
 | 
			
		||||
    window.log = _loggingFunc;
 | 
			
		||||
    window.log = _makeLoggingFunc(window.log);
 | 
			
		||||
 | 
			
		||||
    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,7 +38,6 @@ 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;
 | 
			
		||||
@@ -239,16 +238,11 @@ function initExtension(uuid) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getEnabledExtensions() {
 | 
			
		||||
    let extensions;
 | 
			
		||||
    if (Array.isArray(Main.sessionMode.enabledExtensions))
 | 
			
		||||
        extensions = Main.sessionMode.enabledExtensions;
 | 
			
		||||
    else
 | 
			
		||||
        extensions = [];
 | 
			
		||||
 | 
			
		||||
    if (global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY))
 | 
			
		||||
    let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
 | 
			
		||||
    if (!Array.isArray(Main.sessionMode.enabledExtensions))
 | 
			
		||||
        return extensions;
 | 
			
		||||
 | 
			
		||||
    return extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY));
 | 
			
		||||
    return Main.sessionMode.enabledExtensions.concat(extensions);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onEnabledExtensionsChanged() {
 | 
			
		||||
@@ -282,27 +276,18 @@ function _onVersionValidationChanged() {
 | 
			
		||||
    // temporarily disable them all
 | 
			
		||||
    enabledExtensions = [];
 | 
			
		||||
    for (let uuid in ExtensionUtils.extensions)
 | 
			
		||||
        try {
 | 
			
		||||
            reloadExtension(ExtensionUtils.extensions[uuid]);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
        reloadExtension(ExtensionUtils.extensions[uuid]);
 | 
			
		||||
    enabledExtensions = getEnabledExtensions();
 | 
			
		||||
 | 
			
		||||
    if (Main.sessionMode.allowExtensions) {
 | 
			
		||||
        enabledExtensions.forEach(function(uuid) {
 | 
			
		||||
            try {
 | 
			
		||||
                enableExtension(uuid);
 | 
			
		||||
            } catch(e) {
 | 
			
		||||
                logExtensionError(uuid, e);
 | 
			
		||||
            }
 | 
			
		||||
            enableExtension(uuid);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,7 +19,6 @@ 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) {
 | 
			
		||||
@@ -40,19 +39,6 @@ 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' });
 | 
			
		||||
@@ -158,14 +144,6 @@ 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);
 | 
			
		||||
        }));
 | 
			
		||||
@@ -180,22 +158,10 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        panelService.connect('set-cursor-location',
 | 
			
		||||
                             Lang.bind(this, function(ps, x, y, w, h) {
 | 
			
		||||
                                 this._setDummyCursorGeometry(x, y, w, h);
 | 
			
		||||
                                 Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
 | 
			
		||||
                                 if (this._boxPointer.actor.visible)
 | 
			
		||||
                                     this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
 | 
			
		||||
                             }));
 | 
			
		||||
        try {
 | 
			
		||||
            panelService.connect('set-cursor-location-relative',
 | 
			
		||||
                                 Lang.bind(this, function(ps, x, y, w, h) {
 | 
			
		||||
                                     if (!global.display.focus_window)
 | 
			
		||||
                                         return;
 | 
			
		||||
                                     let window = global.display.focus_window.get_compositor_private();
 | 
			
		||||
                                     this._setDummyCursorGeometry(window.x + x, window.y + y, w, h);
 | 
			
		||||
                                 }));
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            // Only recent IBus versions have support for this signal
 | 
			
		||||
            // which is used for wayland clients. In order to work
 | 
			
		||||
            // with older IBus versions we can silently ignore the
 | 
			
		||||
            // signal's absence.
 | 
			
		||||
        }
 | 
			
		||||
        panelService.connect('update-preedit-text',
 | 
			
		||||
                             Lang.bind(this, function(ps, text, cursorPosition, visible) {
 | 
			
		||||
                                 this._preeditText.visible = visible;
 | 
			
		||||
@@ -280,12 +246,6 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setDummyCursorGeometry: function(x, y, w, h) {
 | 
			
		||||
        Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
 | 
			
		||||
        if (this._boxPointer.actor.visible)
 | 
			
		||||
            this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let isVisible = (this._preeditText.visible ||
 | 
			
		||||
                         this._auxText.visible ||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ 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;
 | 
			
		||||
@@ -570,7 +569,7 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
    shouldTakeEvent: function(event) {
 | 
			
		||||
        let actor = event.get_source();
 | 
			
		||||
        return Main.layoutManager.keyboardBox.contains(actor) ||
 | 
			
		||||
               !!actor._extended_keys || !!actor.extended_key;
 | 
			
		||||
               actor._extended_keys || actor.extended_key;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearKeyboardRestTimer: function() {
 | 
			
		||||
@@ -758,48 +757,19 @@ 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) {
 | 
			
		||||
        this._virtualDevice.notify_keyval(Clutter.get_current_event_time(),
 | 
			
		||||
                                          keyval, Clutter.KeyState.PRESSED);
 | 
			
		||||
        let focus = global.stage.get_key_focus();
 | 
			
		||||
        if (focus instanceof Clutter.Text)
 | 
			
		||||
            Shell.util_text_insert_keyval(focus, keyval);
 | 
			
		||||
        else
 | 
			
		||||
            this.parent(keyval);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_keyval_release: function(keyval) {
 | 
			
		||||
        this._virtualDevice.notify_keyval(Clutter.get_current_event_time(),
 | 
			
		||||
                                          keyval, Clutter.KeyState.RELEASED);
 | 
			
		||||
        let focus = global.stage.get_key_focus();
 | 
			
		||||
        if (focus instanceof Clutter.Text)
 | 
			
		||||
            return;             // do nothing
 | 
			
		||||
        else
 | 
			
		||||
            this.parent(keyval);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -220,8 +220,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        global.stage.add_child(this.uiGroup);
 | 
			
		||||
 | 
			
		||||
        this.overviewGroup = new St.Widget({ name: 'overviewGroup',
 | 
			
		||||
                                             visible: false,
 | 
			
		||||
                                             reactive: true });
 | 
			
		||||
                                             visible: false });
 | 
			
		||||
        this.addChrome(this.overviewGroup);
 | 
			
		||||
 | 
			
		||||
        this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
 | 
			
		||||
@@ -592,10 +591,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.addChrome(this._coverPane);
 | 
			
		||||
 | 
			
		||||
        if (Meta.is_restart()) {
 | 
			
		||||
            // 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();
 | 
			
		||||
            // On restart, we don't do an animation
 | 
			
		||||
        } else if (Main.sessionMode.isGreeter) {
 | 
			
		||||
            this.panelBox.translation_y = -this.panelBox.height;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -896,10 +892,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    findMonitorForActor: function(actor) {
 | 
			
		||||
        let index = this.findIndexForActor(actor);
 | 
			
		||||
        if (index >= 0 && index < this.monitors.length)
 | 
			
		||||
            return this.monitors[index];
 | 
			
		||||
        return null;
 | 
			
		||||
        return this.monitors[this.findIndexForActor(actor)];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueUpdateRegions: function() {
 | 
			
		||||
@@ -945,11 +938,6 @@ 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;
 | 
			
		||||
@@ -969,11 +957,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
            if (actorData.affectsInputRegion && wantsInputRegion && actorData.actor.get_paint_visibility())
 | 
			
		||||
                rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h }));
 | 
			
		||||
 | 
			
		||||
            let monitor = null;
 | 
			
		||||
            if (actorData.affectsStruts)
 | 
			
		||||
                monitor = this.findMonitorForActor(actorData.actor);
 | 
			
		||||
 | 
			
		||||
            if (monitor) {
 | 
			
		||||
            if (actorData.affectsStruts) {
 | 
			
		||||
                // Limit struts to the size of the screen
 | 
			
		||||
                let x1 = Math.max(x, 0);
 | 
			
		||||
                let x2 = Math.min(x + w, global.screen_width);
 | 
			
		||||
@@ -990,6 +974,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
                // spans the width/height across the middle of the
 | 
			
		||||
                // screen, then we don't create a strut for it at all.
 | 
			
		||||
 | 
			
		||||
                let monitor = this.findMonitorForActor(actorData.actor);
 | 
			
		||||
                let side;
 | 
			
		||||
                if (x1 <= monitor.x && x2 >= monitor.x + monitor.width) {
 | 
			
		||||
                    if (y1 <= monitor.y)
 | 
			
		||||
 
 | 
			
		||||
@@ -50,14 +50,15 @@ const LegacyTray = new Lang.Class({
 | 
			
		||||
        this._slideLayout.translationX = 0;
 | 
			
		||||
        this._slideLayout.slideDirection = OverviewControls.SlideDirection.LEFT;
 | 
			
		||||
 | 
			
		||||
        this._slider = new St.Widget({ x_expand: true, y_expand: true,
 | 
			
		||||
        this._slider = new St.Widget({ style_class: 'legacy-tray',
 | 
			
		||||
                                       x_expand: true, y_expand: true,
 | 
			
		||||
                                       x_align: Clutter.ActorAlign.START,
 | 
			
		||||
                                       y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                       layout_manager: this._slideLayout });
 | 
			
		||||
        this.actor.add_actor(this._slider);
 | 
			
		||||
        this._slider.connect('notify::allocation', Lang.bind(this, this._syncBarrier));
 | 
			
		||||
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'legacy-tray' });
 | 
			
		||||
        this._box = new St.BoxLayout();
 | 
			
		||||
        this._slider.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle',
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
 | 
			
		||||
                    'const Mainloop = imports.mainloop; ' +
 | 
			
		||||
                    'const Meta = imports.gi.Meta; ' +
 | 
			
		||||
                    'const Shell = imports.gi.Shell; ' +
 | 
			
		||||
                    'const Tp = imports.gi.TelepathyGLib; ' +
 | 
			
		||||
                    'const Main = imports.ui.main; ' +
 | 
			
		||||
                    'const Lang = imports.lang; ' +
 | 
			
		||||
                    'const Tweener = imports.ui.tweener; ' +
 | 
			
		||||
@@ -783,7 +784,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._open = false;
 | 
			
		||||
 | 
			
		||||
        this._it = null;
 | 
			
		||||
        this._offset = 0;
 | 
			
		||||
        this._results = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,6 @@ 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;
 | 
			
		||||
const EndSessionDialog = imports.ui.endSessionDialog;
 | 
			
		||||
@@ -26,7 +24,6 @@ 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;
 | 
			
		||||
@@ -62,12 +59,9 @@ 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;
 | 
			
		||||
let screenSaverDBus = null;
 | 
			
		||||
@@ -126,8 +120,6 @@ function start() {
 | 
			
		||||
                                       _loadDefaultStylesheet);
 | 
			
		||||
    _initializeUI();
 | 
			
		||||
 | 
			
		||||
    shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus();
 | 
			
		||||
    shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus();
 | 
			
		||||
    shellDBusService = new ShellDBus.GnomeShell();
 | 
			
		||||
    shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +149,6 @@ 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();
 | 
			
		||||
@@ -197,8 +188,6 @@ function _initializeUI() {
 | 
			
		||||
        return true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    global.display.connect('gl-video-memory-purged', loadTheme);
 | 
			
		||||
 | 
			
		||||
    // Provide the bus object for gnome-session to
 | 
			
		||||
    // initiate logouts.
 | 
			
		||||
    EndSessionDialog.init();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,713 +0,0 @@
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const MESSAGE_ANIMATION_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_EXPAND_LINES = 6;
 | 
			
		||||
 | 
			
		||||
function _fixMarkup(text, allowMarkup) {
 | 
			
		||||
    if (allowMarkup) {
 | 
			
		||||
        // Support &, ", ', < and >, escape all other
 | 
			
		||||
        // occurrences of '&'.
 | 
			
		||||
        let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
 | 
			
		||||
 | 
			
		||||
        // Support <b>, <i>, and <u>, escape anything else
 | 
			
		||||
        // so it displays as raw markup.
 | 
			
		||||
        _text = _text.replace(/<(?!\/?[biu]>)/g, '<');
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            Pango.parse_markup(_text, -1, '');
 | 
			
		||||
            return _text;
 | 
			
		||||
        } catch (e) {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // !allowMarkup, or invalid markup
 | 
			
		||||
    return GLib.markup_escape_text(text, -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const URLHighlighter = new Lang.Class({
 | 
			
		||||
    Name: 'URLHighlighter',
 | 
			
		||||
 | 
			
		||||
    _init: function(text, lineWrap, allowMarkup) {
 | 
			
		||||
        if (!text)
 | 
			
		||||
            text = '';
 | 
			
		||||
        this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter',
 | 
			
		||||
                                    x_expand: true, x_align: Clutter.ActorAlign.START });
 | 
			
		||||
        this._linkColor = '#ccccff';
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, function() {
 | 
			
		||||
            let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false);
 | 
			
		||||
            if (hasColor) {
 | 
			
		||||
                let linkColor = color.to_string().substr(0, 7);
 | 
			
		||||
                if (linkColor != this._linkColor) {
 | 
			
		||||
                    this._linkColor = linkColor;
 | 
			
		||||
                    this._highlightUrls();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.clutter_text.line_wrap = lineWrap;
 | 
			
		||||
        this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
 | 
			
		||||
 | 
			
		||||
        this.setMarkup(text, allowMarkup);
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            // Don't try to URL highlight when invisible.
 | 
			
		||||
            // The MessageTray doesn't actually hide us, so
 | 
			
		||||
            // we need to check for paint opacities as well.
 | 
			
		||||
            if (!actor.visible || actor.get_paint_opacity() == 0)
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            // Keep Notification.actor from seeing this and taking
 | 
			
		||||
            // a pointer grab, which would block our button-release-event
 | 
			
		||||
            // handler, if an URL is clicked
 | 
			
		||||
            return this._findUrlAtPos(event) != -1;
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
 | 
			
		||||
            if (!actor.visible || actor.get_paint_opacity() == 0)
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            let urlId = this._findUrlAtPos(event);
 | 
			
		||||
            if (urlId != -1) {
 | 
			
		||||
                let url = this._urls[urlId].url;
 | 
			
		||||
                if (url.indexOf(':') == -1)
 | 
			
		||||
                    url = 'http://' + url;
 | 
			
		||||
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            }
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            if (!actor.visible || actor.get_paint_opacity() == 0)
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            let urlId = this._findUrlAtPos(event);
 | 
			
		||||
            if (urlId != -1 && !this._cursorChanged) {
 | 
			
		||||
                global.screen.set_cursor(Meta.Cursor.POINTING_HAND);
 | 
			
		||||
                this._cursorChanged = true;
 | 
			
		||||
            } else if (urlId == -1) {
 | 
			
		||||
                global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
                this._cursorChanged = false;
 | 
			
		||||
            }
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('leave-event', Lang.bind(this, function() {
 | 
			
		||||
            if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            if (this._cursorChanged) {
 | 
			
		||||
                this._cursorChanged = false;
 | 
			
		||||
                global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
            }
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMarkup: function(text, allowMarkup) {
 | 
			
		||||
        text = text ? _fixMarkup(text, allowMarkup) : '';
 | 
			
		||||
        this._text = text;
 | 
			
		||||
 | 
			
		||||
        this.actor.clutter_text.set_markup(text);
 | 
			
		||||
        /* clutter_text.text contain text without markup */
 | 
			
		||||
        this._urls = Util.findUrls(this.actor.clutter_text.text);
 | 
			
		||||
        this._highlightUrls();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _highlightUrls: function() {
 | 
			
		||||
        // text here contain markup
 | 
			
		||||
        let urls = Util.findUrls(this._text);
 | 
			
		||||
        let markup = '';
 | 
			
		||||
        let pos = 0;
 | 
			
		||||
        for (let i = 0; i < urls.length; i++) {
 | 
			
		||||
            let url = urls[i];
 | 
			
		||||
            let str = this._text.substr(pos, url.pos - pos);
 | 
			
		||||
            markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>';
 | 
			
		||||
            pos = url.pos + url.url.length;
 | 
			
		||||
        }
 | 
			
		||||
        markup += this._text.substr(pos);
 | 
			
		||||
        this.actor.clutter_text.set_markup(markup);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findUrlAtPos: function(event) {
 | 
			
		||||
        let success;
 | 
			
		||||
        let [x, y] = event.get_coords();
 | 
			
		||||
        [success, x, y] = this.actor.transform_stage_point(x, y);
 | 
			
		||||
        let find_pos = -1;
 | 
			
		||||
        for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
 | 
			
		||||
            let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i);
 | 
			
		||||
            if (py > y || py + line_height < y || x < px)
 | 
			
		||||
                continue;
 | 
			
		||||
            find_pos = i;
 | 
			
		||||
        }
 | 
			
		||||
        if (find_pos != -1) {
 | 
			
		||||
            for (let i = 0; i < this._urls.length; i++)
 | 
			
		||||
            if (find_pos >= this._urls[i].pos &&
 | 
			
		||||
                this._urls[i].pos + this._urls[i].url.length > find_pos)
 | 
			
		||||
                return i;
 | 
			
		||||
        }
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ScaleLayout = new Lang.Class({
 | 
			
		||||
    Name: 'ScaleLayout',
 | 
			
		||||
    Extends: Clutter.BinLayout,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this._container = null;
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectContainer: function(container) {
 | 
			
		||||
        if (this._container == container)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._container)
 | 
			
		||||
            for (let id of this._signals)
 | 
			
		||||
                this._container.disconnect(id);
 | 
			
		||||
 | 
			
		||||
        this._container = container;
 | 
			
		||||
        this._signals = [];
 | 
			
		||||
 | 
			
		||||
        if (this._container)
 | 
			
		||||
            for (let signal of ['notify::scale-x', 'notify::scale-y']) {
 | 
			
		||||
                let id = this._container.connect(signal, Lang.bind(this,
 | 
			
		||||
                    function() {
 | 
			
		||||
                        this.layout_changed();
 | 
			
		||||
                    }));
 | 
			
		||||
                this._signals.push(id);
 | 
			
		||||
            }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(container, forHeight) {
 | 
			
		||||
        this._connectContainer(container);
 | 
			
		||||
 | 
			
		||||
        let [min, nat] = this.parent(container, forHeight);
 | 
			
		||||
        return [Math.floor(min * container.scale_x),
 | 
			
		||||
                Math.floor(nat * container.scale_x)];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(container, forWidth) {
 | 
			
		||||
        this._connectContainer(container);
 | 
			
		||||
 | 
			
		||||
        let [min, nat] = this.parent(container, forWidth);
 | 
			
		||||
        return [Math.floor(min * container.scale_y),
 | 
			
		||||
                Math.floor(nat * container.scale_y)];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const LabelExpanderLayout = new Lang.Class({
 | 
			
		||||
    Name: 'LabelExpanderLayout',
 | 
			
		||||
    Extends: Clutter.LayoutManager,
 | 
			
		||||
    Properties: { 'expansion': GObject.ParamSpec.double('expansion',
 | 
			
		||||
                                                        'Expansion',
 | 
			
		||||
                                                        'Expansion of the layout, between 0 (collapsed) ' +
 | 
			
		||||
                                                        'and 1 (fully expanded',
 | 
			
		||||
                                                         GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
 | 
			
		||||
                                                         0, 1, 0)},
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this._expansion = 0;
 | 
			
		||||
        this._expandLines = DEFAULT_EXPAND_LINES;
 | 
			
		||||
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get expansion() {
 | 
			
		||||
        return this._expansion;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set expansion(v) {
 | 
			
		||||
        if (v == this._expansion)
 | 
			
		||||
            return;
 | 
			
		||||
        this._expansion = v;
 | 
			
		||||
        this.notify('expansion');
 | 
			
		||||
 | 
			
		||||
        let visibleIndex = this._expansion > 0 ? 1 : 0;
 | 
			
		||||
        for (let i = 0; this._container && i < this._container.get_n_children(); i++)
 | 
			
		||||
            this._container.get_child_at_index(i).visible = (i == visibleIndex);
 | 
			
		||||
 | 
			
		||||
        this.layout_changed();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set expandLines(v) {
 | 
			
		||||
        if (v == this._expandLines)
 | 
			
		||||
            return;
 | 
			
		||||
        this._expandLines = v;
 | 
			
		||||
        if (this._expansion > 0)
 | 
			
		||||
            this.layout_changed();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_set_container: function(container) {
 | 
			
		||||
        this._container = container;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(container, forHeight) {
 | 
			
		||||
        let [min, nat] = [0, 0];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < container.get_n_children(); i++) {
 | 
			
		||||
            if (i > 1)
 | 
			
		||||
                break; // we support one unexpanded + one expanded child
 | 
			
		||||
 | 
			
		||||
            let child = container.get_child_at_index(i);
 | 
			
		||||
            let [childMin, childNat] = child.get_preferred_width(forHeight);
 | 
			
		||||
            [min, nat] = [Math.max(min, childMin), Math.max(nat, childNat)];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [min, nat];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(container, forWidth) {
 | 
			
		||||
        let [min, nat] = [0, 0];
 | 
			
		||||
 | 
			
		||||
        let children = container.get_children();
 | 
			
		||||
        if (children[0])
 | 
			
		||||
            [min, nat] = children[0].get_preferred_height(forWidth);
 | 
			
		||||
 | 
			
		||||
        if (children[1]) {
 | 
			
		||||
            let [min2, nat2] = children[1].get_preferred_height(forWidth);
 | 
			
		||||
            let [expMin, expNat] = [Math.min(min2, min * this._expandLines),
 | 
			
		||||
                                    Math.min(nat2, nat * this._expandLines)];
 | 
			
		||||
            [min, nat] = [min + this._expansion * (expMin - min),
 | 
			
		||||
                          nat + this._expansion * (expNat - nat)];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [min, nat];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        for (let i = 0; i < container.get_n_children(); i++) {
 | 
			
		||||
            let child = container.get_child_at_index(i);
 | 
			
		||||
 | 
			
		||||
            if (child.visible)
 | 
			
		||||
                child.allocate(box, flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Message = new Lang.Class({
 | 
			
		||||
    Name: 'Message',
 | 
			
		||||
 | 
			
		||||
    _init: function(title, body) {
 | 
			
		||||
        this.expanded = false;
 | 
			
		||||
 | 
			
		||||
        this._useBodyMarkup = false;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'message',
 | 
			
		||||
                                     accessible_role: Atk.Role.NOTIFICATION,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_expand: true, x_fill: true });
 | 
			
		||||
        this.actor.connect('key-press-event',
 | 
			
		||||
                           Lang.bind(this, this._onKeyPressed));
 | 
			
		||||
 | 
			
		||||
        let vbox = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this.actor.set_child(vbox);
 | 
			
		||||
 | 
			
		||||
        let hbox = new St.BoxLayout();
 | 
			
		||||
        vbox.add_actor(hbox);
 | 
			
		||||
 | 
			
		||||
        this._actionBin = new St.Widget({ layout_manager: new ScaleLayout(),
 | 
			
		||||
                                          visible: false });
 | 
			
		||||
        vbox.add_actor(this._actionBin);
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin({ style_class: 'message-icon-bin',
 | 
			
		||||
                                     y_expand: true,
 | 
			
		||||
                                     y_align: St.Align.START,
 | 
			
		||||
                                     visible: false });
 | 
			
		||||
        hbox.add_actor(this._iconBin);
 | 
			
		||||
 | 
			
		||||
        let contentBox = new St.BoxLayout({ style_class: 'message-content',
 | 
			
		||||
                                            vertical: true, x_expand: true });
 | 
			
		||||
        hbox.add_actor(contentBox);
 | 
			
		||||
 | 
			
		||||
        this._mediaControls = new St.BoxLayout();
 | 
			
		||||
        hbox.add_actor(this._mediaControls);
 | 
			
		||||
 | 
			
		||||
        let titleBox = new St.BoxLayout();
 | 
			
		||||
        contentBox.add_actor(titleBox);
 | 
			
		||||
 | 
			
		||||
        this.titleLabel = new St.Label({ style_class: 'message-title' });
 | 
			
		||||
        this.setTitle(title);
 | 
			
		||||
        titleBox.add_actor(this.titleLabel);
 | 
			
		||||
 | 
			
		||||
        this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin',
 | 
			
		||||
                                          x_expand: true, y_expand: true,
 | 
			
		||||
                                          x_fill: true, y_fill: true });
 | 
			
		||||
        titleBox.add_actor(this._secondaryBin);
 | 
			
		||||
 | 
			
		||||
        let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic',
 | 
			
		||||
                                      icon_size: 16 });
 | 
			
		||||
        this._closeButton = new St.Button({ child: closeIcon, opacity: 0 });
 | 
			
		||||
        titleBox.add_actor(this._closeButton);
 | 
			
		||||
 | 
			
		||||
        this._bodyStack = new St.Widget({ x_expand: true });
 | 
			
		||||
        this._bodyStack.layout_manager = new LabelExpanderLayout();
 | 
			
		||||
        contentBox.add_actor(this._bodyStack);
 | 
			
		||||
 | 
			
		||||
        this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup);
 | 
			
		||||
        this.bodyLabel.actor.add_style_class_name('message-body');
 | 
			
		||||
        this._bodyStack.add_actor(this.bodyLabel.actor);
 | 
			
		||||
        this.setBody(body);
 | 
			
		||||
 | 
			
		||||
        this._closeButton.connect('clicked', Lang.bind(this, this.close));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this._sync));
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close: function() {
 | 
			
		||||
        this.emit('close');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setIcon: function(actor) {
 | 
			
		||||
        this._iconBin.child = actor;
 | 
			
		||||
        this._iconBin.visible = (actor != null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSecondaryActor: function(actor) {
 | 
			
		||||
        this._secondaryBin.child = actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setTitle: function(text) {
 | 
			
		||||
        let title = text ? _fixMarkup(text.replace(/\n/g, ' '), false) : '';
 | 
			
		||||
        this.titleLabel.clutter_text.set_markup(title);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setBody: function(text) {
 | 
			
		||||
        this._bodyText = text;
 | 
			
		||||
        this.bodyLabel.setMarkup(text ? text.replace(/\n/g, ' ') : '',
 | 
			
		||||
                                 this._useBodyMarkup);
 | 
			
		||||
        if (this._expandedLabel)
 | 
			
		||||
            this._expandedLabel.setMarkup(text, this._useBodyMarkup);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setUseBodyMarkup: function(enable) {
 | 
			
		||||
        if (this._useBodyMarkup === enable)
 | 
			
		||||
            return;
 | 
			
		||||
        this._useBodyMarkup = enable;
 | 
			
		||||
        if (this.bodyLabel)
 | 
			
		||||
            this.setBody(this._bodyText);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setActionArea: function(actor) {
 | 
			
		||||
        if (actor == null) {
 | 
			
		||||
            if (this._actionBin.get_n_children() > 0)
 | 
			
		||||
                this._actionBin.get_child_at_index(0).destroy();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._actionBin.get_n_children() > 0)
 | 
			
		||||
            throw new Error('Message already has an action area');
 | 
			
		||||
 | 
			
		||||
        this._actionBin.add_actor(actor);
 | 
			
		||||
        this._actionBin.visible = this.expanded;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMediaControl: function(iconName, callback) {
 | 
			
		||||
        let icon = new St.Icon({ icon_name: iconName, icon_size: 16 });
 | 
			
		||||
        let button = new St.Button({ style_class: 'message-media-control',
 | 
			
		||||
                                     child: icon });
 | 
			
		||||
        button.connect('clicked', callback);
 | 
			
		||||
        this._mediaControls.add_actor(button);
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setExpandedBody: function(actor) {
 | 
			
		||||
        if (actor == null) {
 | 
			
		||||
            if (this._bodyStack.get_n_children() > 1)
 | 
			
		||||
                this._bodyStack.get_child_at_index(1).destroy();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._bodyStack.get_n_children() > 1)
 | 
			
		||||
            throw new Error('Message already has an expanded body actor');
 | 
			
		||||
 | 
			
		||||
        this._bodyStack.insert_child_at_index(actor, 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setExpandedLines: function(nLines) {
 | 
			
		||||
        this._bodyStack.layout_manager.expandLines = nLines;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    expand: function(animate) {
 | 
			
		||||
        this.expanded = true;
 | 
			
		||||
 | 
			
		||||
        this._actionBin.visible = (this._actionBin.get_n_children() > 0);
 | 
			
		||||
 | 
			
		||||
        if (this._bodyStack.get_n_children() < 2) {
 | 
			
		||||
            this._expandedLabel = new URLHighlighter(this._bodyText,
 | 
			
		||||
                                                     true, this._useBodyMarkup);
 | 
			
		||||
            this.setExpandedBody(this._expandedLabel.actor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            Tweener.addTween(this._bodyStack.layout_manager,
 | 
			
		||||
                             { expansion: 1,
 | 
			
		||||
                               time: MessageTray.ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
            this._actionBin.scale_y = 0;
 | 
			
		||||
            Tweener.addTween(this._actionBin,
 | 
			
		||||
                             { scale_y: 1,
 | 
			
		||||
                               time: MessageTray.ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
        } else {
 | 
			
		||||
            this._bodyStack.layout_manager.expansion = 1;
 | 
			
		||||
            this._actionBin.scale_y = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('expanded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    unexpand: function(animate) {
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            Tweener.addTween(this._bodyStack.layout_manager,
 | 
			
		||||
                             { expansion: 0,
 | 
			
		||||
                               time: MessageTray.ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
            Tweener.addTween(this._actionBin,
 | 
			
		||||
                             { scale_y: 0,
 | 
			
		||||
                               time: MessageTray.ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
                                   this._actionBin.hide();
 | 
			
		||||
                                   this.expanded = false;
 | 
			
		||||
                               }});
 | 
			
		||||
        } else {
 | 
			
		||||
            this._bodyStack.layout_manager.expansion = 0;
 | 
			
		||||
            this._actionBin.scale_y = 0;
 | 
			
		||||
            this.expanded = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('unexpanded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canClose: function() {
 | 
			
		||||
        return this._mediaControls.get_n_children() == 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let visible = this.actor.hover && this.canClose();
 | 
			
		||||
        this._closeButton.opacity = visible ? 255 : 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressed: function(a, event) {
 | 
			
		||||
        let keysym = event.get_key_symbol();
 | 
			
		||||
 | 
			
		||||
        if (keysym == Clutter.KEY_Delete ||
 | 
			
		||||
            keysym == Clutter.KEY_KP_Delete) {
 | 
			
		||||
            this.close();
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Message.prototype);
 | 
			
		||||
 | 
			
		||||
const MessageListSection = new Lang.Class({
 | 
			
		||||
    Name: 'MessageListSection',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'message-list-section',
 | 
			
		||||
                                        clip_to_allocation: true,
 | 
			
		||||
                                        x_expand: true, vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._list = new St.BoxLayout({ style_class: 'message-list-section-list',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
        this.actor.add_actor(this._list);
 | 
			
		||||
 | 
			
		||||
        this._list.connect('actor-added', Lang.bind(this, this._sync));
 | 
			
		||||
        this._list.connect('actor-removed', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        let id = Main.sessionMode.connect('updated',
 | 
			
		||||
                                          Lang.bind(this, this._sync));
 | 
			
		||||
        this.actor.connect('destroy', function() {
 | 
			
		||||
            Main.sessionMode.disconnect(id);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this._messages = new Map();
 | 
			
		||||
        this._date = new Date();
 | 
			
		||||
        this.empty = true;
 | 
			
		||||
        this.canClear = false;
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusIn: function(actor) {
 | 
			
		||||
        this.emit('key-focus-in', actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get allowed() {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setDate: function(date) {
 | 
			
		||||
        if (Calendar.sameDay(date, this._date))
 | 
			
		||||
            return;
 | 
			
		||||
        this._date = date;
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMessage: function(message, animate) {
 | 
			
		||||
        this.addMessageAtIndex(message, -1, animate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMessageAtIndex: function(message, index, animate) {
 | 
			
		||||
        let obj = {
 | 
			
		||||
            container: null,
 | 
			
		||||
            destroyId: 0,
 | 
			
		||||
            keyFocusId: 0,
 | 
			
		||||
            closeId: 0
 | 
			
		||||
        };
 | 
			
		||||
        let pivot = new Clutter.Point({ x: .5, y: .5 });
 | 
			
		||||
        let scale = animate ? 0 : 1;
 | 
			
		||||
        obj.container = new St.Widget({ layout_manager: new ScaleLayout(),
 | 
			
		||||
                                        pivot_point: pivot,
 | 
			
		||||
                                        scale_x: scale, scale_y: scale });
 | 
			
		||||
        obj.keyFocusId = message.actor.connect('key-focus-in',
 | 
			
		||||
            Lang.bind(this, this._onKeyFocusIn));
 | 
			
		||||
        obj.destroyId = message.actor.connect('destroy',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                this.removeMessage(message, false);
 | 
			
		||||
            }));
 | 
			
		||||
        obj.closeId = message.connect('close',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                this.removeMessage(message, true);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._messages.set(message, obj);
 | 
			
		||||
        obj.container.add_actor(message.actor);
 | 
			
		||||
 | 
			
		||||
        this._list.insert_child_at_index(obj.container, index);
 | 
			
		||||
 | 
			
		||||
        if (animate)
 | 
			
		||||
            Tweener.addTween(obj.container, { scale_x: 1,
 | 
			
		||||
                                              scale_y: 1,
 | 
			
		||||
                                              time: MESSAGE_ANIMATION_TIME,
 | 
			
		||||
                                              transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    moveMessage: function(message, index, animate) {
 | 
			
		||||
        let obj = this._messages.get(message);
 | 
			
		||||
 | 
			
		||||
        if (!animate) {
 | 
			
		||||
            this._list.set_child_at_index(obj.container, index);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let onComplete = Lang.bind(this, function() {
 | 
			
		||||
            this._list.set_child_at_index(obj.container, index);
 | 
			
		||||
            Tweener.addTween(obj.container, { scale_x: 1,
 | 
			
		||||
                                              scale_y: 1,
 | 
			
		||||
                                              time: MESSAGE_ANIMATION_TIME,
 | 
			
		||||
                                              transition: 'easeOutQuad' });
 | 
			
		||||
        });
 | 
			
		||||
        Tweener.addTween(obj.container, { scale_x: 0,
 | 
			
		||||
                                          scale_y: 0,
 | 
			
		||||
                                          time: MESSAGE_ANIMATION_TIME,
 | 
			
		||||
                                          transition: 'easeOutQuad',
 | 
			
		||||
                                          onComplete: onComplete });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeMessage: function(message, animate) {
 | 
			
		||||
        let obj = this._messages.get(message);
 | 
			
		||||
 | 
			
		||||
        message.actor.disconnect(obj.destroyId);
 | 
			
		||||
        message.actor.disconnect(obj.keyFocusId);
 | 
			
		||||
        message.disconnect(obj.closeId);
 | 
			
		||||
 | 
			
		||||
        this._messages.delete(message);
 | 
			
		||||
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            Tweener.addTween(obj.container, { scale_x: 0, scale_y: 0,
 | 
			
		||||
                                              time: MESSAGE_ANIMATION_TIME,
 | 
			
		||||
                                              transition: 'easeOutQuad',
 | 
			
		||||
                                              onComplete: function() {
 | 
			
		||||
                                                  obj.container.destroy();
 | 
			
		||||
                                                  global.sync_pointer();
 | 
			
		||||
                                              }});
 | 
			
		||||
        } else {
 | 
			
		||||
            obj.container.destroy();
 | 
			
		||||
            global.sync_pointer();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        let messages = [...this._messages.keys()].filter(function(message) {
 | 
			
		||||
            return message.canClose();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // If there are few messages, letting them all zoom out looks OK
 | 
			
		||||
        if (messages.length < 2) {
 | 
			
		||||
            messages.forEach(function(message) {
 | 
			
		||||
                message.close();
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            // Otherwise we slide them out one by one, and then zoom them
 | 
			
		||||
            // out "off-screen" in the end to smoothly shrink the parent
 | 
			
		||||
            let delay = MESSAGE_ANIMATION_TIME / Math.max(messages.length, 5);
 | 
			
		||||
            for (let i = 0; i < messages.length; i++) {
 | 
			
		||||
                let message = messages[i];
 | 
			
		||||
                let obj = this._messages.get(message);
 | 
			
		||||
                Tweener.addTween(obj.container,
 | 
			
		||||
                                 { anchor_x: this._list.width,
 | 
			
		||||
                                   opacity: 0,
 | 
			
		||||
                                   time: MESSAGE_ANIMATION_TIME,
 | 
			
		||||
                                   delay: i * delay,
 | 
			
		||||
                                   transition: 'easeOutQuad',
 | 
			
		||||
                                   onComplete: function() {
 | 
			
		||||
                                       message.close();
 | 
			
		||||
                                   }});
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canClear: function() {
 | 
			
		||||
        for (let message of this._messages.keys())
 | 
			
		||||
            if (message.canClose())
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldShow: function() {
 | 
			
		||||
        return !this.empty;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let empty = this._list.get_n_children() == 0;
 | 
			
		||||
        let changed = this.empty !== empty;
 | 
			
		||||
        this.empty = empty;
 | 
			
		||||
 | 
			
		||||
        if (changed)
 | 
			
		||||
            this.emit('empty-changed');
 | 
			
		||||
 | 
			
		||||
        let canClear = this._canClear();
 | 
			
		||||
        changed = this.canClear !== canClear;
 | 
			
		||||
        this.canClear = canClear;
 | 
			
		||||
 | 
			
		||||
        if (changed)
 | 
			
		||||
            this.emit('can-clear-changed');
 | 
			
		||||
 | 
			
		||||
        this.actor.visible = this.allowed && this._shouldShow();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(MessageListSection.prototype);
 | 
			
		||||
@@ -368,7 +368,6 @@ const Notification = new Lang.Class({
 | 
			
		||||
                                        secondaryGIcon: null,
 | 
			
		||||
                                        bannerMarkup: false,
 | 
			
		||||
                                        clear: false,
 | 
			
		||||
                                        datetime: null,
 | 
			
		||||
                                        soundName: null,
 | 
			
		||||
                                        soundFile: null });
 | 
			
		||||
 | 
			
		||||
@@ -376,11 +375,6 @@ const Notification = new Lang.Class({
 | 
			
		||||
        this.bannerBodyText = banner;
 | 
			
		||||
        this.bannerBodyMarkup = params.bannerMarkup;
 | 
			
		||||
 | 
			
		||||
        if (params.datetime)
 | 
			
		||||
            this.datetime = params.datetime;
 | 
			
		||||
        else
 | 
			
		||||
            this.datetime = GLib.DateTime.new_now_local();
 | 
			
		||||
 | 
			
		||||
        if (params.gicon || params.clear)
 | 
			
		||||
            this.gicon = params.gicon;
 | 
			
		||||
 | 
			
		||||
@@ -541,8 +535,7 @@ const NotificationBanner = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _addSecondaryIcon: function() {
 | 
			
		||||
        if (this.notification.secondaryGIcon) {
 | 
			
		||||
            let icon = new St.Icon({ gicon: this.notification.secondaryGIcon,
 | 
			
		||||
                                     x_align: Clutter.ActorAlign.END });
 | 
			
		||||
            let icon = new St.Icon({ gicon: this.notification.secondaryGIcon });
 | 
			
		||||
            this.setSecondaryActor(icon);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -803,7 +796,7 @@ const Source = new Lang.Class({
 | 
			
		||||
        notification.acknowledged = false;
 | 
			
		||||
        this.pushNotification(notification);
 | 
			
		||||
 | 
			
		||||
        if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL) {
 | 
			
		||||
        if (this.policy.showBanners) {
 | 
			
		||||
            this.emit('notify', notification);
 | 
			
		||||
        } else {
 | 
			
		||||
            notification.playSound();
 | 
			
		||||
@@ -1231,7 +1224,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        if (this._notificationState == State.HIDDEN) {
 | 
			
		||||
            let nextNotification = this._notificationQueue[0] || null;
 | 
			
		||||
            if (hasNotifications && nextNotification) {
 | 
			
		||||
                let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen;
 | 
			
		||||
                let limited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
 | 
			
		||||
                let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL);
 | 
			
		||||
                if (showNextNotification)
 | 
			
		||||
                    this._showNotification();
 | 
			
		||||
 
 | 
			
		||||
@@ -259,7 +259,7 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        if (this.state == State.OPENED || this.state == State.OPENING)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (!this.pushModal(timestamp))
 | 
			
		||||
        if (!this.pushModal({ timestamp: timestamp }))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._fadeOpen(onPrimary);
 | 
			
		||||
@@ -318,11 +318,8 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
    pushModal: function (timestamp) {
 | 
			
		||||
        if (this._hasModal)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        let params = { actionMode: this._actionMode };
 | 
			
		||||
        if (timestamp)
 | 
			
		||||
            params['timestamp'] = timestamp;
 | 
			
		||||
        if (!Main.pushModal(this._group, params))
 | 
			
		||||
        if (!Main.pushModal(this._group, { timestamp: timestamp,
 | 
			
		||||
                                           actionMode: this._actionMode }))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._hasModal = true;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										287
									
								
								js/ui/mpris.js
									
									
									
									
									
								
							
							
						
						
									
										287
									
								
								js/ui/mpris.js
									
									
									
									
									
								
							@@ -1,287 +0,0 @@
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageList = imports.ui.messageList;
 | 
			
		||||
 | 
			
		||||
const DBusIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.DBus"> \
 | 
			
		||||
  <method name="ListNames"> \
 | 
			
		||||
    <arg type="as" direction="out" name="names" /> \
 | 
			
		||||
  </method> \
 | 
			
		||||
  <signal name="NameOwnerChanged"> \
 | 
			
		||||
    <arg type="s" direction="out" name="name" /> \
 | 
			
		||||
    <arg type="s" direction="out" name="oldOwner" /> \
 | 
			
		||||
    <arg type="s" direction="out" name="newOwner" /> \
 | 
			
		||||
  </signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const DBusProxy = Gio.DBusProxy.makeProxyWrapper(DBusIface);
 | 
			
		||||
 | 
			
		||||
const MprisIface = '<node> \
 | 
			
		||||
<interface name="org.mpris.MediaPlayer2"> \
 | 
			
		||||
  <method name="Raise" /> \
 | 
			
		||||
  <property name="CanRaise" type="b" access="read" /> \
 | 
			
		||||
  <property name="DesktopEntry" type="s" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const MprisProxy = Gio.DBusProxy.makeProxyWrapper(MprisIface);
 | 
			
		||||
 | 
			
		||||
const MprisPlayerIface = '<node> \
 | 
			
		||||
<interface name="org.mpris.MediaPlayer2.Player"> \
 | 
			
		||||
  <method name="PlayPause" /> \
 | 
			
		||||
  <method name="Next" /> \
 | 
			
		||||
  <method name="Previous" /> \
 | 
			
		||||
  <property name="CanGoNext" type="b" access="read" /> \
 | 
			
		||||
  <property name="CanGoPrevious" type="b" access="read" /> \
 | 
			
		||||
  <property name="CanPlay" type="b" access="read" /> \
 | 
			
		||||
  <property name="Metadata" type="a{sv}" access="read" /> \
 | 
			
		||||
  <property name="PlaybackStatus" type="s" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
 | 
			
		||||
 | 
			
		||||
const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.';
 | 
			
		||||
 | 
			
		||||
const MediaMessage = new Lang.Class({
 | 
			
		||||
    Name: 'MediaMessage',
 | 
			
		||||
    Extends: MessageList.Message,
 | 
			
		||||
 | 
			
		||||
    _init: function(player) {
 | 
			
		||||
        this._player = player;
 | 
			
		||||
 | 
			
		||||
        this.parent('', '');
 | 
			
		||||
 | 
			
		||||
        this._icon = new St.Icon({ style_class: 'media-message-cover-icon' });
 | 
			
		||||
        this.setIcon(this._icon);
 | 
			
		||||
 | 
			
		||||
        this._prevButton = this.addMediaControl('media-skip-backward-symbolic',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                this._player.previous();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._playPauseButton = this.addMediaControl(null,
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                this._player.playPause();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._nextButton = this.addMediaControl('media-skip-forward-symbolic',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                this._player.next();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._player.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
        this._player.connect('closed', Lang.bind(this, this.close));
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this._player.raise();
 | 
			
		||||
        Main.panel.closeCalendar();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateNavButton: function(button, sensitive) {
 | 
			
		||||
        button.reactive = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this.setTitle(this._player.trackArtists.join(', '));
 | 
			
		||||
        this.setBody(this._player.trackTitle);
 | 
			
		||||
 | 
			
		||||
        if (this._player.trackCoverUrl) {
 | 
			
		||||
            let file = Gio.File.new_for_uri(this._player.trackCoverUrl);
 | 
			
		||||
            this._icon.gicon = new Gio.FileIcon({ file: file });
 | 
			
		||||
            this._icon.remove_style_class_name('fallback');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._icon.icon_name = 'audio-x-generic-symbolic';
 | 
			
		||||
            this._icon.add_style_class_name('fallback');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let isPlaying = this._player.status == 'Playing';
 | 
			
		||||
        let iconName = isPlaying ? 'media-playback-pause-symbolic'
 | 
			
		||||
                                 : 'media-playback-start-symbolic';
 | 
			
		||||
        this._playPauseButton.child.icon_name = iconName;
 | 
			
		||||
 | 
			
		||||
        this._updateNavButton(this._prevButton, this._player.canGoPrevious);
 | 
			
		||||
        this._updateNavButton(this._nextButton, this._player.canGoNext);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const MprisPlayer = new Lang.Class({
 | 
			
		||||
    Name: 'MprisPlayer',
 | 
			
		||||
 | 
			
		||||
    _init: function(busName) {
 | 
			
		||||
        this._mprisProxy = new MprisProxy(Gio.DBus.session, busName,
 | 
			
		||||
                                          '/org/mpris/MediaPlayer2',
 | 
			
		||||
                                          Lang.bind(this, this._onMprisProxyReady));
 | 
			
		||||
        this._playerProxy = new MprisPlayerProxy(Gio.DBus.session, busName,
 | 
			
		||||
                                                 '/org/mpris/MediaPlayer2',
 | 
			
		||||
                                                 Lang.bind(this, this._onPlayerProxyReady));
 | 
			
		||||
 | 
			
		||||
        this._visible = false;
 | 
			
		||||
        this._trackArtists = [];
 | 
			
		||||
        this._trackTitle = '';
 | 
			
		||||
        this._trackCoverUrl = '';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get status() {
 | 
			
		||||
        return this._playerProxy.PlaybackStatus;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get trackArtists() {
 | 
			
		||||
        return this._trackArtists;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get trackTitle() {
 | 
			
		||||
        return this._trackTitle;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get trackCoverUrl() {
 | 
			
		||||
        return this._trackCoverUrl;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    playPause: function() {
 | 
			
		||||
        this._playerProxy.PlayPauseRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get canGoNext() {
 | 
			
		||||
        return this._playerProxy.CanGoNext;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    next: function() {
 | 
			
		||||
        this._playerProxy.NextRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get canGoPrevious() {
 | 
			
		||||
        return this._playerProxy.CanGoPrevious;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    previous: function() {
 | 
			
		||||
        this._playerProxy.PreviousRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    raise: function() {
 | 
			
		||||
        // The remote Raise() method may run into focus stealing prevention,
 | 
			
		||||
        // so prefer activating the app via .desktop file if possible
 | 
			
		||||
        let app = null;
 | 
			
		||||
        if (this._mprisProxy.DesktopEntry) {
 | 
			
		||||
            let desktopId = this._mprisProxy.DesktopEntry + '.desktop';
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_app(desktopId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (app)
 | 
			
		||||
            app.activate();
 | 
			
		||||
        else if (this._mprisProxy.CanRaise)
 | 
			
		||||
            this._mprisProxy.RaiseRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _close: function() {
 | 
			
		||||
        this._mprisProxy.disconnect(this._ownerNotifyId);
 | 
			
		||||
        this._mprisProxy = null;
 | 
			
		||||
 | 
			
		||||
        this._playerProxy.disconnect(this._propsChangedId);
 | 
			
		||||
        this._playerProxy = null;
 | 
			
		||||
 | 
			
		||||
        this.emit('closed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMprisProxyReady: function() {
 | 
			
		||||
        this._ownerNotifyId = this._mprisProxy.connect('notify::g-name-owner',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                if (!this._mprisProxy.g_name_owner)
 | 
			
		||||
                    this._close();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPlayerProxyReady: function() {
 | 
			
		||||
        this._propsChangedId = this._playerProxy.connect('g-properties-changed',
 | 
			
		||||
                                                         Lang.bind(this, this._updateState));
 | 
			
		||||
        this._updateState();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateState: function() {
 | 
			
		||||
        let metadata = {};
 | 
			
		||||
        for (let prop in this._playerProxy.Metadata)
 | 
			
		||||
            metadata[prop] = this._playerProxy.Metadata[prop].deep_unpack();
 | 
			
		||||
 | 
			
		||||
        this._trackArtists = metadata['xesam:artist'] || [_("Unknown artist")];
 | 
			
		||||
        this._trackTitle = metadata['xesam:title'] || _("Unknown title");
 | 
			
		||||
        this._trackCoverUrl = metadata['mpris:artUrl'] || '';
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
 | 
			
		||||
        let visible = this._playerProxy.CanPlay;
 | 
			
		||||
 | 
			
		||||
        if (this._visible != visible) {
 | 
			
		||||
            this._visible = visible;
 | 
			
		||||
            if (visible)
 | 
			
		||||
                this.emit('show');
 | 
			
		||||
            else
 | 
			
		||||
                this._close();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(MprisPlayer.prototype);
 | 
			
		||||
 | 
			
		||||
const MediaSection = new Lang.Class({
 | 
			
		||||
    Name: 'MediaSection',
 | 
			
		||||
    Extends: MessageList.MessageListSection,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._players = new Map();
 | 
			
		||||
 | 
			
		||||
        this._proxy = new DBusProxy(Gio.DBus.session,
 | 
			
		||||
                                    'org.freedesktop.DBus',
 | 
			
		||||
                                    '/org/freedesktop/DBus',
 | 
			
		||||
                                    Lang.bind(this, this._onProxyReady));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldShow: function() {
 | 
			
		||||
        return !this.empty && Calendar.isToday(this._date);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPlayer: function(busName) {
 | 
			
		||||
        if (this._players.get(busName))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let player = new MprisPlayer(busName);
 | 
			
		||||
        player.connect('closed', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._players.delete(busName);
 | 
			
		||||
            }));
 | 
			
		||||
        player.connect('show', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let message = new MediaMessage(player);
 | 
			
		||||
                this.addMessage(message, true);
 | 
			
		||||
            }));
 | 
			
		||||
        this._players.set(busName, player);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onProxyReady: function() {
 | 
			
		||||
        this._proxy.ListNamesRemote(Lang.bind(this,
 | 
			
		||||
            function([names]) {
 | 
			
		||||
                names.forEach(Lang.bind(this,
 | 
			
		||||
                    function(name) {
 | 
			
		||||
                        if (!name.startsWith(MPRIS_PLAYER_PREFIX))
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        this._addPlayer(name);
 | 
			
		||||
                    }));
 | 
			
		||||
            }));
 | 
			
		||||
        this._proxy.connectSignal('NameOwnerChanged',
 | 
			
		||||
                                  Lang.bind(this, this._onNameOwnerChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNameOwnerChanged: function(proxy, sender, [name, oldOwner, newOwner]) {
 | 
			
		||||
        if (!name.startsWith(MPRIS_PLAYER_PREFIX))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (newOwner && !oldOwner)
 | 
			
		||||
            this._addPlayer(name);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -128,10 +128,10 @@ const FdoNotificationDaemon = new Lang.Class({
 | 
			
		||||
        switch (hints.urgency) {
 | 
			
		||||
            case Urgency.LOW:
 | 
			
		||||
            case Urgency.NORMAL:
 | 
			
		||||
                stockIcon = 'dialog-information';
 | 
			
		||||
                stockIcon = 'gtk-dialog-info';
 | 
			
		||||
                break;
 | 
			
		||||
            case Urgency.CRITICAL:
 | 
			
		||||
                stockIcon = 'dialog-error';
 | 
			
		||||
                stockIcon = 'gtk-dialog-error';
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return new Gio.ThemedIcon({ name: stockIcon });
 | 
			
		||||
@@ -186,8 +186,7 @@ const FdoNotificationDaemon = new Lang.Class({
 | 
			
		||||
            return source;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let appId = ndata ? ndata.hints['desktop-entry'] || null : null;
 | 
			
		||||
        source = new FdoNotificationDaemonSource(title, pid, sender, appId);
 | 
			
		||||
        let source = new FdoNotificationDaemonSource(title, pid, sender, ndata ? ndata.hints['desktop-entry'] : null);
 | 
			
		||||
 | 
			
		||||
        this._sources.push(source);
 | 
			
		||||
        source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
@@ -392,10 +391,10 @@ const FdoNotificationDaemon = new Lang.Class({
 | 
			
		||||
                notification.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        notification.setResident(!!hints.resident);
 | 
			
		||||
        notification.setResident(hints.resident == true);
 | 
			
		||||
        // 'transient' is a reserved keyword in JS, so we have to retrieve the value
 | 
			
		||||
        // of the 'transient' hint with hints['transient'] rather than hints.transient
 | 
			
		||||
        notification.setTransient(!!hints['transient']);
 | 
			
		||||
        notification.setTransient(hints['transient'] == true);
 | 
			
		||||
 | 
			
		||||
        let sourceGIcon = source.useNotificationIcon ? gicon : null;
 | 
			
		||||
        source.processNotification(notification, sourceGIcon);
 | 
			
		||||
@@ -601,8 +600,7 @@ const GtkNotificationDaemonNotification = new Lang.Class({
 | 
			
		||||
              "priority": priority,
 | 
			
		||||
              "buttons": buttons,
 | 
			
		||||
              "default-action": defaultAction,
 | 
			
		||||
              "default-action-target": defaultActionTarget,
 | 
			
		||||
              "timestamp": time } = notification;
 | 
			
		||||
              "default-action-target": defaultActionTarget } = notification;
 | 
			
		||||
 | 
			
		||||
        if (priority) {
 | 
			
		||||
            let urgency = PRIORITY_URGENCY_MAP[priority.unpack()];
 | 
			
		||||
@@ -625,8 +623,7 @@ const GtkNotificationDaemonNotification = new Lang.Class({
 | 
			
		||||
        this._defaultActionTarget = defaultActionTarget;
 | 
			
		||||
 | 
			
		||||
        this.update(title.unpack(), body ? body.unpack() : null,
 | 
			
		||||
                    { gicon: gicon ? Gio.icon_deserialize(gicon) : null,
 | 
			
		||||
                      datetime : time ? GLib.DateTime.new_from_unix_local(time.unpack()) : null });
 | 
			
		||||
                    { gicon: gicon ? Gio.icon_deserialize(gicon) : null });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _activateAction: function(namespacedActionId, target) {
 | 
			
		||||
@@ -693,7 +690,6 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
 | 
			
		||||
            throw new InvalidAppError();
 | 
			
		||||
 | 
			
		||||
        this._notifications = {};
 | 
			
		||||
        this._notificationPending = false;
 | 
			
		||||
 | 
			
		||||
        this.parent(this._app.get_name());
 | 
			
		||||
    },
 | 
			
		||||
@@ -706,35 +702,27 @@ const GtkNotificationDaemonAppSource = new Lang.Class({
 | 
			
		||||
        return new MessageTray.NotificationApplicationPolicy(this._appId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createApp: function(callback) {
 | 
			
		||||
        return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath, callback);
 | 
			
		||||
    _createApp: function() {
 | 
			
		||||
        return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateAction: function(actionId, target) {
 | 
			
		||||
        this._createApp(function (app, error) {
 | 
			
		||||
            if (error == null)
 | 
			
		||||
                app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
 | 
			
		||||
            else
 | 
			
		||||
                logError(error, 'Failed to activate application proxy');
 | 
			
		||||
        });
 | 
			
		||||
        let app = this._createApp();
 | 
			
		||||
        app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
 | 
			
		||||
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        Main.panel.closeCalendar();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function() {
 | 
			
		||||
        this._createApp(function (app, error) {
 | 
			
		||||
            if (error == null)
 | 
			
		||||
                app.ActivateRemote(getPlatformData());
 | 
			
		||||
            else
 | 
			
		||||
                logError(error, 'Failed to open application proxy');
 | 
			
		||||
        });
 | 
			
		||||
        let app = this._createApp();
 | 
			
		||||
        app.ActivateRemote(getPlatformData());
 | 
			
		||||
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        Main.panel.closeCalendar();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addNotification: function(notificationId, notificationParams, showBanner) {
 | 
			
		||||
        this._notificationPending = true;
 | 
			
		||||
 | 
			
		||||
        if (this._notifications[notificationId])
 | 
			
		||||
            this._notifications[notificationId].destroy();
 | 
			
		||||
 | 
			
		||||
@@ -748,14 +736,6 @@ 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) {
 | 
			
		||||
@@ -867,9 +847,6 @@ const GtkNotificationDaemon = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let timestamp = GLib.DateTime.new_now_local().to_unix();
 | 
			
		||||
        notification['timestamp'] = new GLib.Variant('x', timestamp);
 | 
			
		||||
 | 
			
		||||
        source.addNotification(notificationId, notification, true);
 | 
			
		||||
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,13 +22,11 @@ const LevelBar = new Lang.Class({
 | 
			
		||||
        this._level = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'level',
 | 
			
		||||
                                  x_align: St.Align.START,
 | 
			
		||||
                                  y_fill: true });
 | 
			
		||||
        this._bar = new St.Widget({ style_class: 'level-bar' });
 | 
			
		||||
                                  x_fill: true, y_fill: true });
 | 
			
		||||
        this._bar = new St.DrawingArea();
 | 
			
		||||
        this._bar.connect('repaint', Lang.bind(this, this._repaint));
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(this._bar);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('notify::width', () => { this.level = this.level; });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get level() {
 | 
			
		||||
@@ -36,44 +34,39 @@ const LevelBar = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set level(value) {
 | 
			
		||||
        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);
 | 
			
		||||
        let newValue = Math.max(0, Math.min(value, 100));
 | 
			
		||||
        if (newValue == this._level)
 | 
			
		||||
            return;
 | 
			
		||||
        this._level = newValue;
 | 
			
		||||
        this._bar.queue_repaint();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set minSize(v) {
 | 
			
		||||
        this._minSize = v;
 | 
			
		||||
        if (this.actor)
 | 
			
		||||
            this.actor.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
    _repaint: function() {
 | 
			
		||||
        let cr = this._bar.get_context();
 | 
			
		||||
 | 
			
		||||
    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 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());
 | 
			
		||||
 | 
			
		||||
        // Enforce a ratio of 1
 | 
			
		||||
        let size = Math.ceil(Math.max(minSize, height));
 | 
			
		||||
        actorBox.set_size(size, size);
 | 
			
		||||
        let [w, h] = this._bar.get_surface_size();
 | 
			
		||||
        w *= (this._level / 100.);
 | 
			
		||||
 | 
			
		||||
        // Recenter
 | 
			
		||||
        let [x, y] = actorBox.get_origin();
 | 
			
		||||
        actorBox.set_origin(Math.ceil(x + width / 2 - size / 2),
 | 
			
		||||
                            Math.ceil(y + height / 2 - size / 2));
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -81,6 +74,7 @@ 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,
 | 
			
		||||
@@ -90,12 +84,19 @@ 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 });
 | 
			
		||||
 | 
			
		||||
@@ -109,11 +110,8 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
        this._reset();
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   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();
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._monitorsChanged();
 | 
			
		||||
        Main.uiGroup.add_child(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +189,7 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
        this.setLevel(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _relayout: function() {
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        /* assume 110x110 on a 640x480 display and scale from there */
 | 
			
		||||
        let monitor = Main.layoutManager.monitors[this._monitorIndex];
 | 
			
		||||
        if (!monitor)
 | 
			
		||||
@@ -200,12 +198,30 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
        let scalew = monitor.width / 640.0;
 | 
			
		||||
        let scaleh = monitor.height / 480.0;
 | 
			
		||||
        let scale = Math.min(scalew, scaleh);
 | 
			
		||||
        let popupSize = 110 * Math.max(1, scale);
 | 
			
		||||
        this._popupSize = 110 * Math.max(1, scale);
 | 
			
		||||
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        this._icon.icon_size = popupSize / (2 * scaleFactor);
 | 
			
		||||
        this._icon.icon_size = this._popupSize / (2 * scaleFactor);
 | 
			
		||||
        this._box.translation_y = monitor.height / 4;
 | 
			
		||||
        this._boxConstraint.minSize = popupSize;
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@ 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"),
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            vertical: true,
 | 
			
		||||
                                            x_expand: true,
 | 
			
		||||
                                            y_expand: true });
 | 
			
		||||
        this._overview._delegate = this;
 | 
			
		||||
 | 
			
		||||
        // The main Background actors are inside global.window_group which are
 | 
			
		||||
@@ -120,7 +126,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({ reactive: true });
 | 
			
		||||
        this._backgroundGroup = new Meta.BackgroundGroup();
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
 | 
			
		||||
        this._bgManagers = [];
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +149,8 @@ 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; }));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._overview);
 | 
			
		||||
        this._stack.add_actor(this._overview);
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._stack);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
@@ -152,9 +159,6 @@ 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));
 | 
			
		||||
 | 
			
		||||
@@ -251,6 +255,7 @@ 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;
 | 
			
		||||
@@ -367,7 +372,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._backgroundGroup.add_action(action);
 | 
			
		||||
        this._overview.add_action(action);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDesktopClone: function() {
 | 
			
		||||
@@ -378,7 +383,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let window = windows[0];
 | 
			
		||||
        let clone = new Clutter.Clone({ source: window,
 | 
			
		||||
        let clone = new Clutter.Clone({ source: window.get_texture(),
 | 
			
		||||
                                        x: window.x, y: window.y });
 | 
			
		||||
        clone.source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            clone.destroy();
 | 
			
		||||
@@ -414,7 +419,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    beginItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-begin');
 | 
			
		||||
        this._inItemDrag = true;
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledItemDrag: function(source) {
 | 
			
		||||
@@ -422,15 +427,13 @@ const Overview = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endItemDrag: function(source) {
 | 
			
		||||
        if (!this._inItemDrag)
 | 
			
		||||
            return;
 | 
			
		||||
        this.emit('item-drag-end');
 | 
			
		||||
        this._inItemDrag = false;
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    beginWindowDrag: function(window) {
 | 
			
		||||
        this.emit('window-drag-begin', window);
 | 
			
		||||
        this._inWindowDrag = true;
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledWindowDrag: function(window) {
 | 
			
		||||
@@ -438,10 +441,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endWindowDrag: function(window) {
 | 
			
		||||
        if (!this._inWindowDrag)
 | 
			
		||||
            return;
 | 
			
		||||
        this.emit('window-drag-end', window);
 | 
			
		||||
        this._inWindowDrag = false;
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    focusSearch: function() {
 | 
			
		||||
@@ -485,7 +486,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
    shouldToggleByCornerOrButton: function() {
 | 
			
		||||
        if (this.animationInProgress)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (this._inItemDrag || this._inWindowDrag)
 | 
			
		||||
        if (this._inDrag)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
 | 
			
		||||
            return true;
 | 
			
		||||
@@ -551,8 +552,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
        this.viewSelector.show();
 | 
			
		||||
 | 
			
		||||
        this._overview.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._overview,
 | 
			
		||||
        this._stack.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._stack,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
@@ -617,7 +618,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this.viewSelector.animateFromOverview();
 | 
			
		||||
 | 
			
		||||
        // Make other elements fade out.
 | 
			
		||||
        Tweener.addTween(this._overview,
 | 
			
		||||
        Tweener.addTween(this._stack,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
 
 | 
			
		||||
@@ -421,6 +421,7 @@ 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',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										996
									
								
								js/ui/padOsd.js
									
									
									
									
									
								
							
							
						
						
									
										996
									
								
								js/ui/padOsd.js
									
									
									
									
									
								
							@@ -1,996 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Rsvg = imports.gi.Rsvg;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GDesktopEnums = imports.gi.GDesktopEnums;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
 | 
			
		||||
const ACTIVE_COLOR = "#729fcf";
 | 
			
		||||
 | 
			
		||||
const LTR = 0;
 | 
			
		||||
const RTL = 1;
 | 
			
		||||
 | 
			
		||||
const CW = 0;
 | 
			
		||||
const CCW = 1;
 | 
			
		||||
 | 
			
		||||
const UP = 0;
 | 
			
		||||
const DOWN = 1;
 | 
			
		||||
 | 
			
		||||
const PadChooser = new Lang.Class({
 | 
			
		||||
    Name: 'PadChooser',
 | 
			
		||||
 | 
			
		||||
    _init: function (device, groupDevices) {
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'pad-chooser-button',
 | 
			
		||||
                                     toggle_mode: true,
 | 
			
		||||
                                     x_fill: false,
 | 
			
		||||
                                     y_fill: false,
 | 
			
		||||
                                     x_align: St.Align.MIDDLE,
 | 
			
		||||
                                     y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.currentDevice = device;
 | 
			
		||||
        this._padChooserMenu = null;
 | 
			
		||||
 | 
			
		||||
        let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
 | 
			
		||||
                                  icon_name: 'pan-down-symbolic',
 | 
			
		||||
                                  accessible_role: Atk.Role.ARROW });
 | 
			
		||||
        this.actor.set_child(arrow);
 | 
			
		||||
        this._ensureMenu(groupDevices);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, function (actor) {
 | 
			
		||||
            if (actor.get_checked()) {
 | 
			
		||||
                if (this._padChooserMenu != null)
 | 
			
		||||
                    this._padChooserMenu.open(true);
 | 
			
		||||
                else
 | 
			
		||||
                    this.set_checked(false);
 | 
			
		||||
            } else {
 | 
			
		||||
                this._padChooserMenu.close(true);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureMenu: function (devices) {
 | 
			
		||||
        this._padChooserMenu =  new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.TOP);
 | 
			
		||||
        this._padChooserMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); }));
 | 
			
		||||
        this._padChooserMenu.actor.hide();
 | 
			
		||||
        Main.uiGroup.add_actor(this._padChooserMenu.actor);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < devices.length; i++) {
 | 
			
		||||
            let device = devices[i];
 | 
			
		||||
            if (device == this.currentDevice)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            this._padChooserMenu.addAction(device.get_device_name(), () => {
 | 
			
		||||
                this.emit('pad-selected', device);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function () {
 | 
			
		||||
        this._padChooserMenu.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update: function (devices) {
 | 
			
		||||
        if (this._padChooserMenu)
 | 
			
		||||
            this._padChooserMenu.actor.destroy();
 | 
			
		||||
        this.actor.set_checked(false);
 | 
			
		||||
        this._ensureMenu(devices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function () {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(PadChooser.prototype);
 | 
			
		||||
 | 
			
		||||
const KeybindingEntry = new Lang.Class({
 | 
			
		||||
    Name: 'KeybindingEntry',
 | 
			
		||||
 | 
			
		||||
    _init: function () {
 | 
			
		||||
        this.actor = new St.Entry({ hint_text: _("New shortcut…"),
 | 
			
		||||
                                    style: 'width: 10em' });
 | 
			
		||||
        this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent: function (actor, event) {
 | 
			
		||||
        if (event.type() != Clutter.EventType.KEY_PRESS)
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let str = Gtk.accelerator_name_with_keycode(null,
 | 
			
		||||
                                                    event.get_key_symbol(),
 | 
			
		||||
                                                    event.get_key_code(),
 | 
			
		||||
                                                    event.get_state());
 | 
			
		||||
        this.actor.set_text(str);
 | 
			
		||||
        this.emit('keybinding-edited', str);
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(KeybindingEntry.prototype);
 | 
			
		||||
 | 
			
		||||
const ActionComboBox = new Lang.Class({
 | 
			
		||||
    Name: 'ActionComboBox',
 | 
			
		||||
 | 
			
		||||
    _init: function () {
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'button' });
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onButtonClicked));
 | 
			
		||||
        this.actor.set_toggle_mode(true);
 | 
			
		||||
 | 
			
		||||
        let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
 | 
			
		||||
                                                spacing: 6 });
 | 
			
		||||
        let box = new St.Widget({ layout_manager: boxLayout });
 | 
			
		||||
        this.actor.set_child(box);
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label({ style_class: 'combo-box-label' });
 | 
			
		||||
        box.add_child(this._label)
 | 
			
		||||
 | 
			
		||||
        let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
 | 
			
		||||
                                  icon_name: 'pan-down-symbolic',
 | 
			
		||||
                                  accessible_role: Atk.Role.ARROW,
 | 
			
		||||
                                  y_expand: true,
 | 
			
		||||
                                  y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        box.add_child(arrow);
 | 
			
		||||
 | 
			
		||||
        this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP);
 | 
			
		||||
        this._editMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); }));
 | 
			
		||||
        this._editMenu.actor.hide();
 | 
			
		||||
        Main.uiGroup.add_actor(this._editMenu.actor);
 | 
			
		||||
 | 
			
		||||
        this._actionLabels = new Map();
 | 
			
		||||
        this._actionLabels.set(GDesktopEnums.PadButtonAction.NONE, _("Application defined"));
 | 
			
		||||
        this._actionLabels.set(GDesktopEnums.PadButtonAction.HELP, _("Show on-screen help"));
 | 
			
		||||
        this._actionLabels.set(GDesktopEnums.PadButtonAction.SWITCH_MONITOR, _("Switch monitor"));
 | 
			
		||||
        this._actionLabels.set(GDesktopEnums.PadButtonAction.KEYBINDING, _("Assign keystroke"));
 | 
			
		||||
 | 
			
		||||
        this._buttonItems = [];
 | 
			
		||||
 | 
			
		||||
        for (let [action, label] of this._actionLabels.entries()) {
 | 
			
		||||
            let selectedAction = action;
 | 
			
		||||
            let item = this._editMenu.addAction(label, Lang.bind(this, function() { this._onActionSelected(selectedAction) }));
 | 
			
		||||
 | 
			
		||||
            /* These actions only apply to pad buttons */
 | 
			
		||||
            if (selectedAction == GDesktopEnums.PadButtonAction.HELP ||
 | 
			
		||||
                selectedAction == GDesktopEnums.PadButtonAction.SWITCH_MONITOR)
 | 
			
		||||
                this._buttonItems.push(item);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setButtonActionsActive: function (active) {
 | 
			
		||||
        this._buttonItems.forEach(item => { item.setSensitive(active); });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
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, action) {
 | 
			
		||||
        this._buttonSettings = settings;
 | 
			
		||||
 | 
			
		||||
        this._currentAction = this._buttonSettings.get_enum('action');
 | 
			
		||||
        this._currentKeybinding = this._buttonSettings.get_string('keybinding');
 | 
			
		||||
        this._actionComboBox.setAction(this._currentAction);
 | 
			
		||||
        this._updateKeybindingEntryState();
 | 
			
		||||
 | 
			
		||||
        let isButton = (action == Meta.PadActionType.BUTTON);
 | 
			
		||||
        this._actionComboBox.setButtonActionsActive(isButton);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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._curEdited = null;
 | 
			
		||||
        this._prevEdited = 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 + 1).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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _applyLabel: function(label, action, idx, dir, str) {
 | 
			
		||||
        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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    stopEdition: function (continues, str) {
 | 
			
		||||
        this._editorActor.hide();
 | 
			
		||||
 | 
			
		||||
        if (this._prevEdited) {
 | 
			
		||||
            let [label, action, idx, dir] = this._prevEdited;
 | 
			
		||||
            this._applyLabel(label, action, idx, dir, str);
 | 
			
		||||
            this._prevEdited = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._curEdited) {
 | 
			
		||||
            let [label, action, idx, dir] = this._curEdited;
 | 
			
		||||
            this._applyLabel(label, action, idx, dir, str);
 | 
			
		||||
            if (continues)
 | 
			
		||||
                this._prevEdited = this._curEdited;
 | 
			
		||||
            this._curEdited = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startEdition: function(action, idx, dir) {
 | 
			
		||||
        let editedLabel;
 | 
			
		||||
 | 
			
		||||
        if (this._curEdited)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._labels.length; i++) {
 | 
			
		||||
            let [label, itemAction, itemIdx, itemDir] = this._labels[i];
 | 
			
		||||
            if (action == itemAction && idx == itemIdx && dir == itemDir) {
 | 
			
		||||
                this._curEdited = this._labels[i];
 | 
			
		||||
                editedLabel = label;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._curEdited == null)
 | 
			
		||||
            return;
 | 
			
		||||
        let [found] = this.getLabelCoords(action, idx, dir);
 | 
			
		||||
        if (!found)
 | 
			
		||||
            return;
 | 
			
		||||
        this._editorActor.show();
 | 
			
		||||
        editedLabel.hide();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const PadOsd = new Lang.Class({
 | 
			
		||||
    Name: 'PadOsd',
 | 
			
		||||
 | 
			
		||||
    _init: function (padDevice, settings, imagePath, editionMode, monitorIndex) {
 | 
			
		||||
        this.padDevice = padDevice;
 | 
			
		||||
        this._groupPads = [ padDevice ];
 | 
			
		||||
        this._settings = settings;
 | 
			
		||||
        this._imagePath = imagePath;
 | 
			
		||||
        this._editionMode = editionMode;
 | 
			
		||||
        this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        this._padChooser = null;
 | 
			
		||||
 | 
			
		||||
        let deviceManager = Clutter.DeviceManager.get_default();
 | 
			
		||||
        this._deviceAddedId = deviceManager.connect('device-added', Lang.bind(this, function (manager, device) {
 | 
			
		||||
            if (device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE &&
 | 
			
		||||
                this.padDevice.is_grouped(device)) {
 | 
			
		||||
                this._groupPads.push(device);
 | 
			
		||||
                this._updatePadChooser();
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
        this._deviceRemovedId = deviceManager.connect('device-removed', Lang.bind(this, function (manager, device) {
 | 
			
		||||
            // If the device is being removed, destroy the padOsd.
 | 
			
		||||
            if (device == this.padDevice) {
 | 
			
		||||
                this.destroy();
 | 
			
		||||
            } else if (this._groupPads.indexOf(device) != -1) {
 | 
			
		||||
                // Or update the pad chooser if the device belongs to
 | 
			
		||||
                // the same group.
 | 
			
		||||
                this._groupPads.splice(this._groupPads.indexOf(device), 1);
 | 
			
		||||
                this._updatePadChooser();
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        deviceManager.list_devices().forEach(Lang.bind(this, function(device) {
 | 
			
		||||
            if (device != this.padDevice &&
 | 
			
		||||
                device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE &&
 | 
			
		||||
                this.padDevice.is_grouped(device))
 | 
			
		||||
                this._groupPads.push(device);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'pad-osd-window',
 | 
			
		||||
                                        x_expand: true,
 | 
			
		||||
                                        y_expand: true,
 | 
			
		||||
                                        vertical: true,
 | 
			
		||||
                                        reactive: true });
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
 | 
			
		||||
        this._monitorIndex = monitorIndex;
 | 
			
		||||
        let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
 | 
			
		||||
        this.actor.add_constraint(constraint);
 | 
			
		||||
 | 
			
		||||
        this._titleBox = new St.BoxLayout({ style_class: 'pad-osd-title-box',
 | 
			
		||||
                                            vertical: false,
 | 
			
		||||
                                            x_expand: false,
 | 
			
		||||
                                            x_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_actor(this._titleBox);
 | 
			
		||||
 | 
			
		||||
        let labelBox = new St.BoxLayout({ style_class: 'pad-osd-title-menu-box',
 | 
			
		||||
                                          vertical: true });
 | 
			
		||||
        this._titleBox.add_actor(labelBox);
 | 
			
		||||
 | 
			
		||||
        this._titleLabel = new St.Label({ style: 'font-side: larger; font-weight: bold;',
 | 
			
		||||
                                          x_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this._titleLabel.clutter_text.set_text(padDevice.get_device_name());
 | 
			
		||||
        labelBox.add_actor(this._titleLabel);
 | 
			
		||||
 | 
			
		||||
        this._tipLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        labelBox.add_actor(this._tipLabel);
 | 
			
		||||
 | 
			
		||||
        this._updatePadChooser();
 | 
			
		||||
 | 
			
		||||
        this._actionEditor = new ActionEditor();
 | 
			
		||||
        this._actionEditor.connect('done', Lang.bind(this, this._endActionEdition));
 | 
			
		||||
 | 
			
		||||
        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++) {
 | 
			
		||||
            let [found] = this._padDiagram.getRingLabelCoords(i, CW);
 | 
			
		||||
            if (!found)
 | 
			
		||||
                break;
 | 
			
		||||
            this._createLabel(Meta.PadActionType.RING, i, CW);
 | 
			
		||||
            this._createLabel(Meta.PadActionType.RING, i, CCW);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < padDevice.get_n_strips(); i++) {
 | 
			
		||||
            let [found] = this._padDiagram.getStripLabelCoords(i, UP);
 | 
			
		||||
            if (!found)
 | 
			
		||||
                break;
 | 
			
		||||
            this._createLabel(Meta.PadActionType.STRIP, i, UP);
 | 
			
		||||
            this._createLabel(Meta.PadActionType.STRIP, i, DOWN);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let buttonBox = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                         x_expand: true,
 | 
			
		||||
                                         x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                         y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_actor(buttonBox);
 | 
			
		||||
        this._editButton = new St.Button({ label: _("Edit…"),
 | 
			
		||||
                                           style_class: 'button',
 | 
			
		||||
                                           x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._editButton.connect('clicked', Lang.bind(this, function () { this.setEditionMode(true) }));
 | 
			
		||||
        buttonBox.add_actor(this._editButton);
 | 
			
		||||
 | 
			
		||||
        this._syncEditionMode();
 | 
			
		||||
        Main.pushModal(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updatePadChooser: function () {
 | 
			
		||||
        if (this._groupPads.length > 1) {
 | 
			
		||||
            if (this._padChooser == null) {
 | 
			
		||||
                this._padChooser = new PadChooser(this.padDevice, this._groupPads)
 | 
			
		||||
                this._padChooser.connect('pad-selected', Lang.bind(this, function (chooser, pad) {
 | 
			
		||||
                    this._requestForOtherPad(pad);
 | 
			
		||||
                }));
 | 
			
		||||
                this._titleBox.add_child(this._padChooser.actor);
 | 
			
		||||
            } else {
 | 
			
		||||
                this._padChooser.update(this._groupPads);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this._padChooser != null) {
 | 
			
		||||
            this._padChooser.destroy();
 | 
			
		||||
            this._padChooser = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _requestForOtherPad: function (pad) {
 | 
			
		||||
        if (pad == this.padDevice ||
 | 
			
		||||
            this._groupPads.indexOf(pad) == -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let editionMode = this._editionMode;
 | 
			
		||||
        this.destroy();
 | 
			
		||||
        global.display.request_pad_osd(pad, editionMode);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createLabel: function (type, number, dir) {
 | 
			
		||||
        let str = global.display.get_pad_action_label(this.padDevice, type, number);
 | 
			
		||||
        let label = new St.Label({ text: str ? str : _("None") });
 | 
			
		||||
        this._padDiagram.addLabel(label, type, number, dir);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent : function (actor, event) {
 | 
			
		||||
        if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS &&
 | 
			
		||||
            event.get_source_device() == this.padDevice) {
 | 
			
		||||
            this._padDiagram.activateButton(event.get_button());
 | 
			
		||||
            let isModeSwitch = this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
 | 
			
		||||
 | 
			
		||||
            /* Buttons that switch between modes cannot be edited */
 | 
			
		||||
            if (this._editionMode && !isModeSwitch)
 | 
			
		||||
                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._editedAction != null)
 | 
			
		||||
                this._endActionEdition();
 | 
			
		||||
            else
 | 
			
		||||
                this.destroy();
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        } else if (event.get_source_device() == this.padDevice &&
 | 
			
		||||
                   event.type() == Clutter.EventType.PAD_STRIP) {
 | 
			
		||||
            if (this._editionMode) {
 | 
			
		||||
                let [retval, number, mode] = event.get_pad_event_details();
 | 
			
		||||
                this._startStripActionEdition(number, UP, mode);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.get_source_device() == this.padDevice &&
 | 
			
		||||
                   event.type() == Clutter.EventType.PAD_RING) {
 | 
			
		||||
            if (this._editionMode) {
 | 
			
		||||
                let [retval, number, mode] = event.get_pad_event_details();
 | 
			
		||||
                this._startRingActionEdition(number, CCW, mode);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the event comes from another pad in the same group,
 | 
			
		||||
        // show the OSD for it.
 | 
			
		||||
        if (this._groupPads.indexOf(event.get_source_device()) != -1) {
 | 
			
		||||
            this._requestForOtherPad(event.get_source_device());
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncEditionMode: function () {
 | 
			
		||||
        this._editButton.set_reactive(!this._editionMode);
 | 
			
		||||
        this._editButton.save_easing_state();
 | 
			
		||||
        this._editButton.set_easing_duration(200);
 | 
			
		||||
        this._editButton.set_opacity(this._editionMode ? 128 : 255);
 | 
			
		||||
        this._editButton.restore_easing_state();
 | 
			
		||||
 | 
			
		||||
        let title;
 | 
			
		||||
 | 
			
		||||
        if (this._editionMode) {
 | 
			
		||||
            title = _("Press a button to configure");
 | 
			
		||||
            this._tipLabel.set_text(_("Press Esc to exit"));
 | 
			
		||||
        } else {
 | 
			
		||||
            title = this.padDevice.get_device_name();
 | 
			
		||||
            this._tipLabel.set_text(_("Press any key to exit"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + title + '</b></span>');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isEditedAction: function (type, number, dir) {
 | 
			
		||||
        if (!this._editedAction)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return (this._editedAction.type == type &&
 | 
			
		||||
                this._editedAction.number == number &&
 | 
			
		||||
                this._editedAction.dir == dir);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _followUpActionEdition: function (str) {
 | 
			
		||||
        let { type, dir, number, mode } = this._editedAction;
 | 
			
		||||
        let hasNextAction = (type == Meta.PadActionType.RING && dir == CCW ||
 | 
			
		||||
                             type == Meta.PadActionType.STRIP && dir == UP);
 | 
			
		||||
        if (!hasNextAction)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._padDiagram.stopEdition(true, str);
 | 
			
		||||
        this._editedAction = null;
 | 
			
		||||
        if (type == Meta.PadActionType.RING)
 | 
			
		||||
            this._startRingActionEdition(number, CW, mode);
 | 
			
		||||
        else
 | 
			
		||||
            this._startStripActionEdition(number, DOWN, mode);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _endActionEdition: function () {
 | 
			
		||||
        this._actionEditor.close();
 | 
			
		||||
 | 
			
		||||
        if (this._editedAction != null) {
 | 
			
		||||
            let str = global.display.get_pad_action_label(this.padDevice,
 | 
			
		||||
                                                          this._editedAction.type,
 | 
			
		||||
                                                          this._editedAction.number);
 | 
			
		||||
            if (this._followUpActionEdition(str))
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            this._padDiagram.stopEdition(false, str ? str : _("None"))
 | 
			
		||||
            this._editedAction = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._editedActionSettings = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startActionEdition: function (key, type, number, dir, mode) {
 | 
			
		||||
        if (this._isEditedAction(type, number, dir))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._endActionEdition();
 | 
			
		||||
        this._editedAction = { type, number, dir, mode };
 | 
			
		||||
 | 
			
		||||
        let settingsPath = this._settings.path + key + '/';
 | 
			
		||||
        this._editedActionSettings = Gio.Settings.new_with_path('org.gnome.desktop.peripherals.tablet.pad-button',
 | 
			
		||||
                                                                settingsPath);
 | 
			
		||||
        this._actionEditor.setSettings(this._editedActionSettings, type);
 | 
			
		||||
        this._padDiagram.startEdition(type, number, dir);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startButtonActionEdition: function (button) {
 | 
			
		||||
        let ch = String.fromCharCode('A'.charCodeAt() + button);
 | 
			
		||||
        let key = 'button' + ch;
 | 
			
		||||
        this._startActionEdition(key, Meta.PadActionType.BUTTON, button);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startRingActionEdition: function (ring, dir, mode) {
 | 
			
		||||
        let ch = String.fromCharCode('A'.charCodeAt() + ring);
 | 
			
		||||
        let key = 'ring%s-%s-mode-%d'.format(ch, dir == CCW ? 'ccw' : 'cw', mode);
 | 
			
		||||
        this._startActionEdition(key, Meta.PadActionType.RING, ring, dir, mode);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startStripActionEdition: function (strip, dir, mode) {
 | 
			
		||||
        let ch = String.fromCharCode('A'.charCodeAt() + strip);
 | 
			
		||||
        let key = 'strip%s-%s-mode-%d'.format(ch, dir == UP ? 'up' : 'down', mode);
 | 
			
		||||
        this._startActionEdition(key, Meta.PadActionType.STRIP, strip, dir, mode);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setEditionMode: function (editionMode) {
 | 
			
		||||
        if (this._editionMode == editionMode)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._editionMode = editionMode;
 | 
			
		||||
        this._syncEditionMode();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function () {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function () {
 | 
			
		||||
        Main.popModal(this.actor);
 | 
			
		||||
        this._actionEditor.close();
 | 
			
		||||
 | 
			
		||||
        let deviceManager = Clutter.DeviceManager.get_default();
 | 
			
		||||
        if (this._deviceRemovedId != 0) {
 | 
			
		||||
            deviceManager.disconnect(this._deviceRemovedId);
 | 
			
		||||
            this._deviceRemovedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._deviceAddedId != 0) {
 | 
			
		||||
            deviceManager.disconnect(this._deviceAddedId);
 | 
			
		||||
            this._deviceAddedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._capturedEventId != 0) {
 | 
			
		||||
            global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor = null;
 | 
			
		||||
        this.emit('closed');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(PadOsd.prototype);
 | 
			
		||||
 | 
			
		||||
const PadOsdIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.Wacom.PadOsd"> \
 | 
			
		||||
<method name="Show"> \
 | 
			
		||||
    <arg name="device_node" direction="in" type="o"/> \
 | 
			
		||||
    <arg name="edition_mode" direction="in" type="b"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const PadOsdService = new Lang.Class({
 | 
			
		||||
    Name: 'PadOsdService',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(PadOsdIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Wacom');
 | 
			
		||||
        Gio.DBus.session.own_name('org.gnome.Shell.Wacom.PadOsd', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ShowAsync: function(params, invocation) {
 | 
			
		||||
        let [deviceNode, editionMode] = params;
 | 
			
		||||
        let deviceManager = Clutter.DeviceManager.get_default();
 | 
			
		||||
        let devices = deviceManager.list_devices();
 | 
			
		||||
        let padDevice = null;
 | 
			
		||||
 | 
			
		||||
        devices.forEach(Lang.bind(this, function(device) {
 | 
			
		||||
            if (deviceNode == device.get_device_node())
 | 
			
		||||
                padDevice = device;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (padDevice == null ||
 | 
			
		||||
            padDevice.get_device_type() != Clutter.InputDeviceType.PAD_DEVICE) {
 | 
			
		||||
            invocation.return_error_literal(Gio.IOErrorEnum,
 | 
			
		||||
                                            Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                                            "Invalid params");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.display.request_pad_osd(padDevice, editionMode);
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(PadOsdService.prototype);
 | 
			
		||||
@@ -719,11 +719,9 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -747,7 +745,6 @@ 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);
 | 
			
		||||
@@ -1104,7 +1101,7 @@ const Panel = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuSet: function(indicator) {
 | 
			
		||||
        if (!indicator.menu || indicator.menu.hasOwnProperty('_openChangedId'))
 | 
			
		||||
        if (!indicator.menu || indicator.menu._openChangedId > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        indicator.menu._openChangedId = indicator.menu.connect('open-state-changed',
 | 
			
		||||
 
 | 
			
		||||
@@ -174,14 +174,8 @@ const Button = new Lang.Class({
 | 
			
		||||
        // menu is higher then the screen; it's useful if part of the menu is
 | 
			
		||||
        // scrollable so the minimum height is smaller than the natural height
 | 
			
		||||
        let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        let verticalMargins = this.menu.actor.margin_top + this.menu.actor.margin_bottom;
 | 
			
		||||
 | 
			
		||||
        // The workarea and margin dimensions are in physical pixels, but CSS
 | 
			
		||||
        // measures are in logical pixels, so make sure to consider the scale
 | 
			
		||||
        // factor when computing max-height
 | 
			
		||||
        let maxHeight = Math.round((workArea.height - verticalMargins) / scaleFactor);
 | 
			
		||||
        this.menu.actor.style = ('max-height: %spx;').format(maxHeight);
 | 
			
		||||
        this.menu.actor.style = ('max-height: ' + Math.round(workArea.height - verticalMargins) + 'px;');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -605,24 +605,6 @@ 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) {
 | 
			
		||||
@@ -785,11 +767,6 @@ const PopupMenu = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        // Disable toggling the menu by keyboard
 | 
			
		||||
        // when it cannot be toggled by pointer
 | 
			
		||||
        if (!actor.reactive)
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let navKey;
 | 
			
		||||
        switch (this._boxPointer.arrowSide) {
 | 
			
		||||
            case St.Side.TOP:
 | 
			
		||||
@@ -806,16 +783,6 @@ 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();
 | 
			
		||||
 
 | 
			
		||||
@@ -192,10 +192,6 @@ const RemoteMenu = new Lang.Class({
 | 
			
		||||
                                              _removeItem.bind(null, this));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get actionGroup() {
 | 
			
		||||
        return this._actionGroup;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._tracker.destroy();
 | 
			
		||||
        this.parent();
 | 
			
		||||
 
 | 
			
		||||
@@ -200,7 +200,6 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
        this.appInfo = appInfo;
 | 
			
		||||
        this.id = appInfo.get_id();
 | 
			
		||||
        this.isRemoteProvider = true;
 | 
			
		||||
        this.canLaunchSearch = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(size, meta) {
 | 
			
		||||
@@ -279,8 +278,7 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                               name: metas[i]['name'],
 | 
			
		||||
                               description: metas[i]['description'],
 | 
			
		||||
                               createIcon: Lang.bind(this,
 | 
			
		||||
                                                     this.createIcon, metas[i]),
 | 
			
		||||
                               clipboardText: metas[i]['clipboardText'] });
 | 
			
		||||
                                                     this.createIcon, metas[i]) });
 | 
			
		||||
        }
 | 
			
		||||
        callback(resultMetas);
 | 
			
		||||
    },
 | 
			
		||||
@@ -299,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), false);
 | 
			
		||||
        this.appInfo.launch([], global.create_app_launch_context(0, -1));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,6 @@ const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
 | 
			
		||||
const LOCK_ENABLED_KEY = 'lock-enabled';
 | 
			
		||||
const LOCK_DELAY_KEY = 'lock-delay';
 | 
			
		||||
 | 
			
		||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
 | 
			
		||||
const DISABLE_LOCK_KEY = 'disable-lock-screen';
 | 
			
		||||
 | 
			
		||||
const LOCKED_STATE_STR = 'screenShield.locked';
 | 
			
		||||
// fraction of screen height the arrow must reach before completing
 | 
			
		||||
// the slide up automatically
 | 
			
		||||
@@ -352,6 +349,7 @@ 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));
 | 
			
		||||
@@ -379,22 +377,6 @@ 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');
 | 
			
		||||
@@ -402,8 +384,6 @@ const Arrow = new Lang.Class({
 | 
			
		||||
            this._shadowHelper = St.ShadowHelper.new(this._shadow);
 | 
			
		||||
        else
 | 
			
		||||
            this._shadowHelper = null;
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_paint: function() {
 | 
			
		||||
@@ -544,9 +524,6 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::' + LOCK_ENABLED_KEY, Lang.bind(this, this._syncInhibitor));
 | 
			
		||||
 | 
			
		||||
        this._lockSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
 | 
			
		||||
        this._lockSettings.connect('changed::' + DISABLE_LOCK_KEY, Lang.bind(this, this._syncInhibitor));
 | 
			
		||||
 | 
			
		||||
        this._isModal = false;
 | 
			
		||||
        this._hasLockScreen = false;
 | 
			
		||||
        this._isGreeter = false;
 | 
			
		||||
@@ -582,9 +559,6 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        if (prevIsActive != this._isActive)
 | 
			
		||||
            this.emit('active-changed');
 | 
			
		||||
 | 
			
		||||
        if (this._loginSession)
 | 
			
		||||
            this._loginSession.SetLockedHintRemote(active);
 | 
			
		||||
 | 
			
		||||
        this._syncInhibitor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -671,10 +645,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        let isEnter = (symbol == Clutter.KEY_Return ||
 | 
			
		||||
                       symbol == Clutter.KEY_KP_Enter ||
 | 
			
		||||
                       symbol == Clutter.KEY_ISO_Enter);
 | 
			
		||||
        let isEscape = (symbol == Clutter.KEY_Escape);
 | 
			
		||||
        let isLiftChar = (GLib.unichar_isprint(unichar) &&
 | 
			
		||||
                          (this._isLocked || !GLib.unichar_isgraph(unichar)));
 | 
			
		||||
        if (!isEnter && !isEscape && !isLiftChar)
 | 
			
		||||
        if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (this._isLocked &&
 | 
			
		||||
@@ -707,10 +678,8 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncInhibitor: function() {
 | 
			
		||||
        let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY);
 | 
			
		||||
        let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY);
 | 
			
		||||
        let inhibit = (this._loginSession && this._loginSession.Active &&
 | 
			
		||||
                       !this._isActive && lockEnabled && !lockLocked);
 | 
			
		||||
                       !this._isActive && this._settings.get_boolean(LOCK_ENABLED_KEY));
 | 
			
		||||
        if (inhibit) {
 | 
			
		||||
            this._loginManager.inhibit(_("GNOME needs to lock the screen"),
 | 
			
		||||
                                       Lang.bind(this, function(inhibitor) {
 | 
			
		||||
@@ -739,7 +708,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[i].opacity = 0;
 | 
			
		||||
            arrows.opacity = 0;
 | 
			
		||||
            Tweener.addTween(arrows[i],
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               delay: unitaryDelay * (N_ARROWS - (i + 1)),
 | 
			
		||||
@@ -858,7 +827,6 @@ 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)
 | 
			
		||||
@@ -1298,11 +1266,6 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    lock: function(animate) {
 | 
			
		||||
        if (this._lockSettings.get_boolean(DISABLE_LOCK_KEY)) {
 | 
			
		||||
            log('Screen lock is locked down, not locking') // lock, lock - who's there?
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Warn the user if we can't become modal
 | 
			
		||||
        if (!this._becomeModal()) {
 | 
			
		||||
            Main.notifyError(_("Unable to lock"),
 | 
			
		||||
 
 | 
			
		||||
@@ -152,8 +152,6 @@ const SearchResultsBase = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._resultDisplays = {};
 | 
			
		||||
 | 
			
		||||
        this._clipboard = St.Clipboard.get_default();
 | 
			
		||||
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -183,8 +181,6 @@ const SearchResultsBase = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _activateResult: function(result, id) {
 | 
			
		||||
        this.provider.activateResult(id, this._terms);
 | 
			
		||||
        if (result.metaInfo.clipboardText)
 | 
			
		||||
            this._clipboard.set_text(St.ClipboardType.CLIPBOARD, result.metaInfo.clipboardText);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -142,15 +142,15 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
        for (let param in params)
 | 
			
		||||
            params[param] = params[param].deep_unpack();
 | 
			
		||||
 | 
			
		||||
        let monitorIndex = params['monitor'] || -1;
 | 
			
		||||
        let label = params['label'] || undefined;
 | 
			
		||||
        let level = params['level'] || undefined;
 | 
			
		||||
        let monitorIndex = -1;
 | 
			
		||||
        if (params['monitor'] >= 0)
 | 
			
		||||
            monitorIndex = params['monitor'];
 | 
			
		||||
 | 
			
		||||
        let icon = null;
 | 
			
		||||
        if (params['icon'])
 | 
			
		||||
            icon = Gio.Icon.new_for_string(params['icon']);
 | 
			
		||||
 | 
			
		||||
        Main.osdWindowManager.show(monitorIndex, icon, label, level);
 | 
			
		||||
        Main.osdWindowManager.show(monitorIndex, icon, params['label'], params['level']);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FocusApp: function(id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -380,7 +380,7 @@ const ShellMountPasswordDialog = new Lang.Class({
 | 
			
		||||
        this.setInitialKeyFocus(this._passwordEntry);
 | 
			
		||||
 | 
			
		||||
        this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
 | 
			
		||||
                                                 text: _("Sorry, that didn’t work. Please try again.") });
 | 
			
		||||
                                                 text: _("Sorry, that didn\'t work. Please try again.") });
 | 
			
		||||
        this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._errorMessageLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        this._errorMessageLabel.hide();
 | 
			
		||||
 
 | 
			
		||||
@@ -102,8 +102,8 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
        if (this._syncMenuVisibilityIdle)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._syncMenuVisibilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._syncMenuVisibilityIdle, '[gnome-shell] this._syncMenuVisibility');
 | 
			
		||||
        this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._syncMenuVisbilityIdle, '[gnome-shell] this._syncMenuVisibility');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildItemExtended: function(string, initial_value, writable, on_set) {
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        else if (nConnectedDevices == -1)
 | 
			
		||||
            this._item.label.text = _("Off");
 | 
			
		||||
        else
 | 
			
		||||
            this._item.label.text = _("On");
 | 
			
		||||
            this._item.label.text = _("Not In Use");
 | 
			
		||||
 | 
			
		||||
        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(interactive) {
 | 
			
		||||
        this.emit('activate', !!interactive);
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getXkbId: function() {
 | 
			
		||||
@@ -109,7 +109,7 @@ const InputSourcePopup = new Lang.Class({
 | 
			
		||||
    _finish : function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._items[this._selectedIndex].activate(true);
 | 
			
		||||
        this._items[this._selectedIndex].activate();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -159,14 +159,6 @@ const InputSourceSettings = new Lang.Class({
 | 
			
		||||
        return [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get mruSources() {
 | 
			
		||||
        return [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set mruSources(sourcesList) {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get keyboardOptions() {
 | 
			
		||||
        return [];
 | 
			
		||||
    },
 | 
			
		||||
@@ -259,7 +251,6 @@ 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',
 | 
			
		||||
 | 
			
		||||
@@ -270,9 +261,9 @@ const InputSourceSessionSettings = new Lang.Class({
 | 
			
		||||
        this._settings.connect('changed::' + this._KEY_PER_WINDOW, Lang.bind(this, this._emitPerWindowChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSourcesList: function(key) {
 | 
			
		||||
    get inputSources() {
 | 
			
		||||
        let sourcesList = [];
 | 
			
		||||
        let sources = this._settings.get_value(key);
 | 
			
		||||
        let sources = this._settings.get_value(this._KEY_INPUT_SOURCES);
 | 
			
		||||
        let nSources = sources.n_children();
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < nSources; i++) {
 | 
			
		||||
@@ -282,19 +273,6 @@ 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);
 | 
			
		||||
    },
 | 
			
		||||
@@ -394,7 +372,7 @@ const InputSourceManager = new Lang.Class({
 | 
			
		||||
        while (!(is = this._inputSources[nextIndex]))
 | 
			
		||||
            nextIndex += 1;
 | 
			
		||||
 | 
			
		||||
        is.activate(true);
 | 
			
		||||
        is.activate();
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -422,25 +400,6 @@ 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];
 | 
			
		||||
@@ -457,7 +416,7 @@ const InputSourceManager = new Lang.Class({
 | 
			
		||||
        this._changePerWindowSource();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _activateInputSource: function(is, interactive) {
 | 
			
		||||
    _activateInputSource: function(is) {
 | 
			
		||||
        KeyboardManager.holdKeyboard();
 | 
			
		||||
        this._keyboardManager.apply(is.xkbId);
 | 
			
		||||
 | 
			
		||||
@@ -475,54 +434,6 @@ 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() {
 | 
			
		||||
@@ -599,10 +510,30 @@ const InputSourceManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.emit('sources-changed');
 | 
			
		||||
 | 
			
		||||
        this._updateMruSources();
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        if (this._mruSources.length > 0)
 | 
			
		||||
            this._mruSources[0].activate(false);
 | 
			
		||||
            this._mruSources[0].activate();
 | 
			
		||||
 | 
			
		||||
        // All ibus engines are preloaded here to reduce the launching time
 | 
			
		||||
        // when users switch the input sources.
 | 
			
		||||
@@ -711,7 +642,7 @@ const InputSourceManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (window._currentSource)
 | 
			
		||||
            window._currentSource.activate(false);
 | 
			
		||||
            window._currentSource.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sourcesPerWindowChanged: function() {
 | 
			
		||||
@@ -832,10 +763,7 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
            let is = this._inputSourceManager.inputSources[i];
 | 
			
		||||
 | 
			
		||||
            let menuItem = new LayoutMenuItem(is.displayName, is.shortName);
 | 
			
		||||
            menuItem.connect('activate', function() {
 | 
			
		||||
                is.activate(true);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            menuItem.connect('activate', Lang.bind(is, is.activate));
 | 
			
		||||
            let indicatorLabel = new St.Label({ text: is.shortName,
 | 
			
		||||
                                                visible: false });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
@@ -9,16 +8,14 @@ const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const PermissionStore = imports.misc.permissionStore;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const LOCATION_SCHEMA = 'org.gnome.system.location';
 | 
			
		||||
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
 | 
			
		||||
const ENABLED = 'enabled';
 | 
			
		||||
 | 
			
		||||
const APP_PERMISSIONS_TABLE = 'gnome';
 | 
			
		||||
const APP_PERMISSIONS_TABLE = 'desktop';
 | 
			
		||||
const APP_PERMISSIONS_ID = 'geolocation';
 | 
			
		||||
 | 
			
		||||
const GeoclueAccuracyLevel = {
 | 
			
		||||
@@ -30,15 +27,6 @@ const GeoclueAccuracyLevel = {
 | 
			
		||||
    EXACT: 8
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function accuracyLevelToString(accuracyLevel) {
 | 
			
		||||
    for (let key in GeoclueAccuracyLevel) {
 | 
			
		||||
        if (GeoclueAccuracyLevel[key] == accuracyLevel)
 | 
			
		||||
            return key;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 'NONE';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var GeoclueIface = '<node> \
 | 
			
		||||
  <interface name="org.freedesktop.GeoClue2.Manager"> \
 | 
			
		||||
    <property name="InUse" type="b" access="read"/> \
 | 
			
		||||
@@ -63,6 +51,26 @@ var AgentIface = '<node> \
 | 
			
		||||
  </interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
var XdgAppIface = '<node> \
 | 
			
		||||
  <interface name="org.freedesktop.XdgApp.PermissionStore"> \
 | 
			
		||||
    <method name="Lookup"> \
 | 
			
		||||
      <arg name="table" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="id" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="permissions" type="a{sas}" direction="out"/> \
 | 
			
		||||
      <arg name="data" type="v" direction="out"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
    <method name="Set"> \
 | 
			
		||||
      <arg name="table" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="create" type="b" direction="in"/> \
 | 
			
		||||
      <arg name="id" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="app_permissions" type="a{sas}" direction="in"/> \
 | 
			
		||||
      <arg name="data" type="v" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
  </interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const PermissionStore = Gio.DBusProxy.makeProxyWrapper(XdgAppIface);
 | 
			
		||||
 | 
			
		||||
const Indicator = new Lang.Class({
 | 
			
		||||
    Name: 'LocationIndicator',
 | 
			
		||||
    Extends: PanelMenu.SystemIndicator,
 | 
			
		||||
@@ -108,18 +116,152 @@ const Indicator = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    AuthorizeAppAsync: function(params, invocation) {
 | 
			
		||||
        let [desktopId, reqAccuracyLevel] = params;
 | 
			
		||||
        let [desktop_id, reqAccuracyLevel] = params;
 | 
			
		||||
        log("%s is requesting location".format(desktop_id));
 | 
			
		||||
 | 
			
		||||
        let authorizer = new AppAuthorizer(desktopId,
 | 
			
		||||
                                           reqAccuracyLevel,
 | 
			
		||||
                                           this._permStoreProxy,
 | 
			
		||||
                                           this._getMaxAccuracyLevel());
 | 
			
		||||
        let callback = function(level, timesAllowed) {
 | 
			
		||||
            if (level >= GeoclueAccuracyLevel.NONE && timesAllowed > 2) {
 | 
			
		||||
                log("%s is in store".format(desktop_id));
 | 
			
		||||
                let accuracyLevel = clamp(reqAccuracyLevel, 0, level);
 | 
			
		||||
                this._completeAuthorizeApp(desktop_id,
 | 
			
		||||
                                           accuracyLevel,
 | 
			
		||||
                                           timesAllowed,
 | 
			
		||||
                                           invocation);
 | 
			
		||||
            } else {
 | 
			
		||||
                log("%s not in store".format(desktop_id));
 | 
			
		||||
                this._userAuthorizeApp(desktop_id,
 | 
			
		||||
                                       reqAccuracyLevel,
 | 
			
		||||
                                       timesAllowed,
 | 
			
		||||
                                       invocation);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._fetchPermissionFromStore(desktop_id, Lang.bind(this, callback);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        authorizer.authorize(Lang.bind(this, function(accuracyLevel) {
 | 
			
		||||
            let ret = (accuracyLevel != GeoclueAccuracyLevel.NONE);
 | 
			
		||||
    _userAuthorizeApp: function(desktopId, reqAccuracyLevel, timesAllowed, invocation) {
 | 
			
		||||
        var appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        var app = appSystem.lookup_app(desktopId + ".desktop");
 | 
			
		||||
        if (app == null) {
 | 
			
		||||
            this._completeAuthorizeApp(desktopId,
 | 
			
		||||
                                       GeoclueAccuracyLevel.NONE,
 | 
			
		||||
                                       timesAllowed,
 | 
			
		||||
                                       invocation);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var name = app.get_name();
 | 
			
		||||
        var icon = app.get_app_info().get_icon();
 | 
			
		||||
        var reason = app.get_string("X-Geoclue-Reason");
 | 
			
		||||
        var allowCallback = function() {
 | 
			
		||||
            this._completeAuthorizeApp(desktopId,
 | 
			
		||||
                                       reqAccuracyLevel,
 | 
			
		||||
                                       timesAllowed,
 | 
			
		||||
                                       invocation);
 | 
			
		||||
        };
 | 
			
		||||
        var denyCallback = function() {
 | 
			
		||||
            this._completeAuthorizeApp(desktopId,
 | 
			
		||||
                                       GeoclueAccuracyLevel.NONE,
 | 
			
		||||
                                       timesAllowed,
 | 
			
		||||
                                       invocation);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this._showAppAuthDialog(name,
 | 
			
		||||
                                reason,
 | 
			
		||||
                                icon,
 | 
			
		||||
                                Lang.bind(this, allowCallback),
 | 
			
		||||
                                Lang.bind(this, denyCallback));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showAppAuthDialog: function(name, reason, icon, allowCallback, denyCallback) {
 | 
			
		||||
        if (this._dialog == null)
 | 
			
		||||
            this._dialog = new GeolocationDialog(name, reason, icon);
 | 
			
		||||
        else
 | 
			
		||||
            this._dialog.update(name, reason, icon);
 | 
			
		||||
 | 
			
		||||
        let closedId = this._dialog.connect('closed', function() {
 | 
			
		||||
            this._dialog.disconnect(closedId);
 | 
			
		||||
            if (this._dialog.allowed)
 | 
			
		||||
                allowCallback ();
 | 
			
		||||
            else
 | 
			
		||||
                denyCallback ();
 | 
			
		||||
        }.bind(this));
 | 
			
		||||
 | 
			
		||||
        this._dialog.open(global.get_current_time ());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _completeAuthorizeApp: function(desktopId,
 | 
			
		||||
                                    accuracyLevel,
 | 
			
		||||
                                    timesAllowed,
 | 
			
		||||
                                    invocation) {
 | 
			
		||||
        if (accuracyLevel == GeoclueAccuracyLevel.NONE) {
 | 
			
		||||
            invocation.return_value(GLib.Variant.new('(bu)',
 | 
			
		||||
                                                     [ret, accuracyLevel]));
 | 
			
		||||
        }));
 | 
			
		||||
                                                     [false, accuracyLevel]));
 | 
			
		||||
            this._saveToPermissionStore(desktopId, accuracyLevel, 0);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let allowedAccuracyLevel = clamp(accuracyLevel,
 | 
			
		||||
                                         0,
 | 
			
		||||
                                         this._getMaxAccuracyLevel());
 | 
			
		||||
        invocation.return_value(GLib.Variant.new('(bu)',
 | 
			
		||||
                                                 [true, allowedAccuracyLevel]));
 | 
			
		||||
 | 
			
		||||
        this._saveToPermissionStore(desktopId, allowedAccuracyLevel, timesAllowed + 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fetchPermissionFromStore: function(desktopId, callback) {
 | 
			
		||||
        if (this._permStoreProxy == null) {
 | 
			
		||||
            callback (-1, 0);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._permStoreProxy.LookupRemote(APP_PERMISSIONS_TABLE,
 | 
			
		||||
                                          APP_PERMISSIONS_ID,
 | 
			
		||||
                                          function(result, error) {
 | 
			
		||||
            if (error != null) {
 | 
			
		||||
                log(error.message);
 | 
			
		||||
                callback(-1, 0);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let [permissions, data] = result;
 | 
			
		||||
            let permission = permissions[desktopId];
 | 
			
		||||
            if (permission == null) {
 | 
			
		||||
                callback(-1, 0);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let levelStr = permission[0];
 | 
			
		||||
            let level = GeoclueAccuracyLevel[levelStr.toUpperCase()] ||
 | 
			
		||||
                        GeoclueAccuracyLevel.NONE;
 | 
			
		||||
            let timesAllowed = data.get_byte();
 | 
			
		||||
 | 
			
		||||
            callback(level, timesAllowed);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _saveToPermissionStore: function(desktopId,
 | 
			
		||||
                                     allowedAccuracyLevel,
 | 
			
		||||
                                     timesAllowed) {
 | 
			
		||||
        if (timesAllowed > 2 || this._permStoreProxy == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let levelStr = Object.keys(GeoclueAccuracyLevel)[allowedAccuracyLevel]; 
 | 
			
		||||
        let permission = { desktopId: [levelStr] };
 | 
			
		||||
        let permissions = GLib.Variant.new('a{sas}', [permission]));
 | 
			
		||||
        let data = GLib.Variant.new('y', timesAllowed);
 | 
			
		||||
 | 
			
		||||
        this._permStoreProxy.SetRemote(APP_PERMISSIONS_TABLE,
 | 
			
		||||
                                       true,
 | 
			
		||||
                                       APP_PERMISSIONS_ID,
 | 
			
		||||
                                       permissions,
 | 
			
		||||
                                       data,
 | 
			
		||||
                                       function (result, error) {
 | 
			
		||||
            log(error.message);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncIndicator: function() {
 | 
			
		||||
@@ -234,7 +376,10 @@ const Indicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _connectToPermissionStore: function() {
 | 
			
		||||
        this._permStoreProxy = null;
 | 
			
		||||
        new PermissionStore.PermissionStore(Lang.bind(this, this._onPermStoreProxyReady), null);
 | 
			
		||||
        new PermissionStore(Gio.DBus.session,
 | 
			
		||||
                           'org.freedesktop.XdgApp',
 | 
			
		||||
                           '/org/freedesktop/XdgApp/PermissionStore',
 | 
			
		||||
                           Lang.bind(this, this._onPermStoreProxyReady));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPermStoreProxyReady: function(proxy, error) {
 | 
			
		||||
@@ -251,183 +396,61 @@ function clamp(value, min, max) {
 | 
			
		||||
    return Math.max(min, Math.min(max, value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AppAuthorizer = new Lang.Class({
 | 
			
		||||
    Name: 'LocationAppAuthorizer',
 | 
			
		||||
 | 
			
		||||
    _init: function(desktopId,
 | 
			
		||||
                    reqAccuracyLevel,
 | 
			
		||||
                    permStoreProxy,
 | 
			
		||||
                    maxAccuracyLevel) {
 | 
			
		||||
        this.desktopId = desktopId;
 | 
			
		||||
        this.reqAccuracyLevel = reqAccuracyLevel;
 | 
			
		||||
        this._permStoreProxy = permStoreProxy;
 | 
			
		||||
        this._maxAccuracyLevel = maxAccuracyLevel;
 | 
			
		||||
        this._permissions = {};
 | 
			
		||||
 | 
			
		||||
        this._accuracyLevel = GeoclueAccuracyLevel.NONE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    authorize: function(onAuthDone) {
 | 
			
		||||
        this._onAuthDone = onAuthDone;
 | 
			
		||||
 | 
			
		||||
        let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this._app = appSystem.lookup_app(this.desktopId + ".desktop");
 | 
			
		||||
        if (this._app == null || this._permStoreProxy == null) {
 | 
			
		||||
            this._completeAuth();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._permStoreProxy.LookupRemote(APP_PERMISSIONS_TABLE,
 | 
			
		||||
                                          APP_PERMISSIONS_ID,
 | 
			
		||||
                                          Lang.bind(this,
 | 
			
		||||
                                                    this._onPermLookupDone));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPermLookupDone: function(result, error) {
 | 
			
		||||
        if (error != null) {
 | 
			
		||||
            if (error.domain == Gio.DBusError) {
 | 
			
		||||
                // Likely no xdg-app installed, just authorize the app
 | 
			
		||||
                this._accuracyLevel = this.reqAccuracyLevel;
 | 
			
		||||
                this._permStoreProxy = null;
 | 
			
		||||
                this._completeAuth();
 | 
			
		||||
            } else {
 | 
			
		||||
                // Currently xdg-app throws an error if we lookup for
 | 
			
		||||
                // unknown ID (which would be the case first time this code
 | 
			
		||||
                // runs) so we continue with user authorization as normal
 | 
			
		||||
                // and ID is added to the store if user says "yes".
 | 
			
		||||
                log(error.message);
 | 
			
		||||
                this._permissions = {};
 | 
			
		||||
                this._userAuthorizeApp();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [this._permissions] = result;
 | 
			
		||||
        let permission = this._permissions[this.desktopId];
 | 
			
		||||
 | 
			
		||||
        if (permission == null) {
 | 
			
		||||
            this._userAuthorizeApp();
 | 
			
		||||
        } else {
 | 
			
		||||
            let [levelStr] = permission || ['NONE'];
 | 
			
		||||
            this._accuracyLevel = GeoclueAccuracyLevel[levelStr] ||
 | 
			
		||||
                                  GeoclueAccuracyLevel.NONE;
 | 
			
		||||
            this._completeAuth();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _userAuthorizeApp: function() {
 | 
			
		||||
        let name = this._app.get_name();
 | 
			
		||||
        let appInfo = this._app.get_app_info();
 | 
			
		||||
        let reason = appInfo.get_string("X-Geoclue-Reason");
 | 
			
		||||
 | 
			
		||||
        this._showAppAuthDialog(name, reason);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showAppAuthDialog: function(name, reason) {
 | 
			
		||||
        this._dialog = new GeolocationDialog(name,
 | 
			
		||||
                                             reason,
 | 
			
		||||
                                             this.reqAccuracyLevel);
 | 
			
		||||
 | 
			
		||||
        let responseId = this._dialog.connect('response', Lang.bind(this,
 | 
			
		||||
            function(dialog, level) {
 | 
			
		||||
                this._dialog.disconnect(responseId);
 | 
			
		||||
                this._accuracyLevel = level;
 | 
			
		||||
                this._completeAuth();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._dialog.open();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _completeAuth: function() {
 | 
			
		||||
        if (this._accuracyLevel != GeoclueAccuracyLevel.NONE) {
 | 
			
		||||
            this._accuracyLevel = clamp(this._accuracyLevel,
 | 
			
		||||
                                        0,
 | 
			
		||||
                                        this._maxAccuracyLevel);
 | 
			
		||||
        }
 | 
			
		||||
        this._saveToPermissionStore();
 | 
			
		||||
 | 
			
		||||
        this._onAuthDone(this._accuracyLevel);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _saveToPermissionStore: function() {
 | 
			
		||||
        if (this._permStoreProxy == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let levelStr = accuracyLevelToString(this._accuracyLevel);
 | 
			
		||||
        let dateStr = Math.round(Date.now() / 1000).toString();
 | 
			
		||||
        this._permissions[this.desktopId] = [levelStr, dateStr];
 | 
			
		||||
 | 
			
		||||
        let data = GLib.Variant.new('av', {});
 | 
			
		||||
 | 
			
		||||
        this._permStoreProxy.SetRemote(APP_PERMISSIONS_TABLE,
 | 
			
		||||
                                       true,
 | 
			
		||||
                                       APP_PERMISSIONS_ID,
 | 
			
		||||
                                       this._permissions,
 | 
			
		||||
                                       data,
 | 
			
		||||
                                       function (result, error) {
 | 
			
		||||
            if (error != null)
 | 
			
		||||
                log(error.message);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GeolocationDialog = new Lang.Class({
 | 
			
		||||
    Name: 'GeolocationDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
 | 
			
		||||
    _init: function(name, reason, reqAccuracyLevel) {
 | 
			
		||||
        this.parent({ styleClass: 'geolocation-dialog' });
 | 
			
		||||
        this.reqAccuracyLevel = reqAccuracyLevel;
 | 
			
		||||
    // FIXME: Would be nice to show the application icon too
 | 
			
		||||
    _init: function(name, reason, icon) {
 | 
			
		||||
        this.parent({ destroyOnClose: false });
 | 
			
		||||
 | 
			
		||||
        let mainContentBox = new St.BoxLayout({ style_class: 'geolocation-dialog-main-layout' });
 | 
			
		||||
        this.contentLayout.add_actor(mainContentBox);
 | 
			
		||||
        let text = _("'%s' is requesting access to location data.").format (name);
 | 
			
		||||
        this._label = new St.Label({ style_class: 'prompt-dialog-description',
 | 
			
		||||
                                     text: text });
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ style_class: 'geolocation-dialog-icon',
 | 
			
		||||
                                 icon_name: 'find-location-symbolic',
 | 
			
		||||
                                 y_align: Clutter.ActorAlign.START });
 | 
			
		||||
        mainContentBox.add_actor(icon);
 | 
			
		||||
        this.contentLayout.add(this._label, {});
 | 
			
		||||
 | 
			
		||||
        let messageBox = new St.BoxLayout({ style_class: 'geolocation-dialog-content',
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        mainContentBox.add_actor(messageBox);
 | 
			
		||||
        if (reason != null) {
 | 
			
		||||
            this._reasonLabel = new St.Label({ style_class: 'prompt-dialog-description',
 | 
			
		||||
                                               text: reason });
 | 
			
		||||
            this.contentLayout.add(this._reasonLabel, {});
 | 
			
		||||
        } else {
 | 
			
		||||
            this._reasonLabel = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._title = new St.Label({ style_class: 'geolocation-dialog-title headline' });
 | 
			
		||||
        messageBox.add_actor(this._title);
 | 
			
		||||
 | 
			
		||||
        this._reason = new St.Label({ style_class: 'geolocation-dialog-reason' });
 | 
			
		||||
        messageBox.add_actor(this._reason);
 | 
			
		||||
 | 
			
		||||
        this._privacyNote = new St.Label();
 | 
			
		||||
        messageBox.add_actor(this._privacyNote);
 | 
			
		||||
 | 
			
		||||
        let button = this.addButton({ label: _("Deny Access"),
 | 
			
		||||
                                      action: Lang.bind(this, this._onDenyClicked),
 | 
			
		||||
                                      key: Clutter.KEY_Escape });
 | 
			
		||||
        this.addButton({ label: _("Grant Access"),
 | 
			
		||||
                         action: Lang.bind(this, this._onGrantClicked) });
 | 
			
		||||
 | 
			
		||||
        this.setInitialKeyFocus(button);
 | 
			
		||||
 | 
			
		||||
        /* Translators: %s is an application name */
 | 
			
		||||
        this._title.text = _("Give %s access to your location?").format(name);
 | 
			
		||||
 | 
			
		||||
        this._privacyNote.text = _("Location access can be changed at any time from the privacy settings.");
 | 
			
		||||
 | 
			
		||||
        if (reason)
 | 
			
		||||
            this._reason.text = reason;
 | 
			
		||||
        this._reason.visible = (reason != null);
 | 
			
		||||
        this._allowButton = this.addButton({ label: _("Confirm"),
 | 
			
		||||
                                             action: this._onAllowClicked.bind(this),
 | 
			
		||||
                                             default: false },
 | 
			
		||||
                                           { expand: true, x_fill: false, x_align: St.Align.END });
 | 
			
		||||
        this._denyButton = this.addButton({ label: _("Cancel"),
 | 
			
		||||
                                            action: this._onDisallowClicked.bind(this),
 | 
			
		||||
                                            default: true },
 | 
			
		||||
                                          { expand: true, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGrantClicked: function() {
 | 
			
		||||
        this.emit('response', this.reqAccuracyLevel);
 | 
			
		||||
    update: function(name, reason, icon) {
 | 
			
		||||
        let text = _("'%s' is requesting access to location data.").format (name);
 | 
			
		||||
        this._label.text = text;
 | 
			
		||||
 | 
			
		||||
        if (this._reasonLabel != null) {
 | 
			
		||||
            this.contentLayout.remove(this._reasonLabel, {});
 | 
			
		||||
            this._reasonLabel = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (reason != null) {
 | 
			
		||||
            this._reasonLabel = new St.Label({ style_class: 'prompt-dialog-description',
 | 
			
		||||
                                               text: reason });
 | 
			
		||||
            this.contentLayout.add(this._reasonLabel, {});
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAllowClicked: function() {
 | 
			
		||||
        this.allowed = true;
 | 
			
		||||
        this.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDenyClicked: function() {
 | 
			
		||||
        this.emit('response', GeoclueAccuracyLevel.NONE);
 | 
			
		||||
    _onDisallowClicked: function() {
 | 
			
		||||
        this.allowed = false;
 | 
			
		||||
        this.close();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(GeolocationDialog.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ 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;
 | 
			
		||||
@@ -40,8 +39,6 @@ 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'];
 | 
			
		||||
@@ -299,22 +296,11 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let item = this._connectionItems.get(connection.get_uuid());
 | 
			
		||||
        if (item)
 | 
			
		||||
            this._updateForConnection(item, connection);
 | 
			
		||||
            item.updateForConnection(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)
 | 
			
		||||
@@ -635,7 +621,7 @@ const NMDeviceBluetooth = new Lang.Class({
 | 
			
		||||
    _init: function(client, device, settings) {
 | 
			
		||||
        this.parent(client, device, settings);
 | 
			
		||||
 | 
			
		||||
        this.item.menu.addMenuItem(createSettingsAction(_("Bluetooth Settings"), device));
 | 
			
		||||
        this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDescription: function() {
 | 
			
		||||
@@ -643,7 +629,7 @@ const NMDeviceBluetooth = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getConnectLabel: function() {
 | 
			
		||||
        return _("Connect to Internet");
 | 
			
		||||
        return _("Use as Internet connection");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
@@ -766,17 +752,10 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
        this._updateSensitivity();
 | 
			
		||||
        this._syncView();
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
 | 
			
		||||
        let id = Main.sessionMode.connect('updated', () => {
 | 
			
		||||
            if (Main.sessionMode.allowSettings)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Main.sessionMode.disconnect(id);
 | 
			
		||||
            this.close();
 | 
			
		||||
        });
 | 
			
		||||
        if (accessPoints.length == 0) {
 | 
			
		||||
            /* If there are no visible access points, request a scan */
 | 
			
		||||
            this._device.request_scan_simple(null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -801,19 +780,9 @@ 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);
 | 
			
		||||
@@ -1362,11 +1331,7 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
        if (!this._device.active_connection)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let connectionPath = this._device.active_connection.connection;
 | 
			
		||||
        if (!connectionPath)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let connection = this._settings.get_connection_by_path(connectionPath);
 | 
			
		||||
        let connection = this._settings.get_connection_by_path(this._device.active_connection.connection);
 | 
			
		||||
        if (!connection)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -1443,7 +1408,7 @@ const NMVPNConnectionItem = new Lang.Class({
 | 
			
		||||
        case NetworkManager.VPNConnectionState.PREPARE:
 | 
			
		||||
        case NetworkManager.VPNConnectionState.CONNECT:
 | 
			
		||||
        case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
 | 
			
		||||
            return _("connecting…");
 | 
			
		||||
            return _("connecting...");
 | 
			
		||||
        case NetworkManager.VPNConnectionState.NEED_AUTH:
 | 
			
		||||
            /* Translators: this is for network connections that require some kind of key or password */
 | 
			
		||||
            return _("authentication required");
 | 
			
		||||
@@ -1526,7 +1491,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, false);
 | 
			
		||||
            app.launch(0, -1);
 | 
			
		||||
        } else {
 | 
			
		||||
            let connection = this._connections[0];
 | 
			
		||||
            Util.spawnApp(['gnome-control-center', 'network', 'show-device',
 | 
			
		||||
@@ -1566,10 +1531,8 @@ const NMVPNSection = new Lang.Class({
 | 
			
		||||
            item.setActiveConnection(null);
 | 
			
		||||
        }
 | 
			
		||||
        vpnConnections.forEach(Lang.bind(this, function(a) {
 | 
			
		||||
            if (a._connection) {
 | 
			
		||||
                let item = this._connectionItems.get(a._connection.get_uuid());
 | 
			
		||||
                item.setActiveConnection(a);
 | 
			
		||||
            }
 | 
			
		||||
            let item = this._connectionItems.get(a._connection.get_uuid());
 | 
			
		||||
            item.setActiveConnection(a);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1589,73 +1552,6 @@ 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,
 | 
			
		||||
@@ -1699,6 +1595,15 @@ 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;
 | 
			
		||||
@@ -1709,20 +1614,15 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._mainConnection = null;
 | 
			
		||||
        this._mainConnectionIconChangedId = 0;
 | 
			
		||||
        this._mainConnectionStateChangedId = 0;
 | 
			
		||||
 | 
			
		||||
        this._notification = null;
 | 
			
		||||
 | 
			
		||||
        this._nmDevices = [];
 | 
			
		||||
        this._devices = { };
 | 
			
		||||
 | 
			
		||||
        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._devices.wired = this._createDeviceCategory();
 | 
			
		||||
        this._devices.wireless = this._createDeviceCategory();
 | 
			
		||||
        this._devices.wwan = this._createDeviceCategory();
 | 
			
		||||
 | 
			
		||||
        this._vpnSection = new NMVPNSection(this._client);
 | 
			
		||||
        this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
 | 
			
		||||
@@ -1732,8 +1632,6 @@ 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));
 | 
			
		||||
@@ -2079,7 +1977,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        // (but in general we should only prompt a portal if we know there is a portal)
 | 
			
		||||
        if (GLib.getenv('GNOME_SHELL_CONNECTIVITY_TEST') != null)
 | 
			
		||||
            isPortal = isPortal || this._client.connectivity < NetworkManager.ConnectivityState.FULL;
 | 
			
		||||
        if (!isPortal || Main.sessionMode.isGreeter)
 | 
			
		||||
        if (!isPortal)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let path = this._mainConnection.get_path();
 | 
			
		||||
@@ -2110,24 +2008,13 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateIcon: function() {
 | 
			
		||||
        if (!this._client.networking_enabled) {
 | 
			
		||||
        if (!this._client.networking_enabled || !this._mainConnection) {
 | 
			
		||||
            this._primaryIndicator.visible = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            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) {
 | 
			
		||||
            let dev = this._mainConnection._primaryDevice;
 | 
			
		||||
            this._primaryIndicator.visible = (dev != null);
 | 
			
		||||
            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();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,74 +0,0 @@
 | 
			
		||||
// -*- 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,7 +6,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -42,26 +41,14 @@ const AltSwitcher = new Lang.Class({
 | 
			
		||||
    _init: function(standard, alternate) {
 | 
			
		||||
        this._standard = standard;
 | 
			
		||||
        this._standard.connect('notify::visible', Lang.bind(this, this._sync));
 | 
			
		||||
        if (this._standard instanceof St.Button)
 | 
			
		||||
            this._standard.connect('clicked',
 | 
			
		||||
                                   () => { this._clickAction.release(); });
 | 
			
		||||
 | 
			
		||||
        this._alternate = alternate;
 | 
			
		||||
        this._alternate.connect('notify::visible', Lang.bind(this, this._sync));
 | 
			
		||||
        if (this._alternate instanceof St.Button)
 | 
			
		||||
            this._alternate.connect('clicked',
 | 
			
		||||
                                    () => { this._clickAction.release(); });
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
@@ -70,25 +57,14 @@ 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;
 | 
			
		||||
            if (this._flipped)
 | 
			
		||||
                childToShow = altPressed ? this._standard : this._alternate;
 | 
			
		||||
            else
 | 
			
		||||
                childToShow = altPressed ? this._alternate : this._standard;
 | 
			
		||||
            childToShow = altPressed ? this._alternate : this._standard;
 | 
			
		||||
        } else if (this._standard.visible) {
 | 
			
		||||
            childToShow = this._standard;
 | 
			
		||||
        } else if (this._alternate.visible) {
 | 
			
		||||
            childToShow = this._alternate;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        if (this.actor.get_child() != childToShow) {
 | 
			
		||||
            let hasFocus = this.actor.contains(global.stage.get_key_focus());
 | 
			
		||||
            this.actor.set_child(childToShow);
 | 
			
		||||
            if (hasFocus)
 | 
			
		||||
@@ -119,16 +95,6 @@ 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({
 | 
			
		||||
@@ -144,7 +110,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -190,8 +155,6 @@ 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),
 | 
			
		||||
@@ -301,8 +264,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _updateOrientationLock: function() {
 | 
			
		||||
        if (this._sensorProxy)
 | 
			
		||||
            this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer &&
 | 
			
		||||
                                                  this._monitorManager.get_is_builtin_display_on();
 | 
			
		||||
            this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer;
 | 
			
		||||
        else
 | 
			
		||||
            this._orientationLockAction.visible = false;
 | 
			
		||||
 | 
			
		||||
@@ -339,17 +301,14 @@ const Indicator = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveSuspend: function() {
 | 
			
		||||
        this._loginManager.canSuspend(Lang.bind(this,
 | 
			
		||||
            function(canSuspend, needsAuth) {
 | 
			
		||||
                this._haveSuspend = canSuspend;
 | 
			
		||||
                this._suspendNeedsAuth = needsAuth;
 | 
			
		||||
                this._updateSuspend();
 | 
			
		||||
            }));
 | 
			
		||||
        this._loginManager.canSuspend(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveSuspend = result;
 | 
			
		||||
            this._updateSuspend();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSuspend: function() {
 | 
			
		||||
        let disabled = (Main.sessionMode.isLocked &&
 | 
			
		||||
                        this._suspendNeedsAuth) ||
 | 
			
		||||
        let disabled = Main.sessionMode.isLocked ||
 | 
			
		||||
                       (Main.sessionMode.isGreeter &&
 | 
			
		||||
                        this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
 | 
			
		||||
        this._suspendAction.visible = this._haveSuspend && !disabled;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ const Signals = imports.signals;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
@@ -24,7 +23,6 @@ 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,
 | 
			
		||||
@@ -53,32 +51,9 @@ function getTermsForSearchString(searchString) {
 | 
			
		||||
    return terms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TouchpadShowOverviewAction = new Lang.Class({
 | 
			
		||||
    Name: 'TouchpadShowOverviewAction',
 | 
			
		||||
 | 
			
		||||
    _init: function(actor) {
 | 
			
		||||
        actor.connect('captured-event', Lang.bind(this, this._handleEvent));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _handleEvent: function(actor, event) {
 | 
			
		||||
        if (event.type() != Clutter.EventType.TOUCHPAD_PINCH)
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (event.get_touchpad_gesture_finger_count() != 3)
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END)
 | 
			
		||||
            this.emit('activated', event.get_gesture_pinch_scale ());
 | 
			
		||||
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(TouchpadShowOverviewAction.prototype);
 | 
			
		||||
 | 
			
		||||
const ShowOverviewAction = new Lang.Class({
 | 
			
		||||
    Name: 'ShowOverviewAction',
 | 
			
		||||
    Extends: Clutter.GestureAction,
 | 
			
		||||
    Signals: { 'activated': { param_types: [GObject.TYPE_DOUBLE] } },
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
@@ -137,6 +112,7 @@ const ShowOverviewAction = new Lang.Class({
 | 
			
		||||
        this.emit('activated', areaDiff);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ShowOverviewAction.prototype);
 | 
			
		||||
 | 
			
		||||
const ViewSelector = new Lang.Class({
 | 
			
		||||
    Name: 'ViewSelector',
 | 
			
		||||
@@ -180,7 +156,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.appDisplay = new AppDisplay.AppDisplay();
 | 
			
		||||
        this._appsPage = this._addPage(this.appDisplay.actor,
 | 
			
		||||
                                       _("Applications"), 'view-app-grid-symbolic');
 | 
			
		||||
                                       _("Applications"), 'view-grid-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._searchResults = new Search.SearchResults();
 | 
			
		||||
        this._searchPage = this._addPage(this._searchResults.actor,
 | 
			
		||||
@@ -254,16 +230,11 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        global.stage.add_action(gesture);
 | 
			
		||||
 | 
			
		||||
        gesture = new ShowOverviewAction();
 | 
			
		||||
        gesture.connect('activated', Lang.bind(this, this._pinchGestureActivated));
 | 
			
		||||
        gesture.connect('activated', Lang.bind(this, function(action, areaDiff) {
 | 
			
		||||
            if (areaDiff < 0.7)
 | 
			
		||||
                Main.overview.show();
 | 
			
		||||
        }));
 | 
			
		||||
        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,8 +17,6 @@ const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const WindowMenu = imports.ui.windowMenu;
 | 
			
		||||
const PadOsd = imports.ui.padOsd;
 | 
			
		||||
const EdgeDragAction = imports.ui.edgeDragAction;
 | 
			
		||||
 | 
			
		||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
 | 
			
		||||
const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2;
 | 
			
		||||
@@ -34,25 +32,6 @@ 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,
 | 
			
		||||
@@ -531,7 +510,7 @@ const TouchpadWorkspaceSwitchAction = new Lang.Class({
 | 
			
		||||
        if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE)
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (event.get_touchpad_gesture_finger_count() != 4)
 | 
			
		||||
        if (event.get_gesture_swipe_finger_count() != 4)
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) {
 | 
			
		||||
@@ -555,7 +534,6 @@ 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;
 | 
			
		||||
@@ -593,11 +571,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();
 | 
			
		||||
@@ -659,6 +637,7 @@ const AppSwitchAction = new Lang.Class({
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AppSwitchAction.prototype);
 | 
			
		||||
 | 
			
		||||
const ResizePopup = new Lang.Class({
 | 
			
		||||
    Name: 'ResizePopup',
 | 
			
		||||
@@ -676,7 +655,7 @@ const ResizePopup = new Lang.Class({
 | 
			
		||||
    set: function(rect, displayW, displayH) {
 | 
			
		||||
        /* Translators: This represents the size of a window. The first number is
 | 
			
		||||
         * the width of the window and the second is the height. */
 | 
			
		||||
        let text = _("%d × %d").format(displayW, displayH);
 | 
			
		||||
        let text = _("%d x %d").format(displayW, displayH);
 | 
			
		||||
        this._label.set_text(text);
 | 
			
		||||
 | 
			
		||||
        this._widget.set_position(rect.x, rect.y);
 | 
			
		||||
@@ -704,8 +683,6 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._dimmedWindows = [];
 | 
			
		||||
 | 
			
		||||
        this._skippedActors = [];
 | 
			
		||||
 | 
			
		||||
        this._allowedKeybindings = {};
 | 
			
		||||
 | 
			
		||||
        this._isWorkspacePrepended = false;
 | 
			
		||||
@@ -726,7 +703,6 @@ 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));
 | 
			
		||||
@@ -874,34 +850,22 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-applications',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startSwitcher));
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-group',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startSwitcher));
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-applications-backward',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startSwitcher));
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-group-backward',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startSwitcher));
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-windows',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startSwitcher));
 | 
			
		||||
                                        Lang.bind(this, this._startWindowSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-windows-backward',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL,
 | 
			
		||||
                                        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));
 | 
			
		||||
                                        Lang.bind(this, this._startWindowSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-panels',
 | 
			
		||||
                                        Shell.ActionMode.NORMAL |
 | 
			
		||||
                                        Shell.ActionMode.OVERVIEW |
 | 
			
		||||
@@ -939,35 +903,6 @@ 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++)
 | 
			
		||||
@@ -998,18 +933,6 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        gesture.connect('activated', Lang.bind(this, this._switchApp));
 | 
			
		||||
        global.stage.add_action(gesture);
 | 
			
		||||
 | 
			
		||||
        gesture = new EdgeDragAction.EdgeDragAction(St.Side.BOTTOM, Shell.ActionMode.ALL);
 | 
			
		||||
        gesture.connect('activated', Lang.bind(this, function() {
 | 
			
		||||
            Main.keyboard.show(Main.layoutManager.bottomIndex);
 | 
			
		||||
        }));
 | 
			
		||||
        global.stage.add_action(gesture);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showPadOsd: function (display, device, settings, imagePath, editionMode, monitorIndex) {
 | 
			
		||||
        this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex);
 | 
			
		||||
        this._currentPadOsd.connect('closed', Lang.bind(this, function() { this._currentPadOsd = null }));
 | 
			
		||||
 | 
			
		||||
        return this._currentPadOsd.actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _actionSwitchWorkspace: function(action, direction) {
 | 
			
		||||
@@ -1100,10 +1023,6 @@ 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);
 | 
			
		||||
@@ -1130,15 +1049,9 @@ 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;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1313,17 +1226,37 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((whichChange == Meta.SizeChange.FULLSCREEN ||
 | 
			
		||||
             whichChange == Meta.SizeChange.UNFULLSCREEN) &&
 | 
			
		||||
            oldFrameRect.width > 0 && oldFrameRect.height > 0)
 | 
			
		||||
            this._fullscreenAnimation(shellwm, actor, oldFrameRect, whichChange);
 | 
			
		||||
        if (whichChange == Meta.SizeChange.FULLSCREEN)
 | 
			
		||||
            this._fullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect);
 | 
			
		||||
        else if (whichChange == Meta.SizeChange.UNFULLSCREEN)
 | 
			
		||||
            this._unfullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect);
 | 
			
		||||
        else
 | 
			
		||||
            shellwm.completed_size_change(actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fullscreenAnimation: function(shellwm, actor, oldFrameRect, change) {
 | 
			
		||||
    _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);
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
@@ -1331,28 +1264,10 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        actorClone.set_size(oldFrameRect.width, oldFrameRect.height);
 | 
			
		||||
        Main.uiGroup.add_actor(actorClone);
 | 
			
		||||
 | 
			
		||||
        let rect = change == Meta.SizeChange.FULLSCREEN ? oldFrameRect : null;
 | 
			
		||||
        actor.__fullscreenClone = actorClone;
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
        let scaleX = targetRect.width / oldFrameRect.width;
 | 
			
		||||
        let scaleY = targetRect.height / oldFrameRect.height;
 | 
			
		||||
 | 
			
		||||
        // Now scale and fade out the clone
 | 
			
		||||
        Tweener.addTween(actorClone,
 | 
			
		||||
@@ -1365,17 +1280,9 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -1401,15 +1308,6 @@ 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);
 | 
			
		||||
@@ -1417,25 +1315,31 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
            actor.scale_y = 1.0;
 | 
			
		||||
            actor.translation_x = 0;
 | 
			
		||||
            actor.translation_y = 0;
 | 
			
		||||
            this._clearFullscreenInfo(actor);
 | 
			
		||||
 | 
			
		||||
            let actorClone = actor.__fullscreenClone;
 | 
			
		||||
            if (actorClone) {
 | 
			
		||||
                actorClone.destroy();
 | 
			
		||||
                delete actor.__fullscreenClone;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sizeChangeWindowOverwritten: function(shellwm, actor) {
 | 
			
		||||
        if (this._removeEffect(this._resizing, actor))
 | 
			
		||||
            this._clearFullscreenInfo(actor);
 | 
			
		||||
        if (this._removeEffect(this._resizing, actor)) {
 | 
			
		||||
            let actorClone = actor.__fullscreenClone;
 | 
			
		||||
            if (actorClone) {
 | 
			
		||||
                actorClone.destroy();
 | 
			
		||||
                delete actor.__fullscreenClone;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hasAttachedDialogs: function(window, ignoreWindow) {
 | 
			
		||||
        var count = 0;
 | 
			
		||||
        window.foreach_transient(function(win) {
 | 
			
		||||
            if (win != ignoreWindow &&
 | 
			
		||||
                win.is_attached_dialog() &&
 | 
			
		||||
                win.get_transient_for() == window) {
 | 
			
		||||
            if (win != ignoreWindow && win.is_attached_dialog())
 | 
			
		||||
                count++;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
        return count != 0;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1463,14 +1367,11 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        let dimmer = getWindowDimmer(actor);
 | 
			
		||||
        if (!dimmer)
 | 
			
		||||
            return;
 | 
			
		||||
        if (this._shouldAnimate())
 | 
			
		||||
            Tweener.addTween(dimmer,
 | 
			
		||||
                             { dimFactor: 1.0,
 | 
			
		||||
                               time: DIM_TIME,
 | 
			
		||||
                               transition: 'linear'
 | 
			
		||||
                             });
 | 
			
		||||
        else
 | 
			
		||||
            dimmer.dimFactor = 1.0;
 | 
			
		||||
        Tweener.addTween(dimmer,
 | 
			
		||||
                         { dimFactor: 1.0,
 | 
			
		||||
                           time: DIM_TIME,
 | 
			
		||||
                           transition: 'linear'
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _undimWindow: function(window) {
 | 
			
		||||
@@ -1480,13 +1381,10 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        let dimmer = getWindowDimmer(actor);
 | 
			
		||||
        if (!dimmer)
 | 
			
		||||
            return;
 | 
			
		||||
        if (this._shouldAnimate())
 | 
			
		||||
            Tweener.addTween(dimmer,
 | 
			
		||||
                             { dimFactor: 0.0,
 | 
			
		||||
                               time: UNDIM_TIME,
 | 
			
		||||
                               transition: 'linear' });
 | 
			
		||||
        else
 | 
			
		||||
            dimmer.dimFactor = 0.0;
 | 
			
		||||
        Tweener.addTween(dimmer,
 | 
			
		||||
                         { dimFactor: 0.0,
 | 
			
		||||
                           time: UNDIM_TIME,
 | 
			
		||||
                           transition: 'linear' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _mapWindow : function(shellwm, actor) {
 | 
			
		||||
@@ -1504,14 +1402,6 @@ 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());
 | 
			
		||||
 | 
			
		||||
        let types = [Meta.WindowType.NORMAL,
 | 
			
		||||
                     Meta.WindowType.DIALOG,
 | 
			
		||||
@@ -1521,6 +1411,9 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actor.meta_window.is_attached_dialog())
 | 
			
		||||
            this._checkDimming(actor.get_meta_window().get_transient_for());
 | 
			
		||||
 | 
			
		||||
        switch (actor._windowType) {
 | 
			
		||||
        case Meta.WindowType.NORMAL:
 | 
			
		||||
            actor.set_pivot_point(0.5, 1.0);
 | 
			
		||||
@@ -1603,9 +1496,6 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
                                                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (window.is_attached_dialog())
 | 
			
		||||
            this._checkDimming(window.get_transient_for(), window);
 | 
			
		||||
 | 
			
		||||
        let types = [Meta.WindowType.NORMAL,
 | 
			
		||||
                     Meta.WindowType.DIALOG,
 | 
			
		||||
                     Meta.WindowType.MODAL_DIALOG];
 | 
			
		||||
@@ -1614,7 +1504,7 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (actor.meta_window.window_type) {
 | 
			
		||||
        switch (actor._windowType) {
 | 
			
		||||
        case Meta.WindowType.NORMAL:
 | 
			
		||||
            actor.set_pivot_point(0.5, 0.5);
 | 
			
		||||
            this._destroying.push(actor);
 | 
			
		||||
@@ -1640,6 +1530,7 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            if (window.is_attached_dialog()) {
 | 
			
		||||
                let parent = window.get_transient_for();
 | 
			
		||||
                this._checkDimming(parent, window);
 | 
			
		||||
                actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
 | 
			
		||||
                    Tweener.removeTweens(actor);
 | 
			
		||||
                    this._destroyWindowDone(shellwm, actor);
 | 
			
		||||
@@ -1845,37 +1736,23 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        this._windowMenuManager.showWindowMenuForWindow(window, menu, rect);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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;
 | 
			
		||||
 | 
			
		||||
    _startAppSwitcher : 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 constructor();
 | 
			
		||||
        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();
 | 
			
		||||
 | 
			
		||||
        if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
 | 
			
		||||
            tabPopup.destroy();
 | 
			
		||||
 
 | 
			
		||||
@@ -97,6 +97,8 @@ const WindowMenu = new Lang.Class({
 | 
			
		||||
            if (window.is_always_on_all_workspaces())
 | 
			
		||||
                item.setSensitive(false);
 | 
			
		||||
 | 
			
		||||
            let nWorkspaces = global.screen.n_workspaces;
 | 
			
		||||
 | 
			
		||||
            if (!isSticky) {
 | 
			
		||||
                let workspace = window.get_workspace();
 | 
			
		||||
                if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,7 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
        this.metaWindow._delegate = this;
 | 
			
		||||
        this._workspace = workspace;
 | 
			
		||||
 | 
			
		||||
        this._windowClone = new Clutter.Clone({ source: realWindow });
 | 
			
		||||
        this._windowClone = new Clutter.Clone({ source: realWindow.get_texture() });
 | 
			
		||||
        // 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
 | 
			
		||||
@@ -471,10 +471,6 @@ const WindowOverlay = new Lang.Class({
 | 
			
		||||
        this.title = title;
 | 
			
		||||
        this.closeButton = button;
 | 
			
		||||
 | 
			
		||||
        // Don't block drop targets
 | 
			
		||||
        Shell.util_set_hidden_from_pick(this.title, true);
 | 
			
		||||
        Shell.util_set_hidden_from_pick(this.border, true);
 | 
			
		||||
 | 
			
		||||
        parentActor.add_actor(this.title);
 | 
			
		||||
        parentActor.add_actor(this.border);
 | 
			
		||||
        parentActor.add_actor(this.closeButton);
 | 
			
		||||
@@ -1100,7 +1096,6 @@ const Workspace = new Lang.Class({
 | 
			
		||||
    _init : function(metaWorkspace, monitorIndex) {
 | 
			
		||||
        // When dragging a window, we use this slot for reserve space.
 | 
			
		||||
        this._reservedSlot = null;
 | 
			
		||||
        this._reservedSlotWindow = null;
 | 
			
		||||
        this.metaWorkspace = metaWorkspace;
 | 
			
		||||
 | 
			
		||||
        // The full geometry is the geometry we should try and position
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,8 @@ const WorkspaceSwitcherPopup = new Lang.Class({
 | 
			
		||||
    Name: 'WorkspaceSwitcherPopup',
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new St.Widget({ x: 0,
 | 
			
		||||
        this.actor = new St.Widget({ reactive: true,
 | 
			
		||||
                                     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(container, forHeight) {
 | 
			
		||||
    vfunc_get_preferred_width: function(forHeight) {
 | 
			
		||||
        return this.primaryActor.get_preferred_width(forHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(container, forWidth) {
 | 
			
		||||
    vfunc_get_preferred_height: function(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: global.screen.n_workspaces });
 | 
			
		||||
                                                    upper: 0 });
 | 
			
		||||
        this.scrollAdjustment.connect('notify::value',
 | 
			
		||||
                                      Lang.bind(this, this._onScroll));
 | 
			
		||||
 | 
			
		||||
@@ -374,10 +374,6 @@ 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();
 | 
			
		||||
@@ -425,10 +421,8 @@ 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._workspacesViews[index].getActiveWorkspace().isEmpty())
 | 
			
		||||
                this._getPrimaryView().getActiveWorkspace().isEmpty())
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.addAction(clickAction);
 | 
			
		||||
@@ -437,18 +431,11 @@ 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();
 | 
			
		||||
        }));
 | 
			
		||||
@@ -594,12 +581,6 @@ 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;
 | 
			
		||||
@@ -680,11 +661,6 @@ 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()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,9 @@ const XdndHandler = new Lang.Class({
 | 
			
		||||
        if (!Meta.is_wayland_compositor())
 | 
			
		||||
            global.init_xdnd();
 | 
			
		||||
 | 
			
		||||
        var dnd = Meta.get_backend().get_dnd();
 | 
			
		||||
        dnd.connect('dnd-enter', Lang.bind(this, this._onEnter));
 | 
			
		||||
        dnd.connect('dnd-position-change', Lang.bind(this, this._onPositionChanged));
 | 
			
		||||
        dnd.connect('dnd-leave', Lang.bind(this, this._onLeave));
 | 
			
		||||
        global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
 | 
			
		||||
        global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
 | 
			
		||||
        global.connect('xdnd-leave', Lang.bind(this, this._onLeave));
 | 
			
		||||
 | 
			
		||||
        this._windowGroupVisibilityHandlerId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,6 @@ gl
 | 
			
		||||
gu
 | 
			
		||||
he
 | 
			
		||||
hi
 | 
			
		||||
hr
 | 
			
		||||
hu
 | 
			
		||||
ia
 | 
			
		||||
id
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								po/Makevars
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								po/Makevars
									
									
									
									
									
								
							@@ -1,68 +0,0 @@
 | 
			
		||||
# 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
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user